From bbe594b835f8a3ecaf3367a94f3a144ed012cde1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 10 Dec 2008 20:06:28 -0800 Subject: [PATCH 001/358] sched: Enable might_sleep before initializing drivers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows detection of init bugs in built-in drivers. Signed-off-by: Arve Hjønnevåg --- kernel/sched/core.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index f5c6635b806c..4bf263e8040a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6942,13 +6942,24 @@ static inline int preempt_count_equals(int preempt_offset) return (nested == preempt_offset); } +static int __might_sleep_init_called; +int __init __might_sleep_init(void) +{ + __might_sleep_init_called = 1; + return 0; +} +early_initcall(__might_sleep_init); + void __might_sleep(const char *file, int line, int preempt_offset) { static unsigned long prev_jiffy; /* ratelimiting */ rcu_sleep_check(); /* WARN_ON_ONCE() by default, no rate limit reqd. */ if ((preempt_count_equals(preempt_offset) && !irqs_disabled()) || - system_state != SYSTEM_RUNNING || oops_in_progress) + oops_in_progress) + return; + if (system_state != SYSTEM_RUNNING && + (!__might_sleep_init_called || system_state != SYSTEM_BOOTING)) return; if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy) return; From b69a6ae58a201ca2e19f04c149655bf4874faffe Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 29 Apr 2008 16:44:10 -0400 Subject: [PATCH 002/358] Make /dev/mem configurable, as we don't want it. Signed-off-by: Brian Swetland --- drivers/char/Kconfig | 13 +++++++++++++ drivers/char/mem.c | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 1386749b48ff..c5095b20920a 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -6,6 +6,19 @@ menu "Character devices" source "drivers/tty/Kconfig" +config DEVMEM + bool "Memory device driver" + default y + help + The memory driver provides two character devices, mem and kmem, which + provide access to the system's memory. The mem device is a view of + physical memory, and each byte in the device corresponds to the + matching physical address. The kmem device is the same as mem, but + the addresses correspond to the kernel's virtual address space rather + than physical memory. These devices are standard parts of a Linux + system and most users should say Y here. You might say N if very + security conscience or memory is tight. + config DEVKMEM bool "/dev/kmem virtual device support" default y diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 92c5937f80c3..fe0f89726747 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -58,6 +58,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) } #endif +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) #ifdef CONFIG_STRICT_DEVMEM static inline int range_is_allowed(unsigned long pfn, unsigned long size) { @@ -83,7 +84,9 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size) return 1; } #endif +#endif +#ifdef CONFIG_DEVMEM void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr) { } @@ -210,6 +213,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf, *ppos += written; return written; } +#endif /* CONFIG_DEVMEM */ + +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) int __weak phys_mem_access_prot_allowed(struct file *file, unsigned long pfn, unsigned long size, pgprot_t *vma_prot) @@ -331,6 +337,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) } return 0; } +#endif /* CONFIG_DEVMEM */ #ifdef CONFIG_DEVKMEM static int mmap_kmem(struct file *file, struct vm_area_struct *vma) @@ -691,6 +698,8 @@ static loff_t null_lseek(struct file *file, loff_t offset, int orig) return file->f_pos = 0; } +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT) + /* * The memory devices use the full 32/64 bits of the offset, and so we cannot * check against negative addresses: they are ok. The return value is weird, @@ -724,10 +733,14 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig) return ret; } +#endif + +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT) static int open_port(struct inode *inode, struct file *filp) { return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; } +#endif #define zero_lseek null_lseek #define full_lseek null_lseek @@ -737,6 +750,7 @@ static int open_port(struct inode *inode, struct file *filp) #define open_mem open_port #define open_kmem open_mem +#ifdef CONFIG_DEVMEM static const struct file_operations mem_fops = { .llseek = memory_lseek, .read = read_mem, @@ -745,6 +759,7 @@ static const struct file_operations mem_fops = { .open = open_mem, .get_unmapped_area = get_unmapped_area_mem, }; +#endif #ifdef CONFIG_DEVKMEM static const struct file_operations kmem_fops = { @@ -806,7 +821,9 @@ static const struct memdev { const struct file_operations *fops; struct backing_dev_info *dev_info; } devlist[] = { +#ifdef CONFIG_DEVMEM [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi }, +#endif #ifdef CONFIG_DEVKMEM [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi }, #endif From 214bdfeeaaed384b5fc25c8dbfc1e8cd8c7b2c06 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 4 Dec 2008 12:19:57 -0800 Subject: [PATCH 003/358] mtd: nand: Allow NAND chip ids to be included standalone. Lets non-standard NAND drivers take advantage of known NAND chip information. Change-Id: I87e2fcb40b07b2ec91e102f1fa7f419a4b4af0a3 Signed-off-by: Dima Zavin --- drivers/mtd/nand/Kconfig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 90ff447bf043..997726d9a270 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,3 +1,10 @@ +config MTD_NAND_IDS + tristate "Include chip ids for known NAND devices." + depends on MTD + help + Useful for NAND drivers that do not use the NAND subsystem but + still like to take advantage of the known chip information. + config MTD_NAND_ECC tristate @@ -106,9 +113,6 @@ config MTD_NAND_OMAP_BCH legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine so they should not enable this config symbol. -config MTD_NAND_IDS - tristate - config MTD_NAND_RICOH tristate "Ricoh xD card reader" default n From ce4421b562f523b3308f5d9cbef697b66b8cdced Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 3 Sep 2010 12:41:21 -0700 Subject: [PATCH 004/358] mmc_block: Allow more than 8 partitions per card Set the GENHD_FL_EXT_DEVT flag, which will allocate minor numbers in major 259 for partitions past disk->minors. Also remove the use of disk_devt to determine devidx from md->disk. md->disk->first_minor is always initialized from devidx and can always be used to recover it. Signed-off-by: Colin Cross --- drivers/mmc/card/block.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 7b5424f398ac..5bec76dd35bd 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -166,11 +166,7 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) static inline int mmc_get_devidx(struct gendisk *disk) { - int devmaj = MAJOR(disk_devt(disk)); - int devidx = MINOR(disk_devt(disk)) / perdev_minors; - - if (!devmaj) - devidx = disk->first_minor / perdev_minors; + int devidx = disk->first_minor / perdev_minors; return devidx; } @@ -2083,6 +2079,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, md->disk->queue = md->queue.queue; md->disk->driverfs_dev = parent; set_disk_ro(md->disk, md->read_only || default_ro); + md->disk->flags = GENHD_FL_EXT_DEVT; if (area_type & MMC_BLK_DATA_AREA_RPMB) md->disk->flags |= GENHD_FL_NO_PART_SCAN; From 8ef1e88fe15e874ef20b386e925bde3b2fe8a017 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Thu, 13 Aug 2009 09:40:42 -0700 Subject: [PATCH 005/358] drivers: power: Add watchdog timer to catch drivers which lockup during suspend. Rather than hard-lock the kernel, dump the suspend thread stack and BUG() when a driver takes too long to suspend. The timeout is set to 12 seconds to be longer than the usbhid 10 second timeout. Exclude from the watchdog the time spent waiting for children that are resumed asynchronously and time every device, whether or not they resumed synchronously. Change-Id: Ifd211c06b104860c2fee6eecfe0d61774aa4508a Original-author: San Mehat Signed-off-by: Benoit Goby --- drivers/base/power/main.c | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 1b41fca3d65a..9fe35277c0f2 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -57,6 +57,12 @@ struct suspend_stats suspend_stats; static DEFINE_MUTEX(dpm_list_mtx); static pm_message_t pm_transition; +static void dpm_drv_timeout(unsigned long data); +struct dpm_drv_wd_data { + struct device *dev; + struct task_struct *tsk; +}; + static int async_error; static char *pm_verb(int event) @@ -738,6 +744,30 @@ static bool is_async(struct device *dev) && !pm_trace_is_enabled(); } +/** + * dpm_drv_timeout - Driver suspend / resume watchdog handler + * @data: struct device which timed out + * + * Called when a driver has timed out suspending or resuming. + * There's not much we can do here to recover so + * BUG() out for a crash-dump + * + */ +static void dpm_drv_timeout(unsigned long data) +{ + struct dpm_drv_wd_data *wd_data = (void *)data; + struct device *dev = wd_data->dev; + struct task_struct *tsk = wd_data->tsk; + + printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev), + (dev->driver ? dev->driver->name : "no driver")); + + printk(KERN_EMERG "dpm suspend stack:\n"); + show_stack(tsk, NULL); + + BUG(); +} + /** * dpm_resume - Execute "resume" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. @@ -1130,6 +1160,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) pm_callback_t callback = NULL; char *info = NULL; int error = 0; + struct timer_list timer; + struct dpm_drv_wd_data data; DECLARE_DPM_WATCHDOG_ON_STACK(wd); dpm_wait_for_children(dev, async); @@ -1153,6 +1185,14 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (dev->power.syscore) goto Complete; + + data.dev = dev; + data.tsk = get_current(); + init_timer_on_stack(&timer); + timer.expires = jiffies + HZ * 12; + timer.function = dpm_drv_timeout; + timer.data = (unsigned long)&data; + add_timer(&timer); dpm_watchdog_set(&wd, dev); device_lock(dev); @@ -1213,6 +1253,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) device_unlock(dev); dpm_watchdog_clear(&wd); + del_timer_sync(&timer); + destroy_timer_on_stack(&timer); + Complete: complete_all(&dev->power.completion); if (error) From bea3d29f537904b7b752f11cfaffda979314bc58 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 25 Aug 2009 16:52:22 -0700 Subject: [PATCH 006/358] [ARM] process: Add display of memory around registers when displaying regs. This is extremely useful in diagnosing remote crashes, and is based heavily on original work by . Signed-off-by: San Mehat Cc: Michael Davidson [ARM] process: Use uber-safe probe_kernel_address() to read mem when dumping. This prevents the dump from taking pagefaults / external aborts. Signed-off-by: San Mehat --- arch/arm/kernel/process.c | 73 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 92f7b15dd221..c0778bb02064 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -246,6 +246,77 @@ void machine_restart(char *cmd) while (1); } +/* + * dump a block of kernel memory from around the given address + */ +static void show_data(unsigned long addr, int nbytes, const char *name) +{ + int i, j; + int nlines; + u32 *p; + + /* + * don't attempt to dump non-kernel addresses or + * values that are probably just small negative numbers + */ + if (addr < PAGE_OFFSET || addr > -256UL) + return; + + printk("\n%s: %#lx:\n", name, addr); + + /* + * round address down to a 32 bit boundary + * and always dump a multiple of 32 bytes + */ + p = (u32 *)(addr & ~(sizeof(u32) - 1)); + nbytes += (addr & (sizeof(u32) - 1)); + nlines = (nbytes + 31) / 32; + + + for (i = 0; i < nlines; i++) { + /* + * just display low 16 bits of address to keep + * each line of the dump < 80 characters + */ + printk("%04lx ", (unsigned long)p & 0xffff); + for (j = 0; j < 8; j++) { + u32 data; + if (probe_kernel_address(p, data)) { + printk(" ********"); + } else { + printk(" %08x", data); + } + ++p; + } + printk("\n"); + } +} + +static void show_extra_register_data(struct pt_regs *regs, int nbytes) +{ + mm_segment_t fs; + + fs = get_fs(); + set_fs(KERNEL_DS); + show_data(regs->ARM_pc - nbytes, nbytes * 2, "PC"); + show_data(regs->ARM_lr - nbytes, nbytes * 2, "LR"); + show_data(regs->ARM_sp - nbytes, nbytes * 2, "SP"); + show_data(regs->ARM_ip - nbytes, nbytes * 2, "IP"); + show_data(regs->ARM_fp - nbytes, nbytes * 2, "FP"); + show_data(regs->ARM_r0 - nbytes, nbytes * 2, "R0"); + show_data(regs->ARM_r1 - nbytes, nbytes * 2, "R1"); + show_data(regs->ARM_r2 - nbytes, nbytes * 2, "R2"); + show_data(regs->ARM_r3 - nbytes, nbytes * 2, "R3"); + show_data(regs->ARM_r4 - nbytes, nbytes * 2, "R4"); + show_data(regs->ARM_r5 - nbytes, nbytes * 2, "R5"); + show_data(regs->ARM_r6 - nbytes, nbytes * 2, "R6"); + show_data(regs->ARM_r7 - nbytes, nbytes * 2, "R7"); + show_data(regs->ARM_r8 - nbytes, nbytes * 2, "R8"); + show_data(regs->ARM_r9 - nbytes, nbytes * 2, "R9"); + show_data(regs->ARM_r10 - nbytes, nbytes * 2, "R10"); + set_fs(fs); +} + void __show_regs(struct pt_regs *regs) { unsigned long flags; @@ -302,6 +373,8 @@ void __show_regs(struct pt_regs *regs) printk("Control: %08x%s\n", ctrl, buf); } #endif + + show_extra_register_data(regs, 128); } void show_regs(struct pt_regs * regs) From 732ed9e31c71ae9a9bef6ee0939be25b2acbf839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 13 Nov 2012 15:32:22 -0800 Subject: [PATCH 007/358] Revert "genirq: Do not consider disabled wakeup irqs" This reverts commit 9c6079aa1bfcf7e14de10b824779ce39b679bcb8. --- kernel/irq/pm.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index abcd6ca86cb7..dcd3f9796817 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -103,13 +103,8 @@ int check_wakeup_irqs(void) int irq; for_each_irq_desc(irq, desc) { - /* - * Only interrupts which are marked as wakeup source - * and have not been disabled before the suspend check - * can abort suspend. - */ if (irqd_is_wakeup_set(&desc->irq_data)) { - if (desc->depth == 1 && desc->istate & IRQS_PENDING) + if (desc->istate & IRQS_PENDING) return -EBUSY; continue; } From ea2eb466d0f4cd48a339754f9936b5cec23d3dc8 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 24 Jan 2011 14:36:39 -0800 Subject: [PATCH 008/358] PM: Print pending wakeup IRQ preventing suspend to dmesg Prints the name of the first action for a pending wakeup IRQ. Change-Id: I36f90735c75fb7c7ab1084775ec0d0ab02336e6e Signed-off-by: Todd Poynor --- kernel/irq/pm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index dcd3f9796817..c72b7a43beb9 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -104,8 +104,13 @@ int check_wakeup_irqs(void) for_each_irq_desc(irq, desc) { if (irqd_is_wakeup_set(&desc->irq_data)) { - if (desc->istate & IRQS_PENDING) + if (desc->istate & IRQS_PENDING) { + pr_info("Wakeup IRQ %d %s pending, suspend aborted\n", + irq, + desc->action && desc->action->name ? + desc->action->name : ""); return -EBUSY; + } continue; } /* From 760c492699f92384ec63c9f96987b0eb362d8dac Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 17 Aug 2011 18:31:58 -0500 Subject: [PATCH 009/358] panic: Add board ID to panic output At times, it is necessary for boards to provide some additional information as part of panic logs. Provide information on the board hardware as part of panic logs. It is safer to print this information at the very end in case something bad happens as part of the information retrieval itself. To use this, set global mach_panic_string to an appropriate string in the board file. Change-Id: Id12cdda87b0cd2940dd01d52db97e6162f671b4d Signed-off-by: Nishanth Menon --- include/linux/kernel.h | 3 +++ kernel/panic.c | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 196d1ea86df0..07b3eb466c36 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -841,4 +841,7 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } # define REBUILD_DUE_TO_FTRACE_MCOUNT_RECORD #endif +/* To identify board information in panic logs, set this */ +extern char *mach_panic_string; + #endif diff --git a/kernel/panic.c b/kernel/panic.c index 6d6300375090..7b8d5d257fb3 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -27,6 +27,9 @@ #define PANIC_TIMER_STEP 100 #define PANIC_BLINK_SPD 18 +/* Machine specific panic information string */ +char *mach_panic_string; + int panic_on_oops = CONFIG_PANIC_ON_OOPS_VALUE; static unsigned long tainted_mask; static int pause_on_oops; @@ -380,6 +383,11 @@ late_initcall(init_oops_id); void print_oops_end_marker(void) { init_oops_id(); + + if (mach_panic_string) + printk(KERN_WARNING "Board Information: %s\n", + mach_panic_string); + printk(KERN_WARNING "---[ end trace %016llx ]---\n", (unsigned long long)oops_id); } From 2099355cdcb79ff682c75e19c7574ce6988e18a8 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 19 Jul 2011 12:33:45 -0700 Subject: [PATCH 010/358] power: Add option to log time spent in suspend Prints the time spent in suspend in the kernel log, and keeps statistics on the time spent in suspend in /sys/kernel/debug/suspend_time Change-Id: Ia6b9ebe4baa0f7f5cd211c6a4f7e813aefd3fa1d Signed-off-by: Colin Cross Signed-off-by: Todd Poynor --- kernel/power/Kconfig | 7 +++ kernel/power/Makefile | 1 + kernel/power/suspend_time.c | 111 ++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 kernel/power/suspend_time.c diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 2fac9cc79b3d..4514b86671c4 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -309,3 +309,10 @@ config PM_GENERIC_DOMAINS_RUNTIME config CPU_PM bool depends on SUSPEND || CPU_IDLE + +config SUSPEND_TIME + bool "Log time spent in suspend" + ---help--- + Prints the time spent in suspend in the kernel log, and + keeps statistics on the time spent in suspend in + /sys/kernel/debug/suspend_time diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 29472bff11ef..8450b85d33c0 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -11,5 +11,6 @@ obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o +obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff --git a/kernel/power/suspend_time.c b/kernel/power/suspend_time.c new file mode 100644 index 000000000000..d2a65da9f22c --- /dev/null +++ b/kernel/power/suspend_time.c @@ -0,0 +1,111 @@ +/* + * debugfs file to track time spent in suspend + * + * Copyright (c) 2011, Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct timespec suspend_time_before; +static unsigned int time_in_suspend_bins[32]; + +#ifdef CONFIG_DEBUG_FS +static int suspend_time_debug_show(struct seq_file *s, void *data) +{ + int bin; + seq_printf(s, "time (secs) count\n"); + seq_printf(s, "------------------\n"); + for (bin = 0; bin < 32; bin++) { + if (time_in_suspend_bins[bin] == 0) + continue; + seq_printf(s, "%4d - %4d %4u\n", + bin ? 1 << (bin - 1) : 0, 1 << bin, + time_in_suspend_bins[bin]); + } + return 0; +} + +static int suspend_time_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, suspend_time_debug_show, NULL); +} + +static const struct file_operations suspend_time_debug_fops = { + .open = suspend_time_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init suspend_time_debug_init(void) +{ + struct dentry *d; + + d = debugfs_create_file("suspend_time", 0755, NULL, NULL, + &suspend_time_debug_fops); + if (!d) { + pr_err("Failed to create suspend_time debug file\n"); + return -ENOMEM; + } + + return 0; +} + +late_initcall(suspend_time_debug_init); +#endif + +static int suspend_time_syscore_suspend(void) +{ + read_persistent_clock(&suspend_time_before); + + return 0; +} + +static void suspend_time_syscore_resume(void) +{ + struct timespec after; + + read_persistent_clock(&after); + + after = timespec_sub(after, suspend_time_before); + + time_in_suspend_bins[fls(after.tv_sec)]++; + + pr_info("Suspended for %lu.%03lu seconds\n", after.tv_sec, + after.tv_nsec / NSEC_PER_MSEC); +} + +static struct syscore_ops suspend_time_syscore_ops = { + .suspend = suspend_time_syscore_suspend, + .resume = suspend_time_syscore_resume, +}; + +static int suspend_time_syscore_init(void) +{ + register_syscore_ops(&suspend_time_syscore_ops); + + return 0; +} + +static void suspend_time_syscore_exit(void) +{ + unregister_syscore_ops(&suspend_time_syscore_ops); +} +module_init(suspend_time_syscore_init); +module_exit(suspend_time_syscore_exit); From 5a8c28129e86859c73c5521acea49429487115c5 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 23 Aug 2011 15:56:50 -0700 Subject: [PATCH 011/358] ARM: add option to flush console before reboot If the console_lock was held while the system was rebooted, the messages in the temporary logbuffer would not have propogated to all the console drivers. This force releases the console lock if it failed to be acquired. Change-Id: I193dcf7b968be17966833e50b8b8bc70d5d9fe89 Signed-off-by: Dima Zavin --- arch/arm/Kconfig | 9 +++++++++ arch/arm/kernel/process.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 15949459611f..0f41da796690 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1912,6 +1912,15 @@ config XEN help Say Y if you want to run Linux in a Virtual Machine on Xen on ARM. +config ARM_FLUSH_CONSOLE_ON_RESTART + bool "Force flush the console on restart" + help + If the console is locked while the system is rebooted, the messages + in the temporary logbuffer would not have propogated to all the + console drivers. This option forces the console lock to be + released if it failed to be acquired, which will cause all the + pending messages to be flushed. + endmenu menu "Boot options" diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index c0778bb02064..74a85b225d99 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,31 @@ static const char *isa_modes[] = { extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); typedef void (*phys_reset_t)(unsigned long); +#ifdef CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART +void arm_machine_flush_console(void) +{ + printk("\n"); + pr_emerg("Restarting %s\n", linux_banner); + if (console_trylock()) { + console_unlock(); + return; + } + + mdelay(50); + + local_irq_disable(); + if (!console_trylock()) + pr_emerg("arm_restart: Console was locked! Busting\n"); + else + pr_emerg("arm_restart: Console was locked!\n"); + console_unlock(); +} +#else +void arm_machine_flush_console(void) +{ +} +#endif + /* * A temporary stack to use for CPU reset. This is static so that we * don't clobber it with the identity mapping. When running with this @@ -235,6 +261,10 @@ void machine_restart(char *cmd) local_irq_disable(); smp_send_stop(); + /* Flush the console to make sure all the relevant messages make it + * out to the console drivers */ + arm_machine_flush_console(); + arm_pm_restart(reboot_mode, cmd); /* Give a grace period for failure to restart of 1s */ From 30977e1244781c68f592f212cd4c3291fae6e5ab Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 19 Sep 2011 16:42:44 -0700 Subject: [PATCH 012/358] cpuidle: governor: menu: don't use loadavg get_loadavg doesn't work as intended. According to the comments, it should be returning an average over a few seconds, but it is actually reading the instantaneous load. It is almost always returning 0, but can sometimes, depending on workload, spike very high into the hundreds even when the average cpu load is under 10%. Disable it for now. Change-Id: I63ed100af1cf9463549939b8113ed83676db5f86 Signed-off-by: Colin Cross --- drivers/cpuidle/governors/menu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index cf7f2f0e4ef5..954a30e58f32 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -182,7 +182,12 @@ static inline int performance_multiplier(void) /* for higher loadavg, we are more reluctant */ - mult += 2 * get_loadavg(); + /* + * this doesn't work as intended - it is almost always 0, but can + * sometimes, depending on workload, spike very high into the hundreds + * even when the average cpu load is under 10%. + */ + /* mult += 2 * get_loadavg(); */ /* for IO wait tasks (per cpu!) we add 5x each */ mult += 10 * nr_iowait_cpu(smp_processor_id()); From 6438e925c140301a75b6250c31e9da424805d47e Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Sep 2011 15:59:50 -0700 Subject: [PATCH 013/358] ARM: cache-l2x0: update workaround for PL310 errata 727915 ARM errata 727915 for PL310 has been updated to include a new workaround required for PL310 r2p0 for l2x0_flush_all, which also affects l2x0_clean_all in my testing. For r2p0, clean or flush each set/way individually. For r3p0 or greater, use the debug register for cleaning and flushing. Requires exporting the cache_id, sets and ways detected in the init function for later use. Change-Id: I215055cbe5dc7e4e8184fb2befc4aff672ef0a12 Signed-off-by: Colin Cross --- arch/arm/include/asm/hardware/cache-l2x0.h | 3 + arch/arm/mm/cache-l2x0.c | 76 +++++++++++++++++----- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h index 6795ff743b3d..b1344186954c 100644 --- a/arch/arm/include/asm/hardware/cache-l2x0.h +++ b/arch/arm/include/asm/hardware/cache-l2x0.h @@ -66,6 +66,7 @@ #define L2X0_STNDBY_MODE_EN (1 << 0) /* Registers shifts and masks */ +#define L2X0_CACHE_ID_REV_MASK (0x3f) #define L2X0_CACHE_ID_PART_MASK (0xf << 6) #define L2X0_CACHE_ID_PART_L210 (1 << 6) #define L2X0_CACHE_ID_PART_L310 (3 << 6) @@ -106,6 +107,8 @@ #define L2X0_WAY_SIZE_SHIFT 3 +#define REV_PL310_R2P0 4 + #ifndef __ASSEMBLY__ extern void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask); #if defined(CONFIG_CACHE_L2X0) && defined(CONFIG_OF) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 7abde2ce8973..d96d5bd520e1 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -34,6 +34,9 @@ static void __iomem *l2x0_base; static DEFINE_RAW_SPINLOCK(l2x0_lock); static u32 l2x0_way_mask; /* Bitmask of active ways */ static u32 l2x0_size; +static u32 l2x0_cache_id; +static unsigned int l2x0_sets; +static unsigned int l2x0_ways; static unsigned long sync_reg_offset = L2X0_CACHE_SYNC; /* Aurora don't have the cache ID register available, so we have to @@ -50,6 +53,13 @@ struct l2x0_of_data { static bool of_init = false; +static inline bool is_pl310_rev(int rev) +{ + return (l2x0_cache_id & + (L2X0_CACHE_ID_PART_MASK | L2X0_CACHE_ID_REV_MASK)) == + (L2X0_CACHE_ID_PART_L310 | rev); +} + static inline void cache_wait_way(void __iomem *reg, unsigned long mask) { /* wait for cache operation by line or way to complete */ @@ -138,6 +148,23 @@ static void l2x0_cache_sync(void) raw_spin_unlock_irqrestore(&l2x0_lock, flags); } +#ifdef CONFIG_PL310_ERRATA_727915 +static void l2x0_for_each_set_way(void __iomem *reg) +{ + int set; + int way; + unsigned long flags; + + for (way = 0; way < l2x0_ways; way++) { + raw_spin_lock_irqsave(&l2x0_lock, flags); + for (set = 0; set < l2x0_sets; set++) + writel_relaxed((way << 28) | (set << 5), reg); + cache_sync(); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); + } +} +#endif + static void __l2x0_flush_all(void) { debug_writel(0x03); @@ -151,6 +178,13 @@ static void l2x0_flush_all(void) { unsigned long flags; +#ifdef CONFIG_PL310_ERRATA_727915 + if (is_pl310_rev(REV_PL310_R2P0)) { + l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_INV_LINE_IDX); + return; + } +#endif + /* clean all ways */ raw_spin_lock_irqsave(&l2x0_lock, flags); __l2x0_flush_all(); @@ -161,11 +195,20 @@ static void l2x0_clean_all(void) { unsigned long flags; +#ifdef CONFIG_PL310_ERRATA_727915 + if (is_pl310_rev(REV_PL310_R2P0)) { + l2x0_for_each_set_way(l2x0_base + L2X0_CLEAN_LINE_IDX); + return; + } +#endif + /* clean all ways */ raw_spin_lock_irqsave(&l2x0_lock, flags); + debug_writel(0x03); writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY); cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask); cache_sync(); + debug_writel(0x00); raw_spin_unlock_irqrestore(&l2x0_lock, flags); } @@ -324,65 +367,64 @@ static void l2x0_unlock(u32 cache_id) void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) { u32 aux; - u32 cache_id; u32 way_size = 0; - int ways; int way_size_shift = L2X0_WAY_SIZE_SHIFT; const char *type; l2x0_base = base; if (cache_id_part_number_from_dt) - cache_id = cache_id_part_number_from_dt; + l2x0_cache_id = cache_id_part_number_from_dt; else - cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); + l2x0_cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); aux &= aux_mask; aux |= aux_val; /* Determine the number of ways */ - switch (cache_id & L2X0_CACHE_ID_PART_MASK) { + switch (l2x0_cache_id & L2X0_CACHE_ID_PART_MASK) { case L2X0_CACHE_ID_PART_L310: if (aux & (1 << 16)) - ways = 16; + l2x0_ways = 16; else - ways = 8; + l2x0_ways = 8; type = "L310"; #ifdef CONFIG_PL310_ERRATA_753970 /* Unmapped register. */ sync_reg_offset = L2X0_DUMMY_REG; #endif - if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L2X0_CACHE_ID_RTL_R3P0) + if ((l2x0_cache_id & L2X0_CACHE_ID_RTL_MASK) <= L2X0_CACHE_ID_RTL_R3P0) outer_cache.set_debug = pl310_set_debug; break; case L2X0_CACHE_ID_PART_L210: - ways = (aux >> 13) & 0xf; + l2x0_ways = (aux >> 13) & 0xf; type = "L210"; break; case AURORA_CACHE_ID: sync_reg_offset = AURORA_SYNC_REG; - ways = (aux >> 13) & 0xf; - ways = 2 << ((ways + 1) >> 2); + l2x0_ways = (aux >> 13) & 0xf; + l2x0_ways = 2 << ((l2x0_ways + 1) >> 2); way_size_shift = AURORA_WAY_SIZE_SHIFT; type = "Aurora"; break; default: /* Assume unknown chips have 8 ways */ - ways = 8; + l2x0_ways = 8; type = "L2x0 series"; break; } - l2x0_way_mask = (1 << ways) - 1; + l2x0_way_mask = (1 << l2x0_ways) - 1; /* * L2 cache Size = Way size * Number of ways */ way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17; - way_size = 1 << (way_size + way_size_shift); + way_size = SZ_1K << (way_size + way_size_shift); - l2x0_size = ways * way_size * SZ_1K; + l2x0_size = l2x0_ways * way_size; + l2x0_sets = way_size / CACHE_LINE_SIZE; /* * Check if l2x0 controller is already enabled. @@ -391,7 +433,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) */ if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { /* Make sure that I&D is not locked down when starting */ - l2x0_unlock(cache_id); + l2x0_unlock(l2x0_cache_id); /* l2x0 controller is disabled */ writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); @@ -420,7 +462,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) pr_info("%s cache controller enabled\n", type); pr_info("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d kB\n", - ways, cache_id, aux, l2x0_size >> 10); + l2x0_ways, l2x0_cache_id, aux, l2x0_size >> 10); } #ifdef CONFIG_OF From a04ca7a94868e6fdee024374df664497979adddb Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 18 Oct 2011 16:59:54 -0700 Subject: [PATCH 014/358] ARM: smp: implement arch_trigger_all_cpus_backtrace using IPI Based on a rough patch by frank.rowand@am.sony.com Since ARM doesn't have an NMI (fiq's are not always available), send an IPI to all other CPUs (current cpu prints the stack directly) to capture a backtrace. Change-Id: I8b163c8cec05d521b433ae133795865e8a33d4e2 Signed-off-by: Dima Zavin --- arch/arm/include/asm/hardirq.h | 2 +- arch/arm/include/asm/irq.h | 3 ++ arch/arm/include/asm/smp.h | 2 ++ arch/arm/kernel/process.c | 12 +++++++ arch/arm/kernel/smp.c | 58 ++++++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h index fe3ea776dc34..5df33e30ae1b 100644 --- a/arch/arm/include/asm/hardirq.h +++ b/arch/arm/include/asm/hardirq.h @@ -5,7 +5,7 @@ #include #include -#define NR_IPI 8 +#define NR_IPI 9 typedef struct { unsigned int __softirq_pending; diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h index 53c15dec7af6..809203a4b71b 100644 --- a/arch/arm/include/asm/irq.h +++ b/arch/arm/include/asm/irq.h @@ -35,6 +35,9 @@ extern void (*handle_arch_irq)(struct pt_regs *); extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); #endif +void arch_trigger_all_cpu_backtrace(void); +#define arch_trigger_all_cpu_backtrace arch_trigger_all_cpu_backtrace + #endif #endif diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index 22a3b9b5d4a1..c3278502ec03 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -86,6 +86,8 @@ extern void arch_send_wakeup_ipi_mask(const struct cpumask *mask); extern int register_ipi_completion(struct completion *completion, int cpu); +extern void smp_send_all_cpu_backtrace(void); + struct smp_operations { #ifdef CONFIG_SMP /* diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 74a85b225d99..e49bf644d703 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -60,6 +60,18 @@ static const char *isa_modes[] = { "ARM" , "Thumb" , "Jazelle", "ThumbEE" }; +#ifdef CONFIG_SMP +void arch_trigger_all_cpu_backtrace(void) +{ + smp_send_all_cpu_backtrace(); +} +#else +void arch_trigger_all_cpu_backtrace(void) +{ + dump_stack(); +} +#endif + extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); typedef void (*phys_reset_t)(unsigned long); diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index b7b4c86e338b..7b1e20ec3119 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -69,6 +69,7 @@ enum ipi_msg_type { IPI_CPU_STOP, IPI_IRQ_WORK, IPI_COMPLETION, + IPI_CPU_BACKTRACE, }; static DECLARE_COMPLETION(cpu_running); @@ -471,6 +472,7 @@ static const char *ipi_types[NR_IPI] = { S(IPI_CPU_STOP, "CPU stop interrupts"), S(IPI_IRQ_WORK, "IRQ work interrupts"), S(IPI_COMPLETION, "completion interrupts"), + S(IPI_CPU_BACKTRACE, "CPU backtrace"), }; void show_ipi_list(struct seq_file *p, int prec) @@ -543,6 +545,58 @@ static void ipi_complete(unsigned int cpu) complete(per_cpu(cpu_completion, cpu)); } +static cpumask_t backtrace_mask; +static DEFINE_RAW_SPINLOCK(backtrace_lock); + +/* "in progress" flag of arch_trigger_all_cpu_backtrace */ +static unsigned long backtrace_flag; + +void smp_send_all_cpu_backtrace(void) +{ + unsigned int this_cpu = smp_processor_id(); + int i; + + if (test_and_set_bit(0, &backtrace_flag)) + /* + * If there is already a trigger_all_cpu_backtrace() in progress + * (backtrace_flag == 1), don't output double cpu dump infos. + */ + return; + + cpumask_copy(&backtrace_mask, cpu_online_mask); + cpu_clear(this_cpu, backtrace_mask); + + pr_info("Backtrace for cpu %d (current):\n", this_cpu); + dump_stack(); + + pr_info("\nsending IPI to all other CPUs:\n"); + smp_cross_call(&backtrace_mask, IPI_CPU_BACKTRACE); + + /* Wait for up to 10 seconds for all other CPUs to do the backtrace */ + for (i = 0; i < 10 * 1000; i++) { + if (cpumask_empty(&backtrace_mask)) + break; + mdelay(1); + } + + clear_bit(0, &backtrace_flag); + smp_mb__after_clear_bit(); +} + +/* + * ipi_cpu_backtrace - handle IPI from smp_send_all_cpu_backtrace() + */ +static void ipi_cpu_backtrace(unsigned int cpu, struct pt_regs *regs) +{ + if (cpu_isset(cpu, backtrace_mask)) { + raw_spin_lock(&backtrace_lock); + pr_warning("IPI backtrace for cpu %d\n", cpu); + show_regs(regs); + raw_spin_unlock(&backtrace_lock); + cpu_clear(cpu, backtrace_mask); + } +} + /* * Main handler for inter-processor interrupts */ @@ -607,6 +661,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs) irq_exit(); break; + case IPI_CPU_BACKTRACE: + ipi_cpu_backtrace(cpu, regs); + break; + default: printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); From 93d752ddb17b19282e489386abb90447b9263e27 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 12 Jul 2011 19:53:24 -0700 Subject: [PATCH 015/358] cgroup: Add generic cgroup subsystem permission checks Rather than using explicit euid == 0 checks when trying to move tasks into a cgroup via CFS, move permission checks into each specific cgroup subsystem. If a subsystem does not specify a 'allow_attach' handler, then we fall back to doing our checks the old way. Use the 'allow_attach' handler for the 'cpu' cgroup to allow non-root processes to add arbitrary processes to a 'cpu' cgroup if it has the CAP_SYS_NICE capability set. This version of the patch adds a 'allow_attach' handler instead of reusing the 'can_attach' handler. If the 'can_attach' handler is reused, a new cgroup that implements 'can_attach' but not the permission checks could end up with no permission checks at all. Change-Id: Icfa950aa9321d1ceba362061d32dc7dfa2c64f0c Original-Author: San Mehat Signed-off-by: Colin Cross --- Documentation/cgroups/cgroups.txt | 9 ++++++++ include/linux/cgroup.h | 2 ++ kernel/cgroup.c | 34 ++++++++++++++++++++++++++++--- kernel/sched/core.c | 19 +++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index 821de56d1580..91bd5e0d34e4 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt @@ -578,6 +578,15 @@ is completely unused; @cgrp->parent is still valid. (Note - can also be called for a newly-created cgroup if an error occurs after this subsystem's create() method has been called for the new cgroup). +int allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +(cgroup_mutex held by caller) + +Called prior to moving a task into a cgroup; if the subsystem +returns an error, this will abort the attach operation. Used +to extend the permission checks - if all subsystems in a cgroup +return 0, the attach will be allowed to proceed, even if the +default permission check (root or same user) fails. + int can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) (cgroup_mutex held by caller) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 9450f025fe0c..bbda8c015591 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -579,6 +579,8 @@ struct cgroup_subsys { void (*css_offline)(struct cgroup_subsys_state *css); void (*css_free)(struct cgroup_subsys_state *css); + int (*allow_attach)(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset); int (*can_attach)(struct cgroup_subsys_state *css, struct cgroup_taskset *tset); void (*cancel_attach)(struct cgroup_subsys_state *css, diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 105f273b6f86..81523efd585b 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2110,6 +2110,25 @@ out_free_group_list: return retval; } +static int cgroup_allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +{ + struct cgroup_subsys_state *css; + int i; + int ret; + + for_each_css(css, i, cgrp) { + if (css->ss->allow_attach) { + ret = css->ss->allow_attach(css, tset); + if (ret) + return ret; + } else { + return -EACCES; + } + } + + return 0; +} + /* * Find the task_struct of the task to attach by vpid and pass it along to the * function to attach either it or all tasks in its threadgroup. Will lock @@ -2141,9 +2160,18 @@ retry_find_task: if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) && !uid_eq(cred->euid, tcred->uid) && !uid_eq(cred->euid, tcred->suid)) { - rcu_read_unlock(); - ret = -EACCES; - goto out_unlock_cgroup; + /* + * if the default permission check fails, give each + * cgroup a chance to extend the permission check + */ + struct cgroup_taskset tset = { }; + tset.single.task = tsk; + tset.single.cgrp = cgrp; + ret = cgroup_allow_attach(cgrp, &tset); + if (ret) { + rcu_read_unlock(); + goto out_unlock_cgroup; + } } } else tsk = current; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 4bf263e8040a..a55b70c98302 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7619,6 +7619,24 @@ static void cpu_cgroup_css_offline(struct cgroup_subsys_state *css) sched_offline_group(tg); } +static int cpu_cgroup_allow_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) +{ + const struct cred *cred = current_cred(), *tcred; + struct task_struct *task; + + cgroup_taskset_for_each(task, css, tset) { + tcred = __task_cred(task); + + if ((current != task) && !capable(CAP_SYS_NICE) && + !uid_eq(cred->euid, tcred->uid) && + !uid_eq(cred->euid, tcred->suid)) + return -EACCES; + } + + return 0; +} + static int cpu_cgroup_can_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { @@ -7989,6 +8007,7 @@ struct cgroup_subsys cpu_cgroup_subsys = { .css_offline = cpu_cgroup_css_offline, .can_attach = cpu_cgroup_can_attach, .attach = cpu_cgroup_attach, + .allow_attach = cpu_cgroup_allow_attach, .exit = cpu_cgroup_exit, .subsys_id = cpu_cgroup_subsys_id, .base_cftypes = cpu_files, From eada7ab14e46ee12ccb587ab361caca9bfa61a33 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 9 May 2005 14:10:26 -0700 Subject: [PATCH 016/358] ARM: Make low-level printk work Makes low-level printk work. Signed-off-by: Tony Lindgren --- kernel/printk/printk.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 4dae9cbe9259..521a0c7155a3 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -54,6 +54,10 @@ #include "console_cmdline.h" #include "braille.h" +#ifdef CONFIG_DEBUG_LL +extern void printascii(char *); +#endif + /* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL @@ -1542,6 +1546,10 @@ asmlinkage int vprintk_emit(int facility, int level, */ text_len = vscnprintf(text, sizeof(textbuf), fmt, args); +#ifdef CONFIG_DEBUG_LL + printascii(text); +#endif + /* mark and strip a trailing newline */ if (text_len && text[text_len-1] == '\n') { text_len--; From 65d58dbb17fc449ac0315c904d5c2806433bc73c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Sat, 1 Dec 2007 18:34:14 -0800 Subject: [PATCH 017/358] [ARM] armv6 dcc tty driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Brian Swetland Signed-off-by: Arve Hjønnevåg --- drivers/char/Kconfig | 4 + drivers/char/Makefile | 1 + drivers/char/dcc_tty.c | 326 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 331 insertions(+) create mode 100644 drivers/char/dcc_tty.c diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index c5095b20920a..6ee8a3f6a97f 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -592,6 +592,10 @@ config DEVPORT depends on ISA || PCI default y +config DCC_TTY + tristate "DCC tty driver" + depends on ARM + source "drivers/s390/char/Kconfig" config MSM_SMD_PKT diff --git a/drivers/char/Makefile b/drivers/char/Makefile index a324f9303e36..9e4c495d71e5 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_PCMCIA) += pcmcia/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ +obj-$(CONFIG_DCC_TTY) += dcc_tty.o obj-$(CONFIG_PS3_FLASH) += ps3flash.o obj-$(CONFIG_JS_RTC) += js-rtc.o diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c new file mode 100644 index 000000000000..a787accdcb14 --- /dev/null +++ b/drivers/char/dcc_tty.c @@ -0,0 +1,326 @@ +/* drivers/char/dcc_tty.c + * + * Copyright (C) 2007 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 +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("DCC TTY Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +static spinlock_t g_dcc_tty_lock = SPIN_LOCK_UNLOCKED; +static struct hrtimer g_dcc_timer; +static char g_dcc_buffer[16]; +static int g_dcc_buffer_head; +static int g_dcc_buffer_count; +static unsigned g_dcc_write_delay_usecs = 1; +static struct tty_driver *g_dcc_tty_driver; +static struct tty_struct *g_dcc_tty; +static int g_dcc_tty_open_count; + +static void dcc_poll_locked(void) +{ + char ch; + int rch; + int written; + + while (g_dcc_buffer_count) { + ch = g_dcc_buffer[g_dcc_buffer_head]; + asm( + "mrc 14, 0, r15, c0, c1, 0\n" + "mcrcc 14, 0, %1, c0, c5, 0\n" + "movcc %0, #1\n" + "movcs %0, #0\n" + : "=r" (written) + : "r" (ch) + ); + if (written) { + if (ch == '\n') + g_dcc_buffer[g_dcc_buffer_head] = '\r'; + else { + g_dcc_buffer_head = (g_dcc_buffer_head + 1) % ARRAY_SIZE(g_dcc_buffer); + g_dcc_buffer_count--; + if (g_dcc_tty) + tty_wakeup(g_dcc_tty); + } + g_dcc_write_delay_usecs = 1; + } else { + if (g_dcc_write_delay_usecs > 0x100) + break; + g_dcc_write_delay_usecs <<= 1; + udelay(g_dcc_write_delay_usecs); + } + } + + if (g_dcc_tty && !test_bit(TTY_THROTTLED, &g_dcc_tty->flags)) { + asm( + "mrc 14, 0, %0, c0, c1, 0\n" + "tst %0, #(1 << 30)\n" + "moveq %0, #-1\n" + "mrcne 14, 0, %0, c0, c5, 0\n" + : "=r" (rch) + ); + if (rch >= 0) { + ch = rch; + tty_insert_flip_string(g_dcc_tty, &ch, 1); + tty_flip_buffer_push(g_dcc_tty); + } + } + + + if (g_dcc_buffer_count) + hrtimer_start(&g_dcc_timer, ktime_set(0, g_dcc_write_delay_usecs * NSEC_PER_USEC), HRTIMER_MODE_REL); + else + hrtimer_start(&g_dcc_timer, ktime_set(0, 20 * NSEC_PER_MSEC), HRTIMER_MODE_REL); +} + +static int dcc_tty_open(struct tty_struct * tty, struct file * filp) +{ + int ret; + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + if (g_dcc_tty == NULL || g_dcc_tty == tty) { + g_dcc_tty = tty; + g_dcc_tty_open_count++; + ret = 0; + } else + ret = -EBUSY; + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + + printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty, filp->f_flags, ret); + + return ret; +} + +static void dcc_tty_close(struct tty_struct * tty, struct file * filp) +{ + printk("dcc_tty_close, tty %p, f_flags %x\n", tty, filp->f_flags); + if (g_dcc_tty == tty) { + if (--g_dcc_tty_open_count == 0) + g_dcc_tty = NULL; + } +} + +static int dcc_write(const unsigned char *buf_start, int count) +{ + const unsigned char *buf = buf_start; + unsigned long irq_flags; + int copy_len; + int space_left; + int tail; + + if (count < 1) + return 0; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + do { + tail = (g_dcc_buffer_head + g_dcc_buffer_count) % ARRAY_SIZE(g_dcc_buffer); + copy_len = ARRAY_SIZE(g_dcc_buffer) - tail; + space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; + if (copy_len > space_left) + copy_len = space_left; + if (copy_len > count) + copy_len = count; + memcpy(&g_dcc_buffer[tail], buf, copy_len); + g_dcc_buffer_count += copy_len; + buf += copy_len; + count -= copy_len; + if (copy_len < count && copy_len < space_left) { + space_left -= copy_len; + copy_len = count; + if (copy_len > space_left) { + copy_len = space_left; + } + memcpy(g_dcc_buffer, buf, copy_len); + buf += copy_len; + count -= copy_len; + g_dcc_buffer_count += copy_len; + } + dcc_poll_locked(); + space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; + } while(count && space_left); + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + return buf - buf_start; +} + +static int dcc_tty_write(struct tty_struct * tty, const unsigned char *buf, int count) +{ + int ret; + /* printk("dcc_tty_write %p, %d\n", buf, count); */ + ret = dcc_write(buf, count); + if (ret != count) + printk("dcc_tty_write %p, %d, returned %d\n", buf, count, ret); + return ret; +} + +static int dcc_tty_write_room(struct tty_struct *tty) +{ + int space_left; + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + return space_left; +} + +static int dcc_tty_chars_in_buffer(struct tty_struct *tty) +{ + int ret; + asm( + "mrc 14, 0, %0, c0, c1, 0\n" + "mov %0, %0, LSR #30\n" + "and %0, %0, #1\n" + : "=r" (ret) + ); + return ret; +} + +static void dcc_tty_unthrottle(struct tty_struct * tty) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + dcc_poll_locked(); + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); +} + +static enum hrtimer_restart dcc_tty_timer_func(struct hrtimer *timer) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + dcc_poll_locked(); + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + return HRTIMER_NORESTART; +} + +void dcc_console_write(struct console *co, const char *b, unsigned count) +{ +#if 1 + dcc_write(b, count); +#else + /* blocking printk */ + while (count > 0) { + int written; + written = dcc_write(b, count); + if (written) { + b += written; + count -= written; + } + } +#endif +} + +static struct tty_driver *dcc_console_device(struct console *c, int *index) +{ + *index = 0; + return g_dcc_tty_driver; +} + +static int __init dcc_console_setup(struct console *co, char *options) +{ + if (co->index != 0) + return -ENODEV; + return 0; +} + + +static struct console dcc_console = +{ + .name = "ttyDCC", + .write = dcc_console_write, + .device = dcc_console_device, + .setup = dcc_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +static struct tty_operations dcc_tty_ops = { + .open = dcc_tty_open, + .close = dcc_tty_close, + .write = dcc_tty_write, + .write_room = dcc_tty_write_room, + .chars_in_buffer = dcc_tty_chars_in_buffer, + .unthrottle = dcc_tty_unthrottle, +}; + +static int __init dcc_tty_init(void) +{ + int ret; + + hrtimer_init(&g_dcc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + g_dcc_timer.function = dcc_tty_timer_func; + + g_dcc_tty_driver = alloc_tty_driver(1); + if (!g_dcc_tty_driver) { + printk(KERN_ERR "dcc_tty_probe: alloc_tty_driver failed\n"); + ret = -ENOMEM; + goto err_alloc_tty_driver_failed; + } + g_dcc_tty_driver->owner = THIS_MODULE; + g_dcc_tty_driver->driver_name = "dcc"; + g_dcc_tty_driver->name = "ttyDCC"; + g_dcc_tty_driver->major = 0; // auto assign + g_dcc_tty_driver->minor_start = 0; + g_dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + g_dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL; + g_dcc_tty_driver->init_termios = tty_std_termios; + g_dcc_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(g_dcc_tty_driver, &dcc_tty_ops); + ret = tty_register_driver(g_dcc_tty_driver); + if (ret) { + printk(KERN_ERR "dcc_tty_probe: tty_register_driver failed, %d\n", ret); + goto err_tty_register_driver_failed; + } + tty_register_device(g_dcc_tty_driver, 0, NULL); + + register_console(&dcc_console); + hrtimer_start(&g_dcc_timer, ktime_set(0, 0), HRTIMER_MODE_REL); + + return 0; + +err_tty_register_driver_failed: + put_tty_driver(g_dcc_tty_driver); + g_dcc_tty_driver = NULL; +err_alloc_tty_driver_failed: + return ret; +} + +static void __exit dcc_tty_exit(void) +{ + int ret; + + tty_unregister_device(g_dcc_tty_driver, 0); + ret = tty_unregister_driver(g_dcc_tty_driver); + if (ret < 0) { + printk(KERN_ERR "dcc_tty_remove: tty_unregister_driver failed, %d\n", ret); + } else { + put_tty_driver(g_dcc_tty_driver); + } + g_dcc_tty_driver = NULL; +} + +module_init(dcc_tty_init); +module_exit(dcc_tty_exit); + + From 46123744a14e5aff835680ffc6ba8e16b5912266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 17 Feb 2009 14:51:02 -0800 Subject: [PATCH 018/358] mm: Add min_free_order_shift tunable. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default the kernel tries to keep half as much memory free at each order as it does for one order below. This can be too agressive when running without swap. Change-Id: I5efc1a0b50f41ff3ac71e92d2efd175dedd54ead Signed-off-by: Arve Hjønnevåg --- kernel/sysctl.c | 8 ++++++++ mm/page_alloc.c | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 49e13e1f8fe6..1fee90f4b3a5 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -104,6 +104,7 @@ extern char core_pattern[]; extern unsigned int core_pipe_limit; #endif extern int pid_max; +extern int min_free_order_shift; extern int pid_max_min, pid_max_max; extern int percpu_pagelist_fraction; extern int compat_log; @@ -1312,6 +1313,13 @@ static struct ctl_table vm_table[] = { .proc_handler = min_free_kbytes_sysctl_handler, .extra1 = &zero, }, + { + .procname = "min_free_order_shift", + .data = &min_free_order_shift, + .maxlen = sizeof(min_free_order_shift), + .mode = 0644, + .proc_handler = &proc_dointvec + }, { .procname = "percpu_pagelist_fraction", .data = &percpu_pagelist_fraction, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 3bac76ae4b30..348ca037b055 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -206,6 +206,7 @@ static char * const zone_names[MAX_NR_ZONES] = { int min_free_kbytes = 1024; int user_min_free_kbytes = -1; +int min_free_order_shift = 1; static unsigned long __meminitdata nr_kernel_pages; static unsigned long __meminitdata nr_all_pages; @@ -1712,7 +1713,7 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark, free_pages -= z->free_area[o].nr_free << o; /* Require fewer higher order pages to be free */ - min >>= 1; + min >>= min_free_order_shift; if (free_pages <= min) return false; From ab70336ac33124f9e2b749906214a2ce83bbb50e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 9 Jun 2009 20:17:45 -0700 Subject: [PATCH 019/358] [ARM] Optionally flush entire dcache from v6_dma_flush_range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CACHE_FLUSH_RANGE_LIMIT is defined, then the entire dcache will be flushed if the requested range is larger than this limit. Change-Id: I29277d645a9d6716b1952cf3b870c78496261dd0 Signed-off-by: Arve Hjønnevåg --- arch/arm/mm/cache-v6.S | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S index d8fd4d4bd3d4..7a3d3d8d98d7 100644 --- a/arch/arm/mm/cache-v6.S +++ b/arch/arm/mm/cache-v6.S @@ -270,6 +270,11 @@ v6_dma_clean_range: * - end - virtual end address of region */ ENTRY(v6_dma_flush_range) +#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT + sub r2, r1, r0 + cmp r2, #CONFIG_CACHE_FLUSH_RANGE_LIMIT + bhi v6_dma_flush_dcache_all +#endif #ifdef CONFIG_DMA_CACHE_RWFO ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership @@ -292,6 +297,18 @@ ENTRY(v6_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain write buffer mov pc, lr +#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT +v6_dma_flush_dcache_all: + mov r0, #0 +#ifdef HARVARD_CACHE + mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate +#else + mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate +#endif + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer + mov pc, lr +#endif + /* * dma_map_area(start, size, dir) * - start - kernel virtual start address From c09eab911f0b84d3c159c5fac2f81d9b21511af7 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Thu, 22 Jan 2009 12:23:32 -0800 Subject: [PATCH 020/358] [docs] android: Add android config documentation to boot framework. Signed-off-by: Mike Chan --- Documentation/android.txt | 121 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 Documentation/android.txt diff --git a/Documentation/android.txt b/Documentation/android.txt new file mode 100644 index 000000000000..72a62afdf202 --- /dev/null +++ b/Documentation/android.txt @@ -0,0 +1,121 @@ + ============= + A N D R O I D + ============= + +Copyright (C) 2009 Google, Inc. +Written by Mike Chan + +CONTENTS: +--------- + +1. Android + 1.1 Required enabled config options + 1.2 Required disabled config options + 1.3 Recommended enabled config options +2. Contact + + +1. Android +========== + +Android (www.android.com) is an open source operating system for mobile devices. +This document describes configurations needed to run the Android framework on +top of the Linux kernel. + +To see a working defconfig look at msm_defconfig or goldfish_defconfig +which can be found at http://android.git.kernel.org in kernel/common.git +and kernel/msm.git + + +1.1 Required enabled config options +----------------------------------- +After building a standard defconfig, ensure that these options are enabled in +your .config or defconfig if they are not already. Based off the msm_defconfig. +You should keep the rest of the default options enabled in the defconfig +unless you know what you are doing. + +ANDROID_PARANOID_NETWORK +ASHMEM +CONFIG_FB_MODE_HELPERS +CONFIG_FONT_8x16 +CONFIG_FONT_8x8 +CONFIG_YAFFS_SHORT_NAMES_IN_RAM +DAB +EARLYSUSPEND +FB +FB_CFB_COPYAREA +FB_CFB_FILLRECT +FB_CFB_IMAGEBLIT +FB_DEFERRED_IO +FB_TILEBLITTING +HIGH_RES_TIMERS +INOTIFY +INOTIFY_USER +INPUT_EVDEV +INPUT_GPIO +INPUT_MISC +LEDS_CLASS +LEDS_GPIO +LOCK_KERNEL +LkOGGER +LOW_MEMORY_KILLER +MISC_DEVICES +NEW_LEDS +NO_HZ +POWER_SUPPLY +PREEMPT +RAMFS +RTC_CLASS +RTC_LIB +SWITCH +SWITCH_GPIO +TMPFS +UID_STAT +UID16 +USB_FUNCTION +USB_FUNCTION_ADB +USER_WAKELOCK +VIDEO_OUTPUT_CONTROL +WAKELOCK +YAFFS_AUTO_YAFFS2 +YAFFS_FS +YAFFS_YAFFS1 +YAFFS_YAFFS2 + + +1.2 Required disabled config options +------------------------------------ +CONFIG_YAFFS_DISABLE_LAZY_LOAD +DNOTIFY + + +1.3 Recommended enabled config options +------------------------------ +ANDROID_PMEM +ANDROID_RAM_CONSOLE +ANDROID_RAM_CONSOLE_ERROR_CORRECTION +SCHEDSTATS +DEBUG_PREEMPT +DEBUG_MUTEXES +DEBUG_SPINLOCK_SLEEP +DEBUG_INFO +FRAME_POINTER +CPU_FREQ +CPU_FREQ_TABLE +CPU_FREQ_DEFAULT_GOV_ONDEMAND +CPU_FREQ_GOV_ONDEMAND +CRC_CCITT +EMBEDDED +INPUT_TOUCHSCREEN +I2C +I2C_BOARDINFO +LOG_BUF_SHIFT=17 +SERIAL_CORE +SERIAL_CORE_CONSOLE + + +2. Contact +========== +website: http://android.git.kernel.org + +mailing-lists: android-kernel@googlegroups.com From 81e2149523118c9030be19a6de7986a701659d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 16 Mar 2012 17:44:42 -0700 Subject: [PATCH 021/358] PM / Sleep: Add wake lock api wrapper on top of wakeup sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Icaad02fe1e8856fdc2e4215f380594a5dde8e002 Signed-off-by: Arve Hjønnevåg --- include/linux/wakelock.h | 67 ++++++++++++++++++++++++++++++++++++++++ kernel/power/Kconfig | 8 +++++ 2 files changed, 75 insertions(+) create mode 100644 include/linux/wakelock.h diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h new file mode 100644 index 000000000000..f4a698a22880 --- /dev/null +++ b/include/linux/wakelock.h @@ -0,0 +1,67 @@ +/* include/linux/wakelock.h + * + * Copyright (C) 2007-2012 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_WAKELOCK_H +#define _LINUX_WAKELOCK_H + +#include +#include + +/* A wake_lock prevents the system from entering suspend or other low power + * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock + * prevents a full system suspend. + */ + +enum { + WAKE_LOCK_SUSPEND, /* Prevent suspend */ + WAKE_LOCK_TYPE_COUNT +}; + +struct wake_lock { + struct wakeup_source ws; +}; + +static inline void wake_lock_init(struct wake_lock *lock, int type, + const char *name) +{ + wakeup_source_init(&lock->ws, name); +} + +static inline void wake_lock_destroy(struct wake_lock *lock) +{ + wakeup_source_trash(&lock->ws); +} + +static inline void wake_lock(struct wake_lock *lock) +{ + __pm_stay_awake(&lock->ws); +} + +static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) +{ + __pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout)); +} + +static inline void wake_unlock(struct wake_lock *lock) +{ + __pm_relax(&lock->ws); +} + +static inline int wake_lock_active(struct wake_lock *lock) +{ + return lock->ws.active; +} + +#endif diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 4514b86671c4..6c78fe5ee692 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -18,6 +18,14 @@ config SUSPEND_FREEZER Turning OFF this setting is NOT recommended! If in doubt, say Y. +config HAS_WAKELOCK + bool + default y + +config WAKELOCK + bool + default y + config HIBERNATE_CALLBACKS bool From 625c814c7d5730417d377b91f208df9449101624 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 14 Oct 2008 12:50:16 -0400 Subject: [PATCH 022/358] switch: switch class and GPIO drivers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit switch: Export symbol switch_set_state. Signed-off-by: Mike Lockwood switch: gpio: Don't call request_irq with interrupts disabled Signed-off-by: Arve Hjønnevåg switch: Use device_create instead of device_create_drvdata. device_create_drvdata is obsolete. Signed-off-by: Arve Hjønnevåg switch_gpio: Add missing #include Change-Id: I6c397e41bbe1457162cc69e31a29db5d9f76fccb Signed-off-by: Mike Lockwood --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/switch/Kconfig | 15 +++ drivers/switch/Makefile | 4 + drivers/switch/switch_class.c | 174 ++++++++++++++++++++++++++++++++++ drivers/switch/switch_gpio.c | 172 +++++++++++++++++++++++++++++++++ include/linux/switch.h | 53 +++++++++++ 7 files changed, 421 insertions(+) create mode 100644 drivers/switch/Kconfig create mode 100644 drivers/switch/Makefile create mode 100644 drivers/switch/switch_class.c create mode 100644 drivers/switch/switch_gpio.c create mode 100644 include/linux/switch.h diff --git a/drivers/Kconfig b/drivers/Kconfig index b3138fbb46a4..5e0eb155ee0e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -98,6 +98,8 @@ source "drivers/memstick/Kconfig" source "drivers/leds/Kconfig" +source "drivers/switch/Kconfig" + source "drivers/accessibility/Kconfig" source "drivers/infiniband/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 8e3b8b06c0b2..cde0860bc581 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -113,6 +113,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-y += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-y += leds/ +obj-$(CONFIG_SWITCH) += switch/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ diff --git a/drivers/switch/Kconfig b/drivers/switch/Kconfig new file mode 100644 index 000000000000..19404b6f7778 --- /dev/null +++ b/drivers/switch/Kconfig @@ -0,0 +1,15 @@ +menuconfig SWITCH + tristate "Switch class support" + help + Say Y here to enable switch class support. This allows + monitoring switches by userspace via sysfs and uevent. + +if SWITCH + +config SWITCH_GPIO + tristate "GPIO Swith support" + depends on GPIOLIB + help + Say Y here to enable GPIO based switch support. + +endif # SWITCH diff --git a/drivers/switch/Makefile b/drivers/switch/Makefile new file mode 100644 index 000000000000..f7606ed4a719 --- /dev/null +++ b/drivers/switch/Makefile @@ -0,0 +1,4 @@ +# Switch Class Driver +obj-$(CONFIG_SWITCH) += switch_class.o +obj-$(CONFIG_SWITCH_GPIO) += switch_gpio.o + diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c new file mode 100644 index 000000000000..e05fc2591147 --- /dev/null +++ b/drivers/switch/switch_class.c @@ -0,0 +1,174 @@ +/* + * drivers/switch/switch_class.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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 +#include +#include +#include +#include +#include +#include + +struct class *switch_class; +static atomic_t device_count; + +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct switch_dev *sdev = (struct switch_dev *) + dev_get_drvdata(dev); + + if (sdev->print_state) { + int ret = sdev->print_state(sdev, buf); + if (ret >= 0) + return ret; + } + return sprintf(buf, "%d\n", sdev->state); +} + +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct switch_dev *sdev = (struct switch_dev *) + dev_get_drvdata(dev); + + if (sdev->print_name) { + int ret = sdev->print_name(sdev, buf); + if (ret >= 0) + return ret; + } + return sprintf(buf, "%s\n", sdev->name); +} + +static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL); +static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL); + +void switch_set_state(struct switch_dev *sdev, int state) +{ + char name_buf[120]; + char state_buf[120]; + char *prop_buf; + char *envp[3]; + int env_offset = 0; + int length; + + if (sdev->state != state) { + sdev->state = state; + + prop_buf = (char *)get_zeroed_page(GFP_KERNEL); + if (prop_buf) { + length = name_show(sdev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(name_buf, sizeof(name_buf), + "SWITCH_NAME=%s", prop_buf); + envp[env_offset++] = name_buf; + } + length = state_show(sdev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(state_buf, sizeof(state_buf), + "SWITCH_STATE=%s", prop_buf); + envp[env_offset++] = state_buf; + } + envp[env_offset] = NULL; + kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp); + free_page((unsigned long)prop_buf); + } else { + printk(KERN_ERR "out of memory in switch_set_state\n"); + kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE); + } + } +} +EXPORT_SYMBOL_GPL(switch_set_state); + +static int create_switch_class(void) +{ + if (!switch_class) { + switch_class = class_create(THIS_MODULE, "switch"); + if (IS_ERR(switch_class)) + return PTR_ERR(switch_class); + atomic_set(&device_count, 0); + } + + return 0; +} + +int switch_dev_register(struct switch_dev *sdev) +{ + int ret; + + if (!switch_class) { + ret = create_switch_class(); + if (ret < 0) + return ret; + } + + sdev->index = atomic_inc_return(&device_count); + sdev->dev = device_create(switch_class, NULL, + MKDEV(0, sdev->index), NULL, sdev->name); + if (IS_ERR(sdev->dev)) + return PTR_ERR(sdev->dev); + + ret = device_create_file(sdev->dev, &dev_attr_state); + if (ret < 0) + goto err_create_file_1; + ret = device_create_file(sdev->dev, &dev_attr_name); + if (ret < 0) + goto err_create_file_2; + + dev_set_drvdata(sdev->dev, sdev); + sdev->state = 0; + return 0; + +err_create_file_2: + device_remove_file(sdev->dev, &dev_attr_state); +err_create_file_1: + device_destroy(switch_class, MKDEV(0, sdev->index)); + printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name); + + return ret; +} +EXPORT_SYMBOL_GPL(switch_dev_register); + +void switch_dev_unregister(struct switch_dev *sdev) +{ + device_remove_file(sdev->dev, &dev_attr_name); + device_remove_file(sdev->dev, &dev_attr_state); + device_destroy(switch_class, MKDEV(0, sdev->index)); + dev_set_drvdata(sdev->dev, NULL); +} +EXPORT_SYMBOL_GPL(switch_dev_unregister); + +static int __init switch_class_init(void) +{ + return create_switch_class(); +} + +static void __exit switch_class_exit(void) +{ + class_destroy(switch_class); +} + +module_init(switch_class_init); +module_exit(switch_class_exit); + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("Switch class driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/switch/switch_gpio.c b/drivers/switch/switch_gpio.c new file mode 100644 index 000000000000..621d62d20c99 --- /dev/null +++ b/drivers/switch/switch_gpio.c @@ -0,0 +1,172 @@ +/* + * drivers/switch/switch_gpio.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio_switch_data { + struct switch_dev sdev; + unsigned gpio; + const char *name_on; + const char *name_off; + const char *state_on; + const char *state_off; + int irq; + struct work_struct work; +}; + +static void gpio_switch_work(struct work_struct *work) +{ + int state; + struct gpio_switch_data *data = + container_of(work, struct gpio_switch_data, work); + + state = gpio_get_value(data->gpio); + switch_set_state(&data->sdev, state); +} + +static irqreturn_t gpio_irq_handler(int irq, void *dev_id) +{ + struct gpio_switch_data *switch_data = + (struct gpio_switch_data *)dev_id; + + schedule_work(&switch_data->work); + return IRQ_HANDLED; +} + +static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf) +{ + struct gpio_switch_data *switch_data = + container_of(sdev, struct gpio_switch_data, sdev); + const char *state; + if (switch_get_state(sdev)) + state = switch_data->state_on; + else + state = switch_data->state_off; + + if (state) + return sprintf(buf, "%s\n", state); + return -1; +} + +static int gpio_switch_probe(struct platform_device *pdev) +{ + struct gpio_switch_platform_data *pdata = pdev->dev.platform_data; + struct gpio_switch_data *switch_data; + int ret = 0; + + if (!pdata) + return -EBUSY; + + switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL); + if (!switch_data) + return -ENOMEM; + + switch_data->sdev.name = pdata->name; + switch_data->gpio = pdata->gpio; + switch_data->name_on = pdata->name_on; + switch_data->name_off = pdata->name_off; + switch_data->state_on = pdata->state_on; + switch_data->state_off = pdata->state_off; + switch_data->sdev.print_state = switch_gpio_print_state; + + ret = switch_dev_register(&switch_data->sdev); + if (ret < 0) + goto err_switch_dev_register; + + ret = gpio_request(switch_data->gpio, pdev->name); + if (ret < 0) + goto err_request_gpio; + + ret = gpio_direction_input(switch_data->gpio); + if (ret < 0) + goto err_set_gpio_input; + + INIT_WORK(&switch_data->work, gpio_switch_work); + + switch_data->irq = gpio_to_irq(switch_data->gpio); + if (switch_data->irq < 0) { + ret = switch_data->irq; + goto err_detect_irq_num_failed; + } + + ret = request_irq(switch_data->irq, gpio_irq_handler, + IRQF_TRIGGER_LOW, pdev->name, switch_data); + if (ret < 0) + goto err_request_irq; + + /* Perform initial detection */ + gpio_switch_work(&switch_data->work); + + return 0; + +err_request_irq: +err_detect_irq_num_failed: +err_set_gpio_input: + gpio_free(switch_data->gpio); +err_request_gpio: + switch_dev_unregister(&switch_data->sdev); +err_switch_dev_register: + kfree(switch_data); + + return ret; +} + +static int gpio_switch_remove(struct platform_device *pdev) +{ + struct gpio_switch_data *switch_data = platform_get_drvdata(pdev); + + cancel_work_sync(&switch_data->work); + gpio_free(switch_data->gpio); + switch_dev_unregister(&switch_data->sdev); + kfree(switch_data); + + return 0; +} + +static struct platform_driver gpio_switch_driver = { + .probe = gpio_switch_probe, + .remove = gpio_switch_remove, + .driver = { + .name = "switch-gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_switch_init(void) +{ + return platform_driver_register(&gpio_switch_driver); +} + +static void __exit gpio_switch_exit(void) +{ + platform_driver_unregister(&gpio_switch_driver); +} + +module_init(gpio_switch_init); +module_exit(gpio_switch_exit); + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("GPIO Switch driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/switch.h b/include/linux/switch.h new file mode 100644 index 000000000000..3e4c748e343a --- /dev/null +++ b/include/linux/switch.h @@ -0,0 +1,53 @@ +/* + * Switch class driver + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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_SWITCH_H__ +#define __LINUX_SWITCH_H__ + +struct switch_dev { + const char *name; + struct device *dev; + int index; + int state; + + ssize_t (*print_name)(struct switch_dev *sdev, char *buf); + ssize_t (*print_state)(struct switch_dev *sdev, char *buf); +}; + +struct gpio_switch_platform_data { + const char *name; + unsigned gpio; + + /* if NULL, switch_dev.name will be printed */ + const char *name_on; + const char *name_off; + /* if NULL, "0" or "1" will be printed */ + const char *state_on; + const char *state_off; +}; + +extern int switch_dev_register(struct switch_dev *sdev); +extern void switch_dev_unregister(struct switch_dev *sdev); + +static inline int switch_get_state(struct switch_dev *sdev) +{ + return sdev->state; +} + +extern void switch_set_state(struct switch_dev *sdev, int state); + +#endif /* __LINUX_SWITCH_H__ */ From 32a771aaf05c3599184ef5f9d2b545d79b5b3fc4 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 11 Mar 2014 18:19:53 -0700 Subject: [PATCH 023/358] HACK: input: evdev: disable EVIOCREVOKE Android used the same ioctl signature as EVOICREVOKE for EVIOCSSUSPENDBLOCK. Disable EVIOCREVOKE until Android userspace is available that uses EPOLLWAKEUP instead. Change-Id: I51aa63fd19b20a3966bef1c46d1bb845c2589a5a Signed-off-by: Colin Cross --- drivers/input/evdev.c | 13 ++++++++++++- include/uapi/linux/input.h | 4 +++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index a06e12552886..3b0ff9c82d7b 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -811,6 +811,11 @@ static int evdev_handle_mt_request(struct input_dev *dev, return 0; } +/* + * HACK: disable conflicting EVIOCREVOKE until Android userspace stops using + * EVIOCSSUSPENDBLOCK + */ +/* static int evdev_revoke(struct evdev *evdev, struct evdev_client *client, struct file *file) { @@ -821,6 +826,7 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client, return 0; } +*/ static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) @@ -884,12 +890,17 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, else return evdev_ungrab(evdev, client); + /* + * HACK: disable conflicting EVIOCREVOKE until Android userspace stops + * using EVIOCSSUSPENDBLOCK + */ + /* case EVIOCREVOKE: if (p) return -EINVAL; else return evdev_revoke(evdev, client, file); - + */ case EVIOCSCLOCKID: if (copy_from_user(&i, p, sizeof(unsigned int))) return -EFAULT; diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index bd24470d24a2..24593061fea5 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -152,7 +152,9 @@ struct input_keymap_entry { #define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ -#define EVIOCREVOKE _IOW('E', 0x91, int) /* Revoke device access */ + +/* HACK: disable conflicting EVIOCREVOKE until Android userspace stops using EVIOCSSUSPENDBLOCK */ +/*#define EVIOCREVOKE _IOW('E', 0x91, int)*/ /* Revoke device access */ #define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ From 6e71fda4b35e9b8b4f2f009b9d2b70214ea2db3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 17 Oct 2008 15:20:55 -0700 Subject: [PATCH 024/358] Input: evdev - Add ioctl to block suspend while event queue is not empty. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an ioctl, EVIOCSSUSPENDBLOCK, to enable a wakelock that will block suspend while the event queue is not empty. This allows userspace code to process input events while the device appears to be asleep. The current code holds the wakelock for up 5 seconds for every input device and client. This can prevent suspend if sensor with a high data rate is active, even when that sensor is not capable of waking the device once it is suspended. Change-Id: I624d66ef30a0b3abb543685c343382b8419b42b9 Signed-off-by: Arve Hjønnevåg --- drivers/input/evdev.c | 55 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/input.h | 3 +++ 2 files changed, 58 insertions(+) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 3b0ff9c82d7b..a53928053e91 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "input-compat.h" struct evdev { @@ -46,6 +47,9 @@ struct evdev_client { unsigned int tail; unsigned int packet_head; /* [future] position of the first element of next packet */ spinlock_t buffer_lock; /* protects access to buffer, head and tail */ + struct wake_lock wake_lock; + bool use_wake_lock; + char name[28]; struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; @@ -150,10 +154,14 @@ static void __pass_event(struct evdev_client *client, client->buffer[client->tail].value = 0; client->packet_head = client->tail; + if (client->use_wake_lock) + wake_unlock(&client->wake_lock); } if (event->type == EV_SYN && event->code == SYN_REPORT) { client->packet_head = client->head; + if (client->use_wake_lock) + wake_lock(&client->wake_lock); kill_fasync(&client->fasync, SIGIO, POLL_IN); } } @@ -372,10 +380,14 @@ static int evdev_release(struct inode *inode, struct file *file) evdev_detach_client(evdev, client); + if (client->use_wake_lock) + wake_lock_destroy(&client->wake_lock); + if (is_vmalloc_addr(client)) vfree(client); else kfree(client); + kfree(client); evdev_close_device(evdev); @@ -408,6 +420,8 @@ static int evdev_open(struct inode *inode, struct file *file) client->bufsize = bufsize; spin_lock_init(&client->buffer_lock); + snprintf(client->name, sizeof(client->name), "%s-%d", + dev_name(&evdev->dev), task_tgid_vnr(current)); client->evdev = evdev; evdev_attach_client(evdev, client); @@ -474,6 +488,9 @@ static int evdev_fetch_next_event(struct evdev_client *client, if (have_event) { *event = client->buffer[client->tail++]; client->tail &= client->bufsize - 1; + if (client->use_wake_lock && + client->packet_head == client->tail) + wake_unlock(&client->wake_lock); } spin_unlock_irq(&client->buffer_lock); @@ -828,6 +845,35 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client, } */ +static int evdev_enable_suspend_block(struct evdev *evdev, + struct evdev_client *client) +{ + if (client->use_wake_lock) + return 0; + + spin_lock_irq(&client->buffer_lock); + wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name); + client->use_wake_lock = true; + if (client->packet_head != client->tail) + wake_lock(&client->wake_lock); + spin_unlock_irq(&client->buffer_lock); + return 0; +} + +static int evdev_disable_suspend_block(struct evdev *evdev, + struct evdev_client *client) +{ + if (!client->use_wake_lock) + return 0; + + spin_lock_irq(&client->buffer_lock); + client->use_wake_lock = false; + wake_lock_destroy(&client->wake_lock); + spin_unlock_irq(&client->buffer_lock); + + return 0; +} + static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { @@ -920,6 +966,15 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, case EVIOCSKEYCODE_V2: return evdev_handle_set_keycode_v2(dev, p); + + case EVIOCGSUSPENDBLOCK: + return put_user(client->use_wake_lock, ip); + + case EVIOCSSUSPENDBLOCK: + if (p) + return evdev_enable_suspend_block(evdev, client); + else + return evdev_disable_suspend_block(evdev, client); } size = _IOC_SIZE(cmd); diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index 24593061fea5..9a49af0b6d6e 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -156,6 +156,9 @@ struct input_keymap_entry { /* HACK: disable conflicting EVIOCREVOKE until Android userspace stops using EVIOCSSUSPENDBLOCK */ /*#define EVIOCREVOKE _IOW('E', 0x91, int)*/ /* Revoke device access */ +#define EVIOCGSUSPENDBLOCK _IOR('E', 0x91, int) /* get suspend block enable */ +#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) /* set suspend block enable */ + #define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ /* From c248d947237376e8afbb6c02e8ebb097f3d40a49 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Wed, 15 Oct 2008 15:34:49 -0400 Subject: [PATCH 025/358] Add android_aid.h Add , our mapping of AID defines to gid numbers. Change-Id: I3a02eb2b5c7e336e3de0cb45d8e04ec82f7281b4 Signed-off-by: Robert Love --- include/linux/android_aid.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 include/linux/android_aid.h diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h new file mode 100644 index 000000000000..dc66530e5fc7 --- /dev/null +++ b/include/linux/android_aid.h @@ -0,0 +1,25 @@ +/* include/linux/android_aid.h + * + * Copyright (C) 2008 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_ANDROID_AID_H +#define _LINUX_ANDROID_AID_H + +/* AIDs that the kernel treats differently */ +#define AID_OBSOLETE_000 KGIDT_INIT(3001) /* was NET_BT_ADMIN */ +#define AID_OBSOLETE_001 KGIDT_INIT(3002) /* was NET_BT */ +#define AID_INET KGIDT_INIT(3003) +#define AID_NET_RAW KGIDT_INIT(3004) + +#endif From b86a4974e5b099be3e476931e361d526601061c1 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 15 Dec 2011 18:51:04 -0800 Subject: [PATCH 026/358] ashmem: Add shmem_set_file to mm/shmem.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NOT FOR STAGING This patch re-adds the original shmem_set_file to mm/shmem.c and converts ashmem.c back to using it. CC: Brian Swetland CC: Colin Cross CC: Arve Hjønnevåg CC: Dima Zavin CC: Robert Love CC: Greg KH Signed-off-by: John Stultz --- drivers/staging/android/ashmem.c | 20 ++++++-------------- include/linux/mm.h | 1 + mm/shmem.c | 13 +++++++++---- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 713a97226787..5a26e7c5ab5e 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -396,22 +396,14 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) } get_file(asma->file); - /* - * XXX - Reworked to use shmem_zero_setup() instead of - * shmem_set_file while we're in staging. -jstultz - */ - if (vma->vm_flags & VM_SHARED) { - ret = shmem_zero_setup(vma); - if (ret) { - fput(asma->file); - goto out; - } + if (vma->vm_flags & VM_SHARED) + shmem_set_file(vma, asma->file); + else { + if (vma->vm_file) + fput(vma->vm_file); + vma->vm_file = asma->file; } - if (vma->vm_file) - fput(vma->vm_file); - vma->vm_file = asma->file; - out: mutex_unlock(&ashmem_mutex); return ret; diff --git a/include/linux/mm.h b/include/linux/mm.h index c1b7414c7bef..dc25be363fff 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1040,6 +1040,7 @@ extern void pagefault_out_of_memory(void); extern void show_free_areas(unsigned int flags); extern bool skip_free_areas_node(unsigned int flags, int nid); +void shmem_set_file(struct vm_area_struct *vma, struct file *file); int shmem_zero_setup(struct vm_area_struct *); extern int can_do_mlock(void); diff --git a/mm/shmem.c b/mm/shmem.c index 1f18c9d0d93e..9d0702c50118 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2990,6 +2990,14 @@ struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags } EXPORT_SYMBOL_GPL(shmem_file_setup); +void shmem_set_file(struct vm_area_struct *vma, struct file *file) +{ + if (vma->vm_file) + fput(vma->vm_file); + vma->vm_file = file; + vma->vm_ops = &shmem_vm_ops; +} + /** * shmem_zero_setup - setup a shared anonymous mapping * @vma: the vma to be mmapped is prepared by do_mmap_pgoff @@ -3003,10 +3011,7 @@ int shmem_zero_setup(struct vm_area_struct *vma) if (IS_ERR(file)) return PTR_ERR(file); - if (vma->vm_file) - fput(vma->vm_file); - vma->vm_file = file; - vma->vm_ops = &shmem_vm_ops; + shmem_set_file(vma, file); return 0; } From 819819e11d3c536e7dd1e17be0d86e650bee2be4 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Wed, 15 Oct 2008 15:35:44 -0400 Subject: [PATCH 027/358] Paranoid network. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CONFIG_ANDROID_PARANOID_NETWORK, require specific uids/gids to instantiate network sockets. Signed-off-by: Robert Love paranoid networking: Use in_egroup_p() to check group membership The previous group_search() caused trouble for partners with module builds. in_egroup_p() is also cleaner. Signed-off-by: Nick Pelly Fix 2.6.29 build. Signed-off-by: Arve Hjønnevåg net: Fix compilation of the IPv6 module Fix compilation of the IPv6 module -- current->euid does not exist anymore, current_euid() is what needs to be used. Signed-off-by: Steinar H. Gunderson net: bluetooth: Remove the AID_NET_BT* gid numbers Removed bluetooth checks for AID_NET_BT and AID_NET_BT_ADMIN which are not useful anymore. This is in preparation for getting rid of all the AID_* gids. Signed-off-by: JP Abgrall --- net/Kconfig | 6 ++++++ net/bluetooth/af_bluetooth.c | 29 +++++++++++++++++++++++++++++ net/ipv4/af_inet.c | 31 ++++++++++++++++++++++++++++++- net/ipv6/af_inet6.c | 32 +++++++++++++++++++++++++++++++- 4 files changed, 96 insertions(+), 2 deletions(-) diff --git a/net/Kconfig b/net/Kconfig index e411046a62e3..7adb9da06827 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -82,6 +82,12 @@ source "net/netlabel/Kconfig" endif # if INET +config ANDROID_PARANOID_NETWORK + bool "Only allow certain groups to create sockets" + default y + help + none + config NETWORK_SECMARK bool "Security Marking" help diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 0c5866bb49b6..ca8601901535 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -104,11 +104,40 @@ void bt_sock_unregister(int proto) } EXPORT_SYMBOL(bt_sock_unregister); +#ifdef CONFIG_PARANOID_NETWORK +static inline int current_has_bt_admin(void) +{ + return !current_euid(); +} + +static inline int current_has_bt(void) +{ + return current_has_bt_admin(); +} +# else +static inline int current_has_bt_admin(void) +{ + return 1; +} + +static inline int current_has_bt(void) +{ + return 1; +} +#endif + static int bt_sock_create(struct net *net, struct socket *sock, int proto, int kern) { int err; + if (proto == BTPROTO_RFCOMM || proto == BTPROTO_SCO || + proto == BTPROTO_L2CAP) { + if (!current_has_bt()) + return -EPERM; + } else if (!current_has_bt_admin()) + return -EPERM; + if (net != &init_net) return -EAFNOSUPPORT; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 19ab78aca547..66353f637bfe 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -119,6 +119,9 @@ #include #endif +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif /* The inetsw table contains everything that inet_create needs to * build a new socket. @@ -242,6 +245,29 @@ out: } EXPORT_SYMBOL(inet_listen); +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static inline int current_has_network(void) +{ + return (!current_euid() || in_egroup_p(AID_INET) || + in_egroup_p(AID_NET_RAW)); +} +static inline int current_has_cap(struct net *net, int cap) +{ + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 1; + return ns_capable(net->user_ns, cap); +} +# else +static inline int current_has_network(void) +{ + return 1; +} +static inline int current_has_cap(struct net *net, int cap) +{ + return ns_capable(net->user_ns, cap); +} +#endif + /* * Create an inet socket. */ @@ -258,6 +284,9 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, int try_loading_module = 0; int err; + if (!current_has_network()) + return -EACCES; + sock->state = SS_UNCONNECTED; /* Look for the requested type/protocol pair. */ @@ -307,7 +336,7 @@ lookup_protocol: err = -EPERM; if (sock->type == SOCK_RAW && !kern && - !ns_capable(net->user_ns, CAP_NET_RAW)) + !current_has_cap(net, CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index d935889f1008..96a13b5d1d60 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -64,6 +64,10 @@ #include #include +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif + MODULE_AUTHOR("Cast of dozens"); MODULE_DESCRIPTION("IPv6 protocol stack for Linux"); MODULE_LICENSE("GPL"); @@ -97,6 +101,29 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) return (struct ipv6_pinfo *)(((u8 *)sk) + offset); } +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static inline int current_has_network(void) +{ + return (!current_euid() || in_egroup_p(AID_INET) || + in_egroup_p(AID_NET_RAW)); +} +static inline int current_has_cap(struct net *net, int cap) +{ + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 1; + return ns_capable(net->user_ns, cap); +} +# else +static inline int current_has_network(void) +{ + return 1; +} +static inline int current_has_cap(struct net *net, int cap) +{ + return ns_capable(net->user_ns, cap); +} +#endif + static int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) { @@ -110,6 +137,9 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, int try_loading_module = 0; int err; + if (!current_has_network()) + return -EACCES; + /* Look for the requested type/protocol pair. */ lookup_protocol: err = -ESOCKTNOSUPPORT; @@ -157,7 +187,7 @@ lookup_protocol: err = -EPERM; if (sock->type == SOCK_RAW && !kern && - !ns_capable(net->user_ns, CAP_NET_RAW)) + !current_has_cap(net, CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; From d741a9dcd343e7a71ac973be298bb7df70c5dca2 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Mon, 12 May 2008 17:08:29 -0400 Subject: [PATCH 028/358] net: socket ioctl to reset connections matching local address Introduce a new socket ioctl, SIOCKILLADDR, that nukes all sockets bound to the same local address. This is useful in situations with dynamic IPs, to kill stuck connections. Signed-off-by: Brian Swetland net: fix tcp_v4_nuke_addr Signed-off-by: Dima Zavin net: ipv4: Fix a spinlock recursion bug in tcp_v4_nuke. We can't hold the lock while calling to tcp_done(), so we drop it before calling. We then have to start at the top of the chain again. Signed-off-by: Dima Zavin net: ipv4: Fix race in tcp_v4_nuke_addr(). To fix a recursive deadlock in 2.6.29, we stopped holding the hash table lock across tcp_done() calls. This fixed the deadlock, but introduced a race where the socket could die or change state. Fix: Before unlocking the hash table, we grab a reference to the socket. We can then unlock the hash table without risk of the socket going away. We then lock the socket, which is safe because it is pinned. We can then call tcp_done() without recursive deadlock and without race. Upon return, we unlock the socket and then unpin it, killing it. Change-Id: Idcdae072b48238b01bdbc8823b60310f1976e045 Signed-off-by: Robert Love Acked-by: Dima Zavin ipv4: disable bottom halves around call to tcp_done(). Signed-off-by: Robert Love Signed-off-by: Colin Cross ipv4: Move sk_error_report inside bh_lock_sock in tcp_v4_nuke_addr When sk_error_report is called, it wakes up the user-space thread, which then calls tcp_close. When the tcp_close is interrupted by the tcp_v4_nuke_addr ioctl thread running tcp_done, it leaks 392 bytes and triggers a WARN_ON. This patch moves the call to sk_error_report inside the bh_lock_sock, which matches the locking used in tcp_v4_err. Signed-off-by: Colin Cross --- include/net/tcp.h | 2 + include/uapi/linux/sockios.h | 1 + net/ipv4/af_inet.c | 1 + net/ipv4/devinet.c | 8 ++- net/ipv4/tcp.c | 107 +++++++++++++++++++++++++++++++++++ net/ipv6/af_inet6.c | 17 ++++++ 6 files changed, 135 insertions(+), 1 deletion(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 8c4dd63134d4..489ec8ca4bc6 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1557,6 +1557,8 @@ static inline bool tcp_stream_memory_free(const struct sock *sk) return notsent_bytes < tcp_notsent_lowat(tp); } +extern int tcp_nuke_addr(struct net *net, struct sockaddr *addr); + #ifdef CONFIG_PROC_FS int tcp4_proc_init(void); void tcp4_proc_exit(void); diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h index e888b1aed69f..623e9aab645e 100644 --- a/include/uapi/linux/sockios.h +++ b/include/uapi/linux/sockios.h @@ -65,6 +65,7 @@ #define SIOCDIFADDR 0x8936 /* delete PA address */ #define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */ #define SIOCGIFCOUNT 0x8938 /* get number of devices */ +#define SIOCKILLADDR 0x8939 /* kill sockets with this local addr */ #define SIOCGIFBR 0x8940 /* Bridging support */ #define SIOCSIFBR 0x8941 /* Set bridging options */ diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 66353f637bfe..ebce2373f75e 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -904,6 +904,7 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCSIFPFLAGS: case SIOCGIFPFLAGS: case SIOCSIFFLAGS: + case SIOCKILLADDR: err = devinet_ioctl(net, cmd, (void __user *)arg); break; default: diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index bdbf68bb2e2d..8f12b4dedb88 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -59,6 +59,7 @@ #include #include +#include #include #include #include @@ -929,6 +930,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) case SIOCSIFBRDADDR: /* Set the broadcast address */ case SIOCSIFDSTADDR: /* Set the destination address */ case SIOCSIFNETMASK: /* Set the netmask for the interface */ + case SIOCKILLADDR: /* Nuke all sockets on this address */ ret = -EPERM; if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) goto out; @@ -980,7 +982,8 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) } ret = -EADDRNOTAVAIL; - if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) + if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS + && cmd != SIOCKILLADDR) goto done; switch (cmd) { @@ -1107,6 +1110,9 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) inet_insert_ifa(ifa); } break; + case SIOCKILLADDR: /* Nuke all connections on this address */ + ret = tcp_nuke_addr(net, (struct sockaddr *) sin); + break; } done: rtnl_unlock(); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 97c8f5620c43..ef4b95a3c74c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -274,6 +274,9 @@ #include #include #include +#include +#include +#include #include #include @@ -3229,3 +3232,107 @@ void __init tcp_init(void) tcp_tasklet_init(); } + +static int tcp_is_local(struct net *net, __be32 addr) { + struct rtable *rt; + struct flowi4 fl4 = { .daddr = addr }; + rt = ip_route_output_key(net, &fl4); + if (IS_ERR_OR_NULL(rt)) + return 0; + return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK); +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +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); +} +#endif + +/* + * tcp_nuke_addr - destroy all sockets on the given local address + * if local address is the unspecified address (0.0.0.0 or ::), destroy all + * sockets with local addresses that are not configured. + */ +int tcp_nuke_addr(struct net *net, struct sockaddr *addr) +{ + int family = addr->sa_family; + unsigned int bucket; + + struct in_addr *in; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct in6_addr *in6; +#endif + if (family == AF_INET) { + in = &((struct sockaddr_in *)addr)->sin_addr; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + } else if (family == AF_INET6) { + in6 = &((struct sockaddr_in6 *)addr)->sin6_addr; +#endif + } else { + return -EAFNOSUPPORT; + } + + for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) { + struct hlist_nulls_node *node; + struct sock *sk; + spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket); + +restart: + spin_lock_bh(lock); + sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { + struct inet_sock *inet = inet_sk(sk); + + if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) + continue; + if (sock_flag(sk, SOCK_DEAD)) + continue; + + if (family == AF_INET) { + __be32 s4 = inet->inet_rcv_saddr; + if (s4 == LOOPBACK4_IPV6) + continue; + + if (in->s_addr != s4 && + !(in->s_addr == INADDR_ANY && + !tcp_is_local(net, s4))) + continue; + } + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + if (family == AF_INET6) { + struct in6_addr *s6; + if (!inet->pinet6) + continue; + + s6 = &inet->pinet6->rcv_saddr; + if (ipv6_addr_type(s6) == IPV6_ADDR_MAPPED) + continue; + + if (!ipv6_addr_equal(in6, s6) && + !(ipv6_addr_equal(in6, &in6addr_any) && + !tcp_is_local6(net, s6))) + continue; + } +#endif + + sock_hold(sk); + spin_unlock_bh(lock); + + local_bh_disable(); + bh_lock_sock(sk); + sk->sk_err = ETIMEDOUT; + sk->sk_error_report(sk); + + tcp_done(sk); + bh_unlock_sock(sk); + local_bh_enable(); + sock_put(sk); + + goto restart; + } + spin_unlock_bh(lock); + } + + return 0; +} diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 96a13b5d1d60..b256c72f8163 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -504,6 +504,21 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, } EXPORT_SYMBOL(inet6_getname); +int inet6_killaddr_ioctl(struct net *net, void __user *arg) { + struct in6_ifreq ireq; + struct sockaddr_in6 sin6; + + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + + if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) + return -EFAULT; + + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = ireq.ifr6_addr; + return tcp_nuke_addr(net, (struct sockaddr *) &sin6); +} + int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; @@ -527,6 +542,8 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return addrconf_del_ifaddr(net, (void __user *) arg); case SIOCSIFDSTADDR: return addrconf_set_dstaddr(net, (void __user *) arg); + case SIOCKILLADDR: + return inet6_killaddr_ioctl(net, (void __user *) arg); default: if (!sk->sk_prot->ioctl) return -ENOIOCTLCMD; From e75bc32498f4c57034bb850733eea0fd0aeaee14 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Thu, 31 Jul 2008 11:12:44 -0400 Subject: [PATCH 029/358] sysfs_net_ipv4: Add sysfs-based knobs for controlling TCP window size Add a family of knobs to /sys/kernel/ipv4 for controlling the TCP window size: tcp_wmem_min tcp_wmem_def tcp_wmem_max tcp_rmem_min tcp_rmem_def tcp_rmem_max This six values mirror the sysctl knobs in /proc/sys/net/ipv4/tcp_wmem and /proc/sys/net/ipv4/tcp_rmem. Sysfs, unlike sysctl, allows us to set and manage the files' permissions and owners. Signed-off-by: Robert Love --- net/ipv4/Makefile | 1 + net/ipv4/sysfs_net_ipv4.c | 88 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 net/ipv4/sysfs_net_ipv4.c diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index f8c49ce5b283..ae215f0ebe28 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -15,6 +15,7 @@ obj-y := route.o inetpeer.o protocol.o \ obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o +obj-$(CONFIG_SYSFS) += sysfs_net_ipv4.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o obj-$(CONFIG_IP_MROUTE) += ipmr.o diff --git a/net/ipv4/sysfs_net_ipv4.c b/net/ipv4/sysfs_net_ipv4.c new file mode 100644 index 000000000000..0cbbf10026a6 --- /dev/null +++ b/net/ipv4/sysfs_net_ipv4.c @@ -0,0 +1,88 @@ +/* + * net/ipv4/sysfs_net_ipv4.c + * + * sysfs-based networking knobs (so we can, unlike with sysctl, control perms) + * + * Copyright (C) 2008 Google, Inc. + * + * Robert Love + * + * 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 +#include +#include +#include +#include + +#define CREATE_IPV4_FILE(_name, _var) \ +static ssize_t _name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%d\n", _var); \ +} \ +static ssize_t _name##_store(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + int val, ret; \ + ret = sscanf(buf, "%d", &val); \ + if (ret != 1) \ + return -EINVAL; \ + if (val < 0) \ + return -EINVAL; \ + _var = val; \ + return count; \ +} \ +static struct kobj_attribute _name##_attr = \ + __ATTR(_name, 0644, _name##_show, _name##_store) + +CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]); +CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]); +CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]); + +CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]); +CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]); +CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]); + +static struct attribute *ipv4_attrs[] = { + &tcp_wmem_min_attr.attr, + &tcp_wmem_def_attr.attr, + &tcp_wmem_max_attr.attr, + &tcp_rmem_min_attr.attr, + &tcp_rmem_def_attr.attr, + &tcp_rmem_max_attr.attr, + NULL +}; + +static struct attribute_group ipv4_attr_group = { + .attrs = ipv4_attrs, +}; + +static __init int sysfs_ipv4_init(void) +{ + struct kobject *ipv4_kobject; + int ret; + + ipv4_kobject = kobject_create_and_add("ipv4", kernel_kobj); + if (!ipv4_kobject) + return -ENOMEM; + + ret = sysfs_create_group(ipv4_kobject, &ipv4_attr_group); + if (ret) { + kobject_put(ipv4_kobject); + return ret; + } + + return 0; +} + +subsys_initcall(sysfs_ipv4_init); From 33642d4e8f2832b49fa6f5cc823c6be2010973a9 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Wed, 7 Jan 2009 11:40:42 -0800 Subject: [PATCH 030/358] misc: uidstat: Adding uid stat driver to collect network statistics. Signed-off-by: Mike Chan --- drivers/misc/Kconfig | 4 + drivers/misc/Makefile | 1 + drivers/misc/uid_stat.c | 153 +++++++++++++++++++++++++++++++++++++++ include/linux/uid_stat.h | 29 ++++++++ net/ipv4/tcp.c | 10 +++ 5 files changed, 197 insertions(+) create mode 100644 drivers/misc/uid_stat.c create mode 100644 include/linux/uid_stat.h diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 6cb388e8fb7d..5b49591a2114 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -411,6 +411,10 @@ config TI_DAC7512 This driver can also be built as a module. If so, the module will be called ti_dac7512. +config UID_STAT + bool "UID based statistics tracking exported to /proc/uid_stat" + default n + config VMWARE_BALLOON tristate "VMware Balloon Driver" depends on X86 && HYPERVISOR_GUEST diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 99b9424ce31d..2329073c5153 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_ISL29020) += isl29020.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o +obj-$(CONFIG_UID_STAT) += uid_stat.o obj-$(CONFIG_C2PORT) += c2port/ obj-$(CONFIG_HMC6352) += hmc6352.o obj-y += eeprom/ diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c new file mode 100644 index 000000000000..e6760b56083c --- /dev/null +++ b/drivers/misc/uid_stat.c @@ -0,0 +1,153 @@ +/* drivers/misc/uid_stat.c + * + * Copyright (C) 2008 - 2009 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(uid_lock); +static LIST_HEAD(uid_list); +static struct proc_dir_entry *parent; + +struct uid_stat { + struct list_head link; + uid_t uid; + atomic_t tcp_rcv; + atomic_t tcp_snd; +}; + +static struct uid_stat *find_uid_stat(uid_t uid) { + unsigned long flags; + struct uid_stat *entry; + + spin_lock_irqsave(&uid_lock, flags); + list_for_each_entry(entry, &uid_list, link) { + if (entry->uid == uid) { + spin_unlock_irqrestore(&uid_lock, flags); + return entry; + } + } + spin_unlock_irqrestore(&uid_lock, flags); + return NULL; +} + +static int tcp_snd_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + unsigned int bytes; + char *p = page; + struct uid_stat *uid_entry = (struct uid_stat *) data; + if (!data) + return 0; + + bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN); + p += sprintf(p, "%u\n", bytes); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int tcp_rcv_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + unsigned int bytes; + char *p = page; + struct uid_stat *uid_entry = (struct uid_stat *) data; + if (!data) + return 0; + + bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN); + p += sprintf(p, "%u\n", bytes); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +/* Create a new entry for tracking the specified uid. */ +static struct uid_stat *create_stat(uid_t uid) { + unsigned long flags; + char uid_s[32]; + struct uid_stat *new_uid; + struct proc_dir_entry *entry; + + /* Create the uid stat struct and append it to the list. */ + if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL) + return NULL; + + new_uid->uid = uid; + /* Counters start at INT_MIN, so we can track 4GB of network traffic. */ + atomic_set(&new_uid->tcp_rcv, INT_MIN); + atomic_set(&new_uid->tcp_snd, INT_MIN); + + spin_lock_irqsave(&uid_lock, flags); + list_add_tail(&new_uid->link, &uid_list); + spin_unlock_irqrestore(&uid_lock, flags); + + sprintf(uid_s, "%d", uid); + entry = proc_mkdir(uid_s, parent); + + /* Keep reference to uid_stat so we know what uid to read stats from. */ + create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc, + (void *) new_uid); + + create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc, + (void *) new_uid); + + return new_uid; +} + +int uid_stat_tcp_snd(uid_t uid, int size) { + struct uid_stat *entry; + if ((entry = find_uid_stat(uid)) == NULL && + ((entry = create_stat(uid)) == NULL)) { + return -1; + } + atomic_add(size, &entry->tcp_snd); + return 0; +} + +int uid_stat_tcp_rcv(uid_t uid, int size) { + struct uid_stat *entry; + if ((entry = find_uid_stat(uid)) == NULL && + ((entry = create_stat(uid)) == NULL)) { + return -1; + } + atomic_add(size, &entry->tcp_rcv); + return 0; +} + +static int __init uid_stat_init(void) +{ + parent = proc_mkdir("uid_stat", NULL); + if (!parent) { + pr_err("uid_stat: failed to create proc entry\n"); + return -1; + } + return 0; +} + +__initcall(uid_stat_init); diff --git a/include/linux/uid_stat.h b/include/linux/uid_stat.h new file mode 100644 index 000000000000..6bd6c4e52d17 --- /dev/null +++ b/include/linux/uid_stat.h @@ -0,0 +1,29 @@ +/* include/linux/uid_stat.h + * + * Copyright (C) 2008-2009 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 __uid_stat_h +#define __uid_stat_h + +/* Contains definitions for resource tracking per uid. */ + +#ifdef CONFIG_UID_STAT +int uid_stat_tcp_snd(uid_t uid, int size); +int uid_stat_tcp_rcv(uid_t uid, int size); +#else +#define uid_stat_tcp_snd(uid, size) do {} while (0); +#define uid_stat_tcp_rcv(uid, size) do {} while (0); +#endif + +#endif /* _LINUX_UID_STAT_H */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ef4b95a3c74c..300b93e98560 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -268,6 +268,7 @@ #include #include #include +#include #include #include @@ -1286,6 +1287,9 @@ out: if (copied) tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); release_sock(sk); + + if (copied + copied_syn) + uid_stat_tcp_snd(current_uid(), copied + copied_syn); return copied + copied_syn; do_fault: @@ -1590,6 +1594,7 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, if (copied > 0) { tcp_recv_skb(sk, seq, &offset); tcp_cleanup_rbuf(sk, copied); + uid_stat_tcp_rcv(current_uid(), copied); } return copied; } @@ -1998,6 +2003,9 @@ skip_copy: tcp_cleanup_rbuf(sk, copied); release_sock(sk); + + if (copied > 0) + uid_stat_tcp_rcv(current_uid(), copied); return copied; out: @@ -2006,6 +2014,8 @@ out: recv_urg: err = tcp_recv_urg(sk, msg, len, flags); + if (err > 0) + uid_stat_tcp_rcv(current_uid(), err); goto out; recv_sndq: From 38c987824e5f8dadde511da7af1fad756068177c Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Thu, 24 Apr 2008 10:22:26 -0700 Subject: [PATCH 031/358] Grants system server access to /proc//oom_adj for Android applications. Signed-off-by: Brian Swetland --- fs/proc/base.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index b9760628e1fd..70aeca2867d2 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -139,6 +139,12 @@ struct pid_entry { NULL, &proc_single_file_operations, \ { .proc_show = show } ) +/* ANDROID is for special files in /proc. */ +#define ANDROID(NAME, MODE, OTYPE) \ + NOD(NAME, (S_IFREG|(MODE)), \ + &proc_##OTYPE##_inode_operations, \ + &proc_##OTYPE##_operations, {}) + /* * Count the number of hardlinks for the pid_entry table, excluding the . * and .. links. @@ -1000,6 +1006,37 @@ out: return err < 0 ? err : count; } +static int oom_adjust_permission(struct inode *inode, int mask) +{ + kuid_t uid; + struct task_struct *p; + kuid_t system_server_uid = KUIDT_INIT(1000); + + p = get_proc_task(inode); + if(p) { + uid = task_uid(p); + put_task_struct(p); + } + + /* + * System Server (uid == 1000) is granted access to oom_adj of all + * android applications (uid > 10000) as and services (uid >= 1000) + */ + if (p && (uid_eq(current_fsuid(), system_server_uid) && + uid_gt(uid, system_server_uid))) { + if (inode->i_mode >> 6 & mask) { + return 0; + } + } + + /* Fall back to default. */ + return generic_permission(inode, mask); +} + +static const struct inode_operations proc_oom_adj_inode_operations = { + .permission = oom_adjust_permission, +}; + static const struct file_operations proc_oom_adj_operations = { .read = oom_adj_read, .write = oom_adj_write, @@ -2641,7 +2678,7 @@ static const struct pid_entry tgid_base_stuff[] = { REG("cgroup", S_IRUGO, proc_cgroup_operations), #endif INF("oom_score", S_IRUGO, proc_oom_score), - REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adj_operations), + ANDROID("oom_adj", S_IRUGO|S_IWUSR, oom_adj), REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations), #ifdef CONFIG_AUDITSYSCALL REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), From 4bed7c9c3c641990678e674fe59f6162959a0d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 15 Oct 2008 18:23:47 -0700 Subject: [PATCH 032/358] Input: Generic GPIO Input device. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supports keyboard matrixces, direct inputs, direct outputs and axes connected to gpios. Change-Id: I5e921e6e3a1cc169316ee3b665f4cc21b5735114 Signed-off-by: Arve Hjønnevåg Signed-off-by: Nick Pelly --- drivers/input/misc/Kconfig | 5 + drivers/input/misc/Makefile | 1 + drivers/input/misc/gpio_axis.c | 192 ++++++++++++++ drivers/input/misc/gpio_event.c | 249 +++++++++++++++++ drivers/input/misc/gpio_input.c | 376 ++++++++++++++++++++++++++ drivers/input/misc/gpio_matrix.c | 441 +++++++++++++++++++++++++++++++ drivers/input/misc/gpio_output.c | 97 +++++++ include/linux/gpio_event.h | 170 ++++++++++++ 8 files changed, 1531 insertions(+) create mode 100644 drivers/input/misc/gpio_axis.c create mode 100644 drivers/input/misc/gpio_event.c create mode 100644 drivers/input/misc/gpio_input.c create mode 100644 drivers/input/misc/gpio_matrix.c create mode 100644 drivers/input/misc/gpio_output.c create mode 100644 include/linux/gpio_event.h diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7904ab05527a..a0a2965b4be1 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -443,6 +443,11 @@ config INPUT_SGI_BTNS To compile this driver as a module, choose M here: the module will be called sgi_btns. +config INPUT_GPIO + tristate "GPIO driver support" + help + Say Y here if you want to support gpio based keys, wheels etc... + config HP_SDC_RTC tristate "HP SDC Real Time Clock" depends on (GSC || HP300) && SERIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index cda71fc52fb3..a631ce71ace5 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o +obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c new file mode 100644 index 000000000000..0acf4a576f53 --- /dev/null +++ b/drivers/input/misc/gpio_axis.c @@ -0,0 +1,192 @@ +/* drivers/input/misc/gpio_axis.c + * + * Copyright (C) 2007 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 +#include +#include +#include +#include + +struct gpio_axis_state { + struct gpio_event_input_devs *input_devs; + struct gpio_event_axis_info *info; + uint32_t pos; +}; + +uint16_t gpio_axis_4bit_gray_map_table[] = { + [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */ + [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */ + [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */ + [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */ + [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */ + [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */ + [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */ + [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */ +}; +uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_4bit_gray_map_table[in]; +} + +uint16_t gpio_axis_5bit_singletrack_map_table[] = { + [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */ + [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */ + [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */ + [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */ + [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */ + [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */ + [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */ + [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */ + [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */ + [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */ +}; +uint16_t gpio_axis_5bit_singletrack_map( + struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_5bit_singletrack_map_table[in]; +} + +static void gpio_event_update_axis(struct gpio_axis_state *as, int report) +{ + struct gpio_event_axis_info *ai = as->info; + int i; + int change; + uint16_t state = 0; + uint16_t pos; + uint16_t old_pos = as->pos; + for (i = ai->count - 1; i >= 0; i--) + state = (state << 1) | gpio_get_value(ai->gpio[i]); + pos = ai->map(ai, state); + if (ai->flags & GPIOEAF_PRINT_RAW) + pr_info("axis %d-%d raw %x, pos %d -> %d\n", + ai->type, ai->code, state, old_pos, pos); + if (report && pos != old_pos) { + if (ai->type == EV_REL) { + change = (ai->decoded_size + pos - old_pos) % + ai->decoded_size; + if (change > ai->decoded_size / 2) + change -= ai->decoded_size; + if (change == ai->decoded_size / 2) { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d unknown direction, " + "pos %d -> %d\n", ai->type, + ai->code, old_pos, pos); + change = 0; /* no closest direction */ + } + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d change %d\n", + ai->type, ai->code, change); + input_report_rel(as->input_devs->dev[ai->dev], + ai->code, change); + } else { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d now %d\n", + ai->type, ai->code, pos); + input_event(as->input_devs->dev[ai->dev], + ai->type, ai->code, pos); + } + input_sync(as->input_devs->dev[ai->dev]); + } + as->pos = pos; +} + +static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id) +{ + struct gpio_axis_state *as = dev_id; + gpio_event_update_axis(as, 1); + return IRQ_HANDLED; +} + +int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + int irq; + struct gpio_event_axis_info *ai; + struct gpio_axis_state *as; + + ai = container_of(info, struct gpio_event_axis_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND) { + for (i = 0; i < ai->count; i++) + disable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + for (i = 0; i < ai->count; i++) + enable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + *data = as = kmalloc(sizeof(*as), GFP_KERNEL); + if (as == NULL) { + ret = -ENOMEM; + goto err_alloc_axis_state_failed; + } + as->input_devs = input_devs; + as->info = ai; + if (ai->dev >= input_devs->count) { + pr_err("gpio_event_axis: bad device index %d >= %d " + "for %d:%d\n", ai->dev, input_devs->count, + ai->type, ai->code); + ret = -EINVAL; + goto err_bad_device_index; + } + + input_set_capability(input_devs->dev[ai->dev], + ai->type, ai->code); + if (ai->type == EV_ABS) { + input_set_abs_params(input_devs->dev[ai->dev], ai->code, + 0, ai->decoded_size - 1, 0, 0); + } + for (i = 0; i < ai->count; i++) { + ret = gpio_request(ai->gpio[i], "gpio_event_axis"); + if (ret < 0) + goto err_request_gpio_failed; + ret = gpio_direction_input(ai->gpio[i]); + if (ret < 0) + goto err_gpio_direction_input_failed; + ret = irq = gpio_to_irq(ai->gpio[i]); + if (ret < 0) + goto err_get_irq_num_failed; + ret = request_irq(irq, gpio_axis_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "gpio_event_axis", as); + if (ret < 0) + goto err_request_irq_failed; + } + gpio_event_update_axis(as, 0); + return 0; + } + + ret = 0; + as = *data; + for (i = ai->count - 1; i >= 0; i--) { + free_irq(gpio_to_irq(ai->gpio[i]), as); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(ai->gpio[i]); +err_request_gpio_failed: + ; + } +err_bad_device_index: + kfree(as); + *data = NULL; +err_alloc_axis_state_failed: + return ret; +} diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c new file mode 100644 index 000000000000..d4e5b4dfe19f --- /dev/null +++ b/drivers/input/misc/gpio_event.c @@ -0,0 +1,249 @@ +/* drivers/input/misc/gpio_event.c + * + * Copyright (C) 2007 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 +#include +#include +#include +#include +#include +#include + +struct gpio_event { + struct gpio_event_input_devs *input_devs; + const struct gpio_event_platform_data *info; + struct early_suspend early_suspend; + void *state[0]; +}; + +static int gpio_input_event( + struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + int i; + int devnr; + int ret = 0; + int tmp_ret; + struct gpio_event_info **ii; + struct gpio_event *ip = input_get_drvdata(dev); + + for (devnr = 0; devnr < ip->input_devs->count; devnr++) + if (ip->input_devs->dev[devnr] == dev) + break; + if (devnr == ip->input_devs->count) { + pr_err("gpio_input_event: unknown device %p\n", dev); + return -EIO; + } + + for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) { + if ((*ii)->event) { + tmp_ret = (*ii)->event(ip->input_devs, *ii, + &ip->state[i], + devnr, type, code, value); + if (tmp_ret) + ret = tmp_ret; + } + } + return ret; +} + +static int gpio_event_call_all_func(struct gpio_event *ip, int func) +{ + int i; + int ret; + struct gpio_event_info **ii; + + if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) { + ii = ip->info->info; + for (i = 0; i < ip->info->info_count; i++, ii++) { + if ((*ii)->func == NULL) { + ret = -ENODEV; + pr_err("gpio_event_probe: Incomplete pdata, " + "no function\n"); + goto err_no_func; + } + if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend) + continue; + ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i], + func); + if (ret) { + pr_err("gpio_event_probe: function failed\n"); + goto err_func_failed; + } + } + return 0; + } + + ret = 0; + i = ip->info->info_count; + ii = ip->info->info + i; + while (i > 0) { + i--; + ii--; + if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend) + continue; + (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1); +err_func_failed: +err_no_func: + ; + } + return ret; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +void gpio_event_suspend(struct early_suspend *h) +{ + struct gpio_event *ip; + ip = container_of(h, struct gpio_event, early_suspend); + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); + ip->info->power(ip->info, 0); +} + +void gpio_event_resume(struct early_suspend *h) +{ + struct gpio_event *ip; + ip = container_of(h, struct gpio_event, early_suspend); + ip->info->power(ip->info, 1); + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); +} +#endif + +static int gpio_event_probe(struct platform_device *pdev) +{ + int err; + struct gpio_event *ip; + struct gpio_event_platform_data *event_info; + int dev_count = 1; + int i; + int registered = 0; + + event_info = pdev->dev.platform_data; + if (event_info == NULL) { + pr_err("gpio_event_probe: No pdata\n"); + return -ENODEV; + } + if ((!event_info->name && !event_info->names[0]) || + !event_info->info || !event_info->info_count) { + pr_err("gpio_event_probe: Incomplete pdata\n"); + return -ENODEV; + } + if (!event_info->name) + while (event_info->names[dev_count]) + dev_count++; + ip = kzalloc(sizeof(*ip) + + sizeof(ip->state[0]) * event_info->info_count + + sizeof(*ip->input_devs) + + sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL); + if (ip == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + ip->input_devs = (void*)&ip->state[event_info->info_count]; + platform_set_drvdata(pdev, ip); + + for (i = 0; i < dev_count; i++) { + struct input_dev *input_dev = input_allocate_device(); + if (input_dev == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: " + "Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + input_set_drvdata(input_dev, ip); + input_dev->name = event_info->name ? + event_info->name : event_info->names[i]; + input_dev->event = gpio_input_event; + ip->input_devs->dev[i] = input_dev; + } + ip->input_devs->count = dev_count; + ip->info = event_info; + if (event_info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ip->early_suspend.suspend = gpio_event_suspend; + ip->early_suspend.resume = gpio_event_resume; + register_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 1); + } + + err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); + if (err) + goto err_call_all_func_failed; + + for (i = 0; i < dev_count; i++) { + err = input_register_device(ip->input_devs->dev[i]); + if (err) { + pr_err("gpio_event_probe: Unable to register %s " + "input device\n", ip->input_devs->dev[i]->name); + goto err_input_register_device_failed; + } + registered++; + } + + return 0; + +err_input_register_device_failed: + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); +err_call_all_func_failed: + if (event_info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 0); + } + for (i = 0; i < registered; i++) + input_unregister_device(ip->input_devs->dev[i]); + for (i = dev_count - 1; i >= registered; i--) { + input_free_device(ip->input_devs->dev[i]); +err_input_dev_alloc_failed: + ; + } + kfree(ip); +err_kp_alloc_failed: + return err; +} + +static int gpio_event_remove(struct platform_device *pdev) +{ + struct gpio_event *ip = platform_get_drvdata(pdev); + int i; + + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); + if (ip->info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 0); + } + for (i = 0; i < ip->input_devs->count; i++) + input_unregister_device(ip->input_devs->dev[i]); + kfree(ip); + return 0; +} + +static struct platform_driver gpio_event_driver = { + .probe = gpio_event_probe, + .remove = gpio_event_remove, + .driver = { + .name = GPIO_EVENT_DEV_NAME, + }, +}; + +module_platform_driver(gpio_event_driver); + +MODULE_DESCRIPTION("GPIO Event Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c new file mode 100644 index 000000000000..6a0c31510968 --- /dev/null +++ b/drivers/input/misc/gpio_input.c @@ -0,0 +1,376 @@ +/* drivers/input/misc/gpio_input.c + * + * Copyright (C) 2007 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 +#include +#include +#include +#include +#include +#include +#include + +enum { + DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ + DEBOUNCE_PRESSED = BIT(1), + DEBOUNCE_NOTPRESSED = BIT(2), + DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */ + DEBOUNCE_POLL = BIT(4), /* Stable polling state */ + + DEBOUNCE_UNKNOWN = + DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED, +}; + +struct gpio_key_state { + struct gpio_input_state *ds; + uint8_t debounce; +}; + +struct gpio_input_state { + struct gpio_event_input_devs *input_devs; + const struct gpio_event_input_info *info; + struct hrtimer timer; + int use_irq; + int debounce_count; + spinlock_t irq_lock; + struct wake_lock wake_lock; + struct gpio_key_state key_state[0]; +}; + +static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) +{ + int i; + int pressed; + struct gpio_input_state *ds = + container_of(timer, struct gpio_input_state, timer); + unsigned gpio_flags = ds->info->flags; + unsigned npolarity; + int nkeys = ds->info->keymap_size; + const struct gpio_event_direct_entry *key_entry; + struct gpio_key_state *key_state; + unsigned long irqflags; + uint8_t debounce; + bool sync_needed; + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); +#endif + key_entry = ds->info->keymap; + key_state = ds->key_state; + sync_needed = false; + spin_lock_irqsave(&ds->irq_lock, irqflags); + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + debounce = key_state->debounce; + if (debounce & DEBOUNCE_WAIT_IRQ) + continue; + if (key_state->debounce & DEBOUNCE_UNSTABLE) { + debounce = key_state->debounce = DEBOUNCE_UNKNOWN; + enable_irq(gpio_to_irq(key_entry->gpio)); + if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) continue debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH); + pressed = gpio_get_value(key_entry->gpio) ^ npolarity; + if (debounce & DEBOUNCE_POLL) { + if (pressed == !(debounce & DEBOUNCE_PRESSED)) { + ds->debounce_count++; + key_state->debounce = DEBOUNCE_UNKNOWN; + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-" + "%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + continue; + } + if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 1\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_PRESSED; + continue; + } + if (!pressed && (debounce & DEBOUNCE_PRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 0\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_NOTPRESSED; + continue; + } + /* key is stable */ + ds->debounce_count--; + if (ds->use_irq) + key_state->debounce |= DEBOUNCE_WAIT_IRQ; + else + key_state->debounce |= DEBOUNCE_POLL; + if (gpio_flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) " + "changed to %d\n", ds->info->type, + key_entry->code, i, key_entry->gpio, pressed); + input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, + key_entry->code, pressed); + sync_needed = true; + } + if (sync_needed) { + for (i = 0; i < ds->input_devs->count; i++) + input_sync(ds->input_devs->dev[i]); + } + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); + } +#endif + + if (ds->debounce_count) + hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL); + else if (!ds->use_irq) + hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); + else + wake_unlock(&ds->wake_lock); + + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) +{ + struct gpio_key_state *ks = dev_id; + struct gpio_input_state *ds = ks->ds; + int keymap_index = ks - ds->key_state; + const struct gpio_event_direct_entry *key_entry; + unsigned long irqflags; + int pressed; + + if (!ds->use_irq) + return IRQ_HANDLED; + + key_entry = &ds->info->keymap[keymap_index]; + + if (ds->info->debounce_time.tv64) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ks->debounce & DEBOUNCE_WAIT_IRQ) { + ks->debounce = DEBOUNCE_UNKNOWN; + if (ds->debounce_count++ == 0) { + wake_lock(&ds->wake_lock); + hrtimer_start( + &ds->timer, ds->info->debounce_time, + HRTIMER_MODE_REL); + } + if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_event_input_irq_handler: " + "key %x-%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + keymap_index, key_entry->gpio); + } else { + disable_irq_nosync(irq); + ks->debounce = DEBOUNCE_UNSTABLE; + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + } else { + pressed = gpio_get_value(key_entry->gpio) ^ + !(ds->info->flags & GPIOEDF_ACTIVE_HIGH); + if (ds->info->flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_event_input_irq_handler: key %x-%x, %d " + "(%d) changed to %d\n", + ds->info->type, key_entry->code, keymap_index, + key_entry->gpio, pressed); + input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, + key_entry->code, pressed); + input_sync(ds->input_devs->dev[key_entry->dev]); + } + return IRQ_HANDLED; +} + +static int gpio_event_input_request_irqs(struct gpio_input_state *ds) +{ + int i; + int err; + unsigned int irq; + unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + + for (i = 0; i < ds->info->keymap_size; i++) { + err = irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_event_input_irq_handler, + req_flags, "gpio_keys", &ds->key_state[i]); + if (err) { + pr_err("gpio_event_input_request_irqs: request_irq " + "failed for input %d, irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_request_irq_failed; + } + if (ds->info->info.no_suspend) { + err = enable_irq_wake(irq); + if (err) { + pr_err("gpio_event_input_request_irqs: " + "enable_irq_wake failed for input %d, " + "irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_enable_irq_wake_failed; + } + } + } + return 0; + + for (i = ds->info->keymap_size - 1; i >= 0; i--) { + irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (ds->info->info.no_suspend) + disable_irq_wake(irq); +err_enable_irq_wake_failed: + free_irq(irq, &ds->key_state[i]); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_input_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + unsigned long irqflags; + struct gpio_event_input_info *di; + struct gpio_input_state *ds = *data; + + di = container_of(info, struct gpio_event_input_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND) { + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + disable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_cancel(&ds->timer); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + enable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (ktime_to_ns(di->poll_time) <= 0) + di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC); + + *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) * + di->keymap_size, GFP_KERNEL); + if (ds == NULL) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate private data\n"); + goto err_ds_alloc_failed; + } + ds->debounce_count = di->keymap_size; + ds->input_devs = input_devs; + ds->info = di; + wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input"); + spin_lock_init(&ds->irq_lock); + + for (i = 0; i < di->keymap_size; i++) { + int dev = di->keymap[i].dev; + if (dev >= input_devs->count) { + pr_err("gpio_event_input_func: bad device " + "index %d >= %d for key code %d\n", + dev, input_devs->count, + di->keymap[i].code); + ret = -EINVAL; + goto err_bad_keymap; + } + input_set_capability(input_devs->dev[dev], di->type, + di->keymap[i].code); + ds->key_state[i].ds = ds; + ds->key_state[i].debounce = DEBOUNCE_UNKNOWN; + } + + for (i = 0; i < di->keymap_size; i++) { + ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in"); + if (ret) { + pr_err("gpio_event_input_func: gpio_request " + "failed for %d\n", di->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_input(di->keymap[i].gpio); + if (ret) { + pr_err("gpio_event_input_func: " + "gpio_direction_input failed for %d\n", + di->keymap[i].gpio); + goto err_gpio_configure_failed; + } + } + + ret = gpio_event_input_request_irqs(ds); + + spin_lock_irqsave(&ds->irq_lock, irqflags); + ds->use_irq = ret == 0; + + pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s " + "mode\n", input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : "", + ret == 0 ? "interrupt" : "polling"); + + hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ds->timer.function = gpio_event_input_timer_func; + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + ret = 0; + spin_lock_irqsave(&ds->irq_lock, irqflags); + hrtimer_cancel(&ds->timer); + if (ds->use_irq) { + for (i = di->keymap_size - 1; i >= 0; i--) { + int irq = gpio_to_irq(di->keymap[i].gpio); + if (ds->info->info.no_suspend) + disable_irq_wake(irq); + free_irq(irq, &ds->key_state[i]); + } + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + for (i = di->keymap_size - 1; i >= 0; i--) { +err_gpio_configure_failed: + gpio_free(di->keymap[i].gpio); +err_gpio_request_failed: + ; + } +err_bad_keymap: + wake_lock_destroy(&ds->wake_lock); + kfree(ds); +err_ds_alloc_failed: + return ret; +} diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c new file mode 100644 index 000000000000..eaa9e89d473a --- /dev/null +++ b/drivers/input/misc/gpio_matrix.c @@ -0,0 +1,441 @@ +/* drivers/input/misc/gpio_matrix.c + * + * Copyright (C) 2007 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 +#include +#include +#include +#include +#include +#include + +struct gpio_kp { + struct gpio_event_input_devs *input_devs; + struct gpio_event_matrix_info *keypad_info; + struct hrtimer timer; + struct wake_lock wake_lock; + int current_output; + unsigned int use_irq:1; + unsigned int key_state_changed:1; + unsigned int last_key_state_changed:1; + unsigned int some_keys_pressed:2; + unsigned int disabled_irq:1; + unsigned long keys_pressed[0]; +}; + +static void clear_phantom_key(struct gpio_kp *kp, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int key_index = out * mi->ninputs + in; + unsigned short keyentry = mi->keymap[key_index]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + + if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + __clear_bit(key_index, kp->keys_pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "not cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + } +} + +static int restore_keys_for_input(struct gpio_kp *kp, int out, int in) +{ + int rv = 0; + int key_index; + + key_index = out * kp->keypad_info->ninputs + in; + while (out < kp->keypad_info->noutputs) { + if (test_bit(key_index, kp->keys_pressed)) { + rv = 1; + clear_phantom_key(kp, out, in); + } + key_index += kp->keypad_info->ninputs; + out++; + } + return rv; +} + +static void remove_phantom_keys(struct gpio_kp *kp) +{ + int out, in, inp; + int key_index; + + if (kp->some_keys_pressed < 3) + return; + + for (out = 0; out < kp->keypad_info->noutputs; out++) { + inp = -1; + key_index = out * kp->keypad_info->ninputs; + for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) { + if (test_bit(key_index, kp->keys_pressed)) { + if (inp == -1) { + inp = in; + continue; + } + if (inp >= 0) { + if (!restore_keys_for_input(kp, out + 1, + inp)) + break; + clear_phantom_key(kp, out, inp); + inp = -2; + } + restore_keys_for_input(kp, out, in); + } + } + } +} + +static void report_key(struct gpio_kp *kp, int key_index, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int pressed = test_bit(key_index, kp->keys_pressed); + unsigned short keyentry = mi->keymap[key_index]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + + if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) { + if (keycode == KEY_RESERVED) { + if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) + pr_info("gpiomatrix: unmapped key, %d-%d " + "(%d-%d) changed to %d\n", + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS) + pr_info("gpiomatrix: key %x, %d-%d (%d-%d) " + "changed to %d\n", keycode, + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + input_report_key(kp->input_devs->dev[dev], keycode, pressed); + } + } +} + +static void report_sync(struct gpio_kp *kp) +{ + int i; + + for (i = 0; i < kp->input_devs->count; i++) + input_sync(kp->input_devs->dev[i]); +} + +static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) +{ + int out, in; + int key_index; + int gpio; + struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer); + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH); + + out = kp->current_output; + if (out == mi->noutputs) { + out = 0; + kp->last_key_state_changed = kp->key_state_changed; + kp->key_state_changed = 0; + kp->some_keys_pressed = 0; + } else { + key_index = out * mi->ninputs; + for (in = 0; in < mi->ninputs; in++, key_index++) { + gpio = mi->input_gpios[in]; + if (gpio_get_value(gpio) ^ !polarity) { + if (kp->some_keys_pressed < 3) + kp->some_keys_pressed++; + kp->key_state_changed |= !__test_and_set_bit( + key_index, kp->keys_pressed); + } else + kp->key_state_changed |= __test_and_clear_bit( + key_index, kp->keys_pressed); + } + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, !polarity); + else + gpio_direction_input(gpio); + out++; + } + kp->current_output = out; + if (out < mi->noutputs) { + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, polarity); + else + gpio_direction_output(gpio, polarity); + hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) { + if (kp->key_state_changed) { + hrtimer_start(&kp->timer, mi->debounce_delay, + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + kp->key_state_changed = kp->last_key_state_changed; + } + if (kp->key_state_changed) { + if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) + remove_phantom_keys(kp); + key_index = 0; + for (out = 0; out < mi->noutputs; out++) + for (in = 0; in < mi->ninputs; in++, key_index++) + report_key(kp, key_index, out, in); + report_sync(kp); + } + if (!kp->use_irq || kp->some_keys_pressed) { + hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + + /* No keys are pressed, reenable interrupt */ + for (out = 0; out < mi->noutputs; out++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[out], polarity); + else + gpio_direction_output(mi->output_gpios[out], polarity); + } + for (in = 0; in < mi->ninputs; in++) + enable_irq(gpio_to_irq(mi->input_gpios[in])); + wake_unlock(&kp->wake_lock); + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) +{ + int i; + struct gpio_kp *kp = dev_id; + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + + if (!kp->use_irq) { + /* ignore interrupt while registering the handler */ + kp->disabled_irq = 1; + disable_irq_nosync(irq_in); + return IRQ_HANDLED; + } + + for (i = 0; i < mi->ninputs; i++) + disable_irq_nosync(gpio_to_irq(mi->input_gpios[i])); + for (i = 0; i < mi->noutputs; i++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[i], + !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH)); + else + gpio_direction_input(mi->output_gpios[i]); + } + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + return IRQ_HANDLED; +} + +static int gpio_keypad_request_irqs(struct gpio_kp *kp) +{ + int i; + int err; + unsigned int irq; + unsigned long request_flags; + struct gpio_event_matrix_info *mi = kp->keypad_info; + + switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) { + default: + request_flags = IRQF_TRIGGER_FALLING; + break; + case GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_RISING; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ: + request_flags = IRQF_TRIGGER_LOW; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_HIGH; + break; + } + + for (i = 0; i < mi->ninputs; i++) { + err = irq = gpio_to_irq(mi->input_gpios[i]); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_keypad_irq_handler, request_flags, + "gpio_kp", kp); + if (err) { + pr_err("gpiomatrix: request_irq failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + goto err_request_irq_failed; + } + err = enable_irq_wake(irq); + if (err) { + pr_err("gpiomatrix: set_irq_wake failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + } + disable_irq(irq); + if (kp->disabled_irq) { + kp->disabled_irq = 0; + enable_irq(irq); + } + } + return 0; + + for (i = mi->noutputs - 1; i >= 0; i--) { + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int i; + int err; + int key_count; + struct gpio_kp *kp; + struct gpio_event_matrix_info *mi; + + mi = container_of(info, struct gpio_event_matrix_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) { + /* TODO: disable scanning */ + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (mi->keymap == NULL || + mi->input_gpios == NULL || + mi->output_gpios == NULL) { + err = -ENODEV; + pr_err("gpiomatrix: Incomplete pdata\n"); + goto err_invalid_platform_data; + } + key_count = mi->ninputs * mi->noutputs; + + *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) * + BITS_TO_LONGS(key_count), GFP_KERNEL); + if (kp == NULL) { + err = -ENOMEM; + pr_err("gpiomatrix: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + kp->input_devs = input_devs; + kp->keypad_info = mi; + for (i = 0; i < key_count; i++) { + unsigned short keyentry = mi->keymap[i]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + if (dev >= input_devs->count) { + pr_err("gpiomatrix: bad device index %d >= " + "%d for key code %d\n", + dev, input_devs->count, keycode); + err = -EINVAL; + goto err_bad_keymap; + } + if (keycode && keycode <= KEY_MAX) + input_set_capability(input_devs->dev[dev], + EV_KEY, keycode); + } + + for (i = 0; i < mi->noutputs; i++) { + err = gpio_request(mi->output_gpios[i], "gpio_kp_out"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "output %d\n", mi->output_gpios[i]); + goto err_request_output_gpio_failed; + } + if (gpio_cansleep(mi->output_gpios[i])) { + pr_err("gpiomatrix: unsupported output gpio %d," + " can sleep\n", mi->output_gpios[i]); + err = -EINVAL; + goto err_output_gpio_configure_failed; + } + if (mi->flags & GPIOKPF_DRIVE_INACTIVE) + err = gpio_direction_output(mi->output_gpios[i], + !(mi->flags & GPIOKPF_ACTIVE_HIGH)); + else + err = gpio_direction_input(mi->output_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_configure failed for " + "output %d\n", mi->output_gpios[i]); + goto err_output_gpio_configure_failed; + } + } + for (i = 0; i < mi->ninputs; i++) { + err = gpio_request(mi->input_gpios[i], "gpio_kp_in"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "input %d\n", mi->input_gpios[i]); + goto err_request_input_gpio_failed; + } + err = gpio_direction_input(mi->input_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_direction_input failed" + " for input %d\n", mi->input_gpios[i]); + goto err_gpio_direction_input_failed; + } + } + kp->current_output = mi->noutputs; + kp->key_state_changed = 1; + + hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + kp->timer.function = gpio_keypad_timer_func; + wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp"); + err = gpio_keypad_request_irqs(kp); + kp->use_irq = err == 0; + + pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for " + "%s%s in %s mode\n", input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : "", + kp->use_irq ? "interrupt" : "polling"); + + if (kp->use_irq) + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + + return 0; + } + + err = 0; + kp = *data; + + if (kp->use_irq) + for (i = mi->noutputs - 1; i >= 0; i--) + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); + + hrtimer_cancel(&kp->timer); + wake_lock_destroy(&kp->wake_lock); + for (i = mi->noutputs - 1; i >= 0; i--) { +err_gpio_direction_input_failed: + gpio_free(mi->input_gpios[i]); +err_request_input_gpio_failed: + ; + } + for (i = mi->noutputs - 1; i >= 0; i--) { +err_output_gpio_configure_failed: + gpio_free(mi->output_gpios[i]); +err_request_output_gpio_failed: + ; + } +err_bad_keymap: + kfree(kp); +err_kp_alloc_failed: +err_invalid_platform_data: + return err; +} diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c new file mode 100644 index 000000000000..2aac2fad0a17 --- /dev/null +++ b/drivers/input/misc/gpio_output.c @@ -0,0 +1,97 @@ +/* drivers/input/misc/gpio_output.c + * + * Copyright (C) 2007 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 +#include +#include + +int gpio_event_output_event( + struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, + void **data, unsigned int dev, unsigned int type, + unsigned int code, int value) +{ + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + if (type != oi->type) + return 0; + if (!(oi->flags & GPIOEDF_ACTIVE_HIGH)) + value = !value; + for (i = 0; i < oi->keymap_size; i++) + if (dev == oi->keymap[i].dev && code == oi->keymap[i].code) + gpio_set_value(oi->keymap[i].gpio, value); + return 0; +} + +int gpio_event_output_func( + struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, + void **data, int func) +{ + int ret; + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) + return 0; + + if (func == GPIO_EVENT_FUNC_INIT) { + int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH); + + for (i = 0; i < oi->keymap_size; i++) { + int dev = oi->keymap[i].dev; + if (dev >= input_devs->count) { + pr_err("gpio_event_output_func: bad device " + "index %d >= %d for key code %d\n", + dev, input_devs->count, + oi->keymap[i].code); + ret = -EINVAL; + goto err_bad_keymap; + } + input_set_capability(input_devs->dev[dev], oi->type, + oi->keymap[i].code); + } + + for (i = 0; i < oi->keymap_size; i++) { + ret = gpio_request(oi->keymap[i].gpio, + "gpio_event_output"); + if (ret) { + pr_err("gpio_event_output_func: gpio_request " + "failed for %d\n", oi->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_output(oi->keymap[i].gpio, + output_level); + if (ret) { + pr_err("gpio_event_output_func: " + "gpio_direction_output failed for %d\n", + oi->keymap[i].gpio); + goto err_gpio_direction_output_failed; + } + } + return 0; + } + + ret = 0; + for (i = oi->keymap_size - 1; i >= 0; i--) { +err_gpio_direction_output_failed: + gpio_free(oi->keymap[i].gpio); +err_gpio_request_failed: + ; + } +err_bad_keymap: + return ret; +} + diff --git a/include/linux/gpio_event.h b/include/linux/gpio_event.h new file mode 100644 index 000000000000..2613fc5e4a93 --- /dev/null +++ b/include/linux/gpio_event.h @@ -0,0 +1,170 @@ +/* include/linux/gpio_event.h + * + * Copyright (C) 2007 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_GPIO_EVENT_H +#define _LINUX_GPIO_EVENT_H + +#include + +struct gpio_event_input_devs { + int count; + struct input_dev *dev[]; +}; +enum { + GPIO_EVENT_FUNC_UNINIT = 0x0, + GPIO_EVENT_FUNC_INIT = 0x1, + GPIO_EVENT_FUNC_SUSPEND = 0x2, + GPIO_EVENT_FUNC_RESUME = 0x3, +}; +struct gpio_event_info { + int (*func)(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, + void **data, int func); + int (*event)(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, + void **data, unsigned int dev, unsigned int type, + unsigned int code, int value); /* out events */ + bool no_suspend; +}; + +struct gpio_event_platform_data { + const char *name; + struct gpio_event_info **info; + size_t info_count; + int (*power)(const struct gpio_event_platform_data *pdata, bool on); + const char *names[]; /* If name is NULL, names contain a NULL */ + /* terminated list of input devices to create */ +}; + +#define GPIO_EVENT_DEV_NAME "gpio-event" + +/* Key matrix */ + +enum gpio_event_matrix_flags { + /* unset: drive active output low, set: drive active output high */ + GPIOKPF_ACTIVE_HIGH = 1U << 0, + GPIOKPF_DEBOUNCE = 1U << 1, + GPIOKPF_REMOVE_SOME_PHANTOM_KEYS = 1U << 2, + GPIOKPF_REMOVE_PHANTOM_KEYS = GPIOKPF_REMOVE_SOME_PHANTOM_KEYS | + GPIOKPF_DEBOUNCE, + GPIOKPF_DRIVE_INACTIVE = 1U << 3, + GPIOKPF_LEVEL_TRIGGERED_IRQ = 1U << 4, + GPIOKPF_PRINT_UNMAPPED_KEYS = 1U << 16, + GPIOKPF_PRINT_MAPPED_KEYS = 1U << 17, + GPIOKPF_PRINT_PHANTOM_KEYS = 1U << 18, +}; + +#define MATRIX_CODE_BITS (10) +#define MATRIX_KEY_MASK ((1U << MATRIX_CODE_BITS) - 1) +#define MATRIX_KEY(dev, code) \ + (((dev) << MATRIX_CODE_BITS) | (code & MATRIX_KEY_MASK)) + +extern int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_matrix_info { + /* initialize to gpio_event_matrix_func */ + struct gpio_event_info info; + /* size must be ninputs * noutputs */ + const unsigned short *keymap; + unsigned int *input_gpios; + unsigned int *output_gpios; + unsigned int ninputs; + unsigned int noutputs; + /* time to wait before reading inputs after driving each output */ + ktime_t settle_time; + /* time to wait before scanning the keypad a second time */ + ktime_t debounce_delay; + ktime_t poll_time; + unsigned flags; +}; + +/* Directly connected inputs and outputs */ + +enum gpio_event_direct_flags { + GPIOEDF_ACTIVE_HIGH = 1U << 0, +/* GPIOEDF_USE_DOWN_IRQ = 1U << 1, */ +/* GPIOEDF_USE_IRQ = (1U << 2) | GPIOIDF_USE_DOWN_IRQ, */ + GPIOEDF_PRINT_KEYS = 1U << 8, + GPIOEDF_PRINT_KEY_DEBOUNCE = 1U << 9, + GPIOEDF_PRINT_KEY_UNSTABLE = 1U << 10, +}; + +struct gpio_event_direct_entry { + uint32_t gpio:16; + uint32_t code:10; + uint32_t dev:6; +}; + +/* inputs */ +extern int gpio_event_input_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_input_info { + /* initialize to gpio_event_input_func */ + struct gpio_event_info info; + ktime_t debounce_time; + ktime_t poll_time; + uint16_t flags; + uint16_t type; + const struct gpio_event_direct_entry *keymap; + size_t keymap_size; +}; + +/* outputs */ +extern int gpio_event_output_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +extern int gpio_event_output_event(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, + unsigned int dev, unsigned int type, + unsigned int code, int value); +struct gpio_event_output_info { + /* initialize to gpio_event_output_func and gpio_event_output_event */ + struct gpio_event_info info; + uint16_t flags; + uint16_t type; + const struct gpio_event_direct_entry *keymap; + size_t keymap_size; +}; + + +/* axes */ + +enum gpio_event_axis_flags { + GPIOEAF_PRINT_UNKNOWN_DIRECTION = 1U << 16, + GPIOEAF_PRINT_RAW = 1U << 17, + GPIOEAF_PRINT_EVENT = 1U << 18, +}; + +extern int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_axis_info { + /* initialize to gpio_event_axis_func */ + struct gpio_event_info info; + uint8_t count; /* number of gpios for this axis */ + uint8_t dev; /* device index when using multiple input devices */ + uint8_t type; /* EV_REL or EV_ABS */ + uint16_t code; + uint16_t decoded_size; + uint16_t (*map)(struct gpio_event_axis_info *info, uint16_t in); + uint32_t *gpio; + uint32_t flags; +}; +#define gpio_axis_2bit_gray_map gpio_axis_4bit_gray_map +#define gpio_axis_3bit_gray_map gpio_axis_4bit_gray_map +uint16_t gpio_axis_4bit_gray_map( + struct gpio_event_axis_info *info, uint16_t in); +uint16_t gpio_axis_5bit_singletrack_map( + struct gpio_event_axis_info *info, uint16_t in); + +#endif From 391f7b69c8a36b524c55af7a7f2a0f67379fe8be Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 1 Feb 2012 20:26:28 -0800 Subject: [PATCH 033/358] input: misc: gpio_event: remove early suspend Remove the early suspend handler. Leave the suspend functions for now, they should eventually get called through a userspace interface.x Change-Id: I67f9dafe32fe32577bab93c42b95824db96c215c Signed-off-by: Colin Cross --- drivers/input/misc/gpio_event.c | 39 ++++++++------------------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c index d4e5b4dfe19f..90f07eba3ce9 100644 --- a/drivers/input/misc/gpio_event.c +++ b/drivers/input/misc/gpio_event.c @@ -13,7 +13,6 @@ * */ -#include #include #include #include @@ -24,7 +23,6 @@ struct gpio_event { struct gpio_event_input_devs *input_devs; const struct gpio_event_platform_data *info; - struct early_suspend early_suspend; void *state[0]; }; @@ -101,23 +99,19 @@ err_no_func: return ret; } -#ifdef CONFIG_HAS_EARLYSUSPEND -void gpio_event_suspend(struct early_suspend *h) +static void __maybe_unused gpio_event_suspend(struct gpio_event *ip) { - struct gpio_event *ip; - ip = container_of(h, struct gpio_event, early_suspend); gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); - ip->info->power(ip->info, 0); + if (ip->info->power) + ip->info->power(ip->info, 0); } -void gpio_event_resume(struct early_suspend *h) +static void __maybe_unused gpio_event_resume(struct gpio_event *ip) { - struct gpio_event *ip; - ip = container_of(h, struct gpio_event, early_suspend); - ip->info->power(ip->info, 1); + if (ip->info->power) + ip->info->power(ip->info, 1); gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); } -#endif static int gpio_event_probe(struct platform_device *pdev) { @@ -169,15 +163,8 @@ static int gpio_event_probe(struct platform_device *pdev) } ip->input_devs->count = dev_count; ip->info = event_info; - if (event_info->power) { -#ifdef CONFIG_HAS_EARLYSUSPEND - ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; - ip->early_suspend.suspend = gpio_event_suspend; - ip->early_suspend.resume = gpio_event_resume; - register_early_suspend(&ip->early_suspend); -#endif + if (event_info->power) ip->info->power(ip->info, 1); - } err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); if (err) @@ -198,12 +185,8 @@ static int gpio_event_probe(struct platform_device *pdev) err_input_register_device_failed: gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); err_call_all_func_failed: - if (event_info->power) { -#ifdef CONFIG_HAS_EARLYSUSPEND - unregister_early_suspend(&ip->early_suspend); -#endif + if (event_info->power) ip->info->power(ip->info, 0); - } for (i = 0; i < registered; i++) input_unregister_device(ip->input_devs->dev[i]); for (i = dev_count - 1; i >= registered; i--) { @@ -222,12 +205,8 @@ static int gpio_event_remove(struct platform_device *pdev) int i; gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); - if (ip->info->power) { -#ifdef CONFIG_HAS_EARLYSUSPEND - unregister_early_suspend(&ip->early_suspend); -#endif + if (ip->info->power) ip->info->power(ip->info, 0); - } for (i = 0; i < ip->input_devs->count; i++) input_unregister_device(ip->input_devs->dev[i]); kfree(ip); From 19e929a1da84e9a904d8f6c1814e6ae981b1cfba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 21 Nov 2008 21:47:23 -0800 Subject: [PATCH 034/358] input: Add keyreset driver. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a platform device in the board file to specify a reset key-combo. The first time the key-combo is detected a work function that syncs the filesystems is scheduled. If all the keys are released and then pressed again, it calls panic. Reboot on panic should be set for this to work. Change-Id: I9d54283ca1fba45e4b1ae1a407524cdda8171143 Signed-off-by: Arve Hjønnevåg --- drivers/input/Kconfig | 9 ++ drivers/input/Makefile | 1 + drivers/input/keyreset.c | 239 +++++++++++++++++++++++++++++++++++++++ include/linux/keyreset.h | 28 +++++ 4 files changed, 277 insertions(+) create mode 100644 drivers/input/keyreset.c create mode 100644 include/linux/keyreset.h diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index a11ff74a5127..9c13b63ef915 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -174,6 +174,15 @@ config INPUT_APMPOWER To compile this driver as a module, choose M here: the module will be called apm-power. +config INPUT_KEYRESET + tristate "Reset key" + depends on INPUT + ---help--- + Say Y here if you want to reboot when some keys are pressed; + + To compile this driver as a module, choose M here: the + module will be called keyreset. + comment "Input Device Drivers" source "drivers/input/keyboard/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 5ca3f631497f..191ea43d0800 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o +obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c new file mode 100644 index 000000000000..36208fe0baae --- /dev/null +++ b/drivers/input/keyreset.c @@ -0,0 +1,239 @@ +/* drivers/input/keyreset.c + * + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include +#include + + +struct keyreset_state { + struct input_handler input_handler; + unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long upbit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long key[BITS_TO_LONGS(KEY_CNT)]; + spinlock_t lock; + int key_down_target; + int key_down; + int key_up; + int restart_disabled; + int (*reset_fn)(void); +}; + +int restart_requested; +static void deferred_restart(struct work_struct *dummy) +{ + restart_requested = 2; + sys_sync(); + restart_requested = 3; + kernel_restart(NULL); +} +static DECLARE_WORK(restart_work, deferred_restart); + +static void keyreset_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + unsigned long flags; + struct keyreset_state *state = handle->private; + + if (type != EV_KEY) + return; + + if (code >= KEY_MAX) + return; + + if (!test_bit(code, state->keybit)) + return; + + spin_lock_irqsave(&state->lock, flags); + if (!test_bit(code, state->key) == !value) + goto done; + __change_bit(code, state->key); + if (test_bit(code, state->upbit)) { + if (value) { + state->restart_disabled = 1; + state->key_up++; + } else + state->key_up--; + } else { + if (value) + state->key_down++; + else + state->key_down--; + } + if (state->key_down == 0 && state->key_up == 0) + state->restart_disabled = 0; + + pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value, + state->key_down, state->key_up, state->restart_disabled); + + if (value && !state->restart_disabled && + state->key_down == state->key_down_target) { + state->restart_disabled = 1; + if (restart_requested) + panic("keyboard reset failed, %d", restart_requested); + if (state->reset_fn) { + restart_requested = state->reset_fn(); + } else { + pr_info("keyboard reset\n"); + schedule_work(&restart_work); + restart_requested = 1; + } + } +done: + spin_unlock_irqrestore(&state->lock, flags); +} + +static int keyreset_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i; + int ret; + struct input_handle *handle; + struct keyreset_state *state = + container_of(handler, struct keyreset_state, input_handler); + + for (i = 0; i < KEY_MAX; i++) { + if (test_bit(i, state->keybit) && test_bit(i, dev->keybit)) + break; + } + if (i == KEY_MAX) + return -ENODEV; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "keyreset"; + handle->private = state; + + ret = input_register_handle(handle); + if (ret) + goto err_input_register_handle; + + ret = input_open_device(handle); + if (ret) + goto err_input_open_device; + + pr_info("using input dev %s for key reset\n", dev->name); + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keyreset_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id keyreset_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, keyreset_ids); + +static int keyreset_probe(struct platform_device *pdev) +{ + int ret; + int key, *keyp; + struct keyreset_state *state; + struct keyreset_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return -EINVAL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + spin_lock_init(&state->lock); + keyp = pdata->keys_down; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + state->key_down_target++; + __set_bit(key, state->keybit); + } + if (pdata->keys_up) { + keyp = pdata->keys_up; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + __set_bit(key, state->keybit); + __set_bit(key, state->upbit); + } + } + + if (pdata->reset_fn) + state->reset_fn = pdata->reset_fn; + + state->input_handler.event = keyreset_event; + state->input_handler.connect = keyreset_connect; + state->input_handler.disconnect = keyreset_disconnect; + state->input_handler.name = KEYRESET_NAME; + state->input_handler.id_table = keyreset_ids; + ret = input_register_handler(&state->input_handler); + if (ret) { + kfree(state); + return ret; + } + platform_set_drvdata(pdev, state); + return 0; +} + +int keyreset_remove(struct platform_device *pdev) +{ + struct keyreset_state *state = platform_get_drvdata(pdev); + input_unregister_handler(&state->input_handler); + kfree(state); + return 0; +} + + +struct platform_driver keyreset_driver = { + .driver.name = KEYRESET_NAME, + .probe = keyreset_probe, + .remove = keyreset_remove, +}; + +static int __init keyreset_init(void) +{ + return platform_driver_register(&keyreset_driver); +} + +static void __exit keyreset_exit(void) +{ + return platform_driver_unregister(&keyreset_driver); +} + +module_init(keyreset_init); +module_exit(keyreset_exit); diff --git a/include/linux/keyreset.h b/include/linux/keyreset.h new file mode 100644 index 000000000000..a2ac49e5b684 --- /dev/null +++ b/include/linux/keyreset.h @@ -0,0 +1,28 @@ +/* + * include/linux/keyreset.h - platform data structure for resetkeys driver + * + * Copyright (C) 2008 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_KEYRESET_H +#define _LINUX_KEYRESET_H + +#define KEYRESET_NAME "keyreset" + +struct keyreset_platform_data { + int (*reset_fn)(void); + int *keys_up; + int keys_down[]; /* 0 terminated */ +}; + +#endif /* _LINUX_KEYRESET_H */ From af4058dce0034f4e1533f09d1a7902e93a38a998 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 15 Dec 2008 14:51:56 -0500 Subject: [PATCH 035/358] input: keychord: Add keychord driver This driver allows userspace to receive notification when client specified key combinations are pressed. The client opens /dev/keychord and writes a list of keychords for the driver to monitor. The client then reads or polls /dev/keychord for notifications. A client specified ID for the keychord is returned from read() when a keychord press is detected. Signed-off-by: Mike Lockwood keychord: fix to build without CONFIG_PREEMPT Change-Id: I911f13aeda4224b6fa57863bc7e8972fec8837fb --- drivers/input/misc/Kconfig | 11 + drivers/input/misc/Makefile | 1 + drivers/input/misc/keychord.c | 387 ++++++++++++++++++++++++++++++++++ include/linux/keychord.h | 52 +++++ 4 files changed, 451 insertions(+) create mode 100644 drivers/input/misc/keychord.c create mode 100644 include/linux/keychord.h diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index a0a2965b4be1..eb7e193dad66 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -308,6 +308,17 @@ config INPUT_ATI_REMOTE2 To compile this driver as a module, choose M here: the module will be called ati_remote2. +config INPUT_KEYCHORD + tristate "Key chord input driver support" + help + Say Y here if you want to enable the key chord driver + accessible at /dev/keychord. This driver can be used + for receiving notifications when client specified key + combinations are pressed. + + To compile this driver as a module, choose M here: the + module will be called keychord. + config INPUT_KEYSPAN_REMOTE tristate "Keyspan DMR USB remote control" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index a631ce71ace5..37813b476bcd 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o +obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c new file mode 100644 index 000000000000..3ffab6da411b --- /dev/null +++ b/drivers/input/misc/keychord.c @@ -0,0 +1,387 @@ +/* + * drivers/input/misc/keychord.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define KEYCHORD_NAME "keychord" +#define BUFFER_SIZE 16 + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("Key chord input driver"); +MODULE_SUPPORTED_DEVICE("keychord"); +MODULE_LICENSE("GPL"); + +#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \ + ((char *)kc + sizeof(struct input_keychord) + \ + kc->count * sizeof(kc->keycodes[0]))) + +struct keychord_device { + struct input_handler input_handler; + int registered; + + /* list of keychords to monitor */ + struct input_keychord *keychords; + int keychord_count; + + /* bitmask of keys contained in our keychords */ + unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; + /* current state of the keys */ + unsigned long keystate[BITS_TO_LONGS(KEY_CNT)]; + /* number of keys that are currently pressed */ + int key_down; + + /* second input_device_id is needed for null termination */ + struct input_device_id device_ids[2]; + + spinlock_t lock; + wait_queue_head_t waitq; + unsigned char head; + unsigned char tail; + __u16 buff[BUFFER_SIZE]; +}; + +static int check_keychord(struct keychord_device *kdev, + struct input_keychord *keychord) +{ + int i; + + if (keychord->count != kdev->key_down) + return 0; + + for (i = 0; i < keychord->count; i++) { + if (!test_bit(keychord->keycodes[i], kdev->keystate)) + return 0; + } + + /* we have a match */ + return 1; +} + +static void keychord_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + struct keychord_device *kdev = handle->private; + struct input_keychord *keychord; + unsigned long flags; + int i, got_chord = 0; + + if (type != EV_KEY || code >= KEY_MAX) + return; + + spin_lock_irqsave(&kdev->lock, flags); + /* do nothing if key state did not change */ + if (!test_bit(code, kdev->keystate) == !value) + goto done; + __change_bit(code, kdev->keystate); + if (value) + kdev->key_down++; + else + kdev->key_down--; + + /* don't notify on key up */ + if (!value) + goto done; + /* ignore this event if it is not one of the keys we are monitoring */ + if (!test_bit(code, kdev->keybit)) + goto done; + + keychord = kdev->keychords; + if (!keychord) + goto done; + + /* check to see if the keyboard state matches any keychords */ + for (i = 0; i < kdev->keychord_count; i++) { + if (check_keychord(kdev, keychord)) { + kdev->buff[kdev->head] = keychord->id; + kdev->head = (kdev->head + 1) % BUFFER_SIZE; + got_chord = 1; + break; + } + /* skip to next keychord */ + keychord = NEXT_KEYCHORD(keychord); + } + +done: + spin_unlock_irqrestore(&kdev->lock, flags); + + if (got_chord) + wake_up_interruptible(&kdev->waitq); +} + +static int keychord_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i, ret; + struct input_handle *handle; + struct keychord_device *kdev = + container_of(handler, struct keychord_device, input_handler); + + /* + * ignore this input device if it does not contain any keycodes + * that we are monitoring + */ + for (i = 0; i < KEY_MAX; i++) { + if (test_bit(i, kdev->keybit) && test_bit(i, dev->keybit)) + break; + } + if (i == KEY_MAX) + return -ENODEV; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = KEYCHORD_NAME; + handle->private = kdev; + + ret = input_register_handle(handle); + if (ret) + goto err_input_register_handle; + + ret = input_open_device(handle); + if (ret) + goto err_input_open_device; + + pr_info("keychord: using input dev %s for fevent\n", dev->name); + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keychord_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +/* + * keychord_read is used to read keychord events from the driver + */ +static ssize_t keychord_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct keychord_device *kdev = file->private_data; + __u16 id; + int retval; + unsigned long flags; + + if (count < sizeof(id)) + return -EINVAL; + count = sizeof(id); + + if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(kdev->waitq, + kdev->head != kdev->tail); + if (retval) + return retval; + + spin_lock_irqsave(&kdev->lock, flags); + /* pop a keychord ID off the queue */ + id = kdev->buff[kdev->tail]; + kdev->tail = (kdev->tail + 1) % BUFFER_SIZE; + spin_unlock_irqrestore(&kdev->lock, flags); + + if (copy_to_user(buffer, &id, count)) + return -EFAULT; + + return count; +} + +/* + * keychord_write is used to configure the driver + */ +static ssize_t keychord_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct keychord_device *kdev = file->private_data; + struct input_keychord *keychords = 0; + struct input_keychord *keychord, *next, *end; + int ret, i, key; + unsigned long flags; + + if (count < sizeof(struct input_keychord)) + return -EINVAL; + keychords = kzalloc(count, GFP_KERNEL); + if (!keychords) + return -ENOMEM; + + /* read list of keychords from userspace */ + if (copy_from_user(keychords, buffer, count)) { + kfree(keychords); + return -EFAULT; + } + + /* unregister handler before changing configuration */ + if (kdev->registered) { + input_unregister_handler(&kdev->input_handler); + kdev->registered = 0; + } + + spin_lock_irqsave(&kdev->lock, flags); + /* clear any existing configuration */ + kfree(kdev->keychords); + kdev->keychords = 0; + kdev->keychord_count = 0; + kdev->key_down = 0; + memset(kdev->keybit, 0, sizeof(kdev->keybit)); + memset(kdev->keystate, 0, sizeof(kdev->keystate)); + kdev->head = kdev->tail = 0; + + keychord = keychords; + end = (struct input_keychord *)((char *)keychord + count); + + while (keychord < end) { + next = NEXT_KEYCHORD(keychord); + if (keychord->count <= 0 || next > end) { + pr_err("keychord: invalid keycode count %d\n", + keychord->count); + goto err_unlock_return; + } + if (keychord->version != KEYCHORD_VERSION) { + pr_err("keychord: unsupported version %d\n", + keychord->version); + goto err_unlock_return; + } + + /* keep track of the keys we are monitoring in keybit */ + for (i = 0; i < keychord->count; i++) { + key = keychord->keycodes[i]; + if (key < 0 || key >= KEY_CNT) { + pr_err("keychord: keycode %d out of range\n", + key); + goto err_unlock_return; + } + __set_bit(key, kdev->keybit); + } + + kdev->keychord_count++; + keychord = next; + } + + kdev->keychords = keychords; + spin_unlock_irqrestore(&kdev->lock, flags); + + ret = input_register_handler(&kdev->input_handler); + if (ret) { + kfree(keychords); + kdev->keychords = 0; + return ret; + } + kdev->registered = 1; + + return count; + +err_unlock_return: + spin_unlock_irqrestore(&kdev->lock, flags); + kfree(keychords); + return -EINVAL; +} + +static unsigned int keychord_poll(struct file *file, poll_table *wait) +{ + struct keychord_device *kdev = file->private_data; + + poll_wait(file, &kdev->waitq, wait); + + if (kdev->head != kdev->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int keychord_open(struct inode *inode, struct file *file) +{ + struct keychord_device *kdev; + + kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL); + if (!kdev) + return -ENOMEM; + + spin_lock_init(&kdev->lock); + init_waitqueue_head(&kdev->waitq); + + kdev->input_handler.event = keychord_event; + kdev->input_handler.connect = keychord_connect; + kdev->input_handler.disconnect = keychord_disconnect; + kdev->input_handler.name = KEYCHORD_NAME; + kdev->input_handler.id_table = kdev->device_ids; + + kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT; + __set_bit(EV_KEY, kdev->device_ids[0].evbit); + + file->private_data = kdev; + + return 0; +} + +static int keychord_release(struct inode *inode, struct file *file) +{ + struct keychord_device *kdev = file->private_data; + + if (kdev->registered) + input_unregister_handler(&kdev->input_handler); + kfree(kdev); + + return 0; +} + +static const struct file_operations keychord_fops = { + .owner = THIS_MODULE, + .open = keychord_open, + .release = keychord_release, + .read = keychord_read, + .write = keychord_write, + .poll = keychord_poll, +}; + +static struct miscdevice keychord_misc = { + .fops = &keychord_fops, + .name = KEYCHORD_NAME, + .minor = MISC_DYNAMIC_MINOR, +}; + +static int __init keychord_init(void) +{ + return misc_register(&keychord_misc); +} + +static void __exit keychord_exit(void) +{ + misc_deregister(&keychord_misc); +} + +module_init(keychord_init); +module_exit(keychord_exit); diff --git a/include/linux/keychord.h b/include/linux/keychord.h new file mode 100644 index 000000000000..856a5850217b --- /dev/null +++ b/include/linux/keychord.h @@ -0,0 +1,52 @@ +/* + * Key chord input driver + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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_KEYCHORD_H_ +#define __LINUX_KEYCHORD_H_ + +#include + +#define KEYCHORD_VERSION 1 + +/* + * One or more input_keychord structs are written to /dev/keychord + * at once to specify the list of keychords to monitor. + * Reading /dev/keychord returns the id of a keychord when the + * keychord combination is pressed. A keychord is signalled when + * all of the keys in the keycode list are in the pressed state. + * The order in which the keys are pressed does not matter. + * The keychord will not be signalled if keys not in the keycode + * list are pressed. + * Keychords will not be signalled on key release events. + */ +struct input_keychord { + /* should be KEYCHORD_VERSION */ + __u16 version; + /* + * client specified ID, returned from read() + * when this keychord is pressed. + */ + __u16 id; + + /* number of keycodes in this keychord */ + __u16 count; + + /* variable length array of keycodes */ + __u16 keycodes[]; +}; + +#endif /* __LINUX_KEYCHORD_H_ */ From 3638fe267b11b80eebacbec1b9410d0e9c235856 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 10 Nov 2008 16:29:50 -0800 Subject: [PATCH 036/358] mmc: Add status IRQ and status callback function to mmc platform data Signed-off-by: San Mehat --- include/linux/amba/mmci.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 32a89cf5ec45..0926063bff9f 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -73,6 +73,8 @@ struct mmci_platform_data { bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; + unsigned int status_irq; + int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); }; #endif From ab4cdccd724660dbf3db5d38e5aae73791c025a8 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 11 Nov 2008 09:35:36 -0800 Subject: [PATCH 037/358] mmc: sd: Add new CONFIG_MMC_PARANOID_SD_INIT for enabling retries during SD detection Signed-off-by: San Mehat --- drivers/mmc/core/Kconfig | 8 ++++++++ drivers/mmc/core/sd.c | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index 269d072ef55e..b2c6616d3b62 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -26,3 +26,11 @@ config MMC_CLKGATE support handling this in order for it to be of any use. If unsure, say N. + +config MMC_PARANOID_SD_INIT + bool "Enable paranoid SD card initialization (EXPERIMENTAL)" + help + If you say Y here, the MMC layer will be extra paranoid + about re-trying SD init requests. This can be a useful + work-around for buggy controllers and hardware. Enable + if you are experiencing issues with SD detection. diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 692fdb177294..2c3020c03fb5 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -823,6 +823,9 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, bool reinit) { int err; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries; +#endif if (!reinit) { /* @@ -849,7 +852,26 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, /* * Fetch switch information from card. */ +#ifdef CONFIG_MMC_PARANOID_SD_INIT + for (retries = 1; retries <= 3; retries++) { + err = mmc_read_switch(card); + if (!err) { + if (retries > 1) { + printk(KERN_WARNING + "%s: recovered\n", + mmc_hostname(host)); + } + break; + } else { + printk(KERN_WARNING + "%s: read switch failed (attempt %d)\n", + mmc_hostname(host), retries); + } + } +#else err = mmc_read_switch(card); +#endif + if (err) return err; } From 545e0a7df93cceda7bb1d270449595e4a8c48535 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 14 Apr 2008 15:22:49 -0700 Subject: [PATCH 038/358] mmc: Add concept of an 'embedded' SDIO device. This is required to support chips which use SDIO for signaling/ communication but do not implement the various card enumeration registers as required for full SD / SDIO cards. mmc: sdio: Fix bug where we're freeing the CIS tables we never allocated when using EMBEDDED_SDIO mmc: Add max_blksize to embedded SDIO data Signed-off-by: San Mehat --- drivers/mmc/core/Kconfig | 9 +++++ drivers/mmc/core/core.c | 16 ++++++++ drivers/mmc/core/sdio.c | 72 +++++++++++++++++++++++++++-------- drivers/mmc/core/sdio_bus.c | 13 ++++++- include/linux/amba/mmci.h | 10 +++++ include/linux/mmc/host.h | 17 +++++++++ include/linux/mmc/sdio_func.h | 8 ++++ 7 files changed, 129 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index b2c6616d3b62..a7c3cdc59f76 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -27,8 +27,17 @@ config MMC_CLKGATE If unsure, say N. +config MMC_EMBEDDED_SDIO + boolean "MMC embedded SDIO device support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you say Y here, support will be added for embedded SDIO + devices which do not contain the necessary enumeration + support in hardware to be properly detected. + config MMC_PARANOID_SD_INIT bool "Enable paranoid SD card initialization (EXPERIMENTAL)" + depends on EXPERIMENTAL help If you say Y here, the MMC layer will be extra paranoid about re-trying SD init requests. This can be a useful diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 098374b1ab2b..e4bb25efcba6 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2712,6 +2712,22 @@ void mmc_init_context_info(struct mmc_host *host) init_waitqueue_head(&host->context_info.wait); } +#ifdef CONFIG_MMC_EMBEDDED_SDIO +void mmc_set_embedded_sdio_data(struct mmc_host *host, + struct sdio_cis *cis, + struct sdio_cccr *cccr, + struct sdio_embedded_func *funcs, + int num_funcs) +{ + host->embedded_sdio_data.cis = cis; + host->embedded_sdio_data.cccr = cccr; + host->embedded_sdio_data.funcs = funcs; + host->embedded_sdio_data.num_funcs = num_funcs; +} + +EXPORT_SYMBOL(mmc_set_embedded_sdio_data); +#endif + static int __init mmc_init(void) { int ret; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 4d721c6e2af0..360cd7109f74 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -28,6 +28,10 @@ #include "sdio_ops.h" #include "sdio_cis.h" +#ifdef CONFIG_MMC_EMBEDDED_SDIO +#include +#endif + static int sdio_read_fbr(struct sdio_func *func) { int ret; @@ -740,19 +744,35 @@ try_again: goto finish; } - /* - * Read the common registers. - */ - err = sdio_read_cccr(card, ocr); - if (err) - goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.cccr) + memcpy(&card->cccr, host->embedded_sdio_data.cccr, sizeof(struct sdio_cccr)); + else { +#endif + /* + * Read the common registers. + */ + err = sdio_read_cccr(card, ocr); + if (err) + goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif - /* - * Read the common CIS tuples. - */ - err = sdio_read_common_cis(card); - if (err) - goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.cis) + memcpy(&card->cis, host->embedded_sdio_data.cis, sizeof(struct sdio_cis)); + else { +#endif + /* + * Read the common CIS tuples. + */ + err = sdio_read_common_cis(card); + if (err) + goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif if (oldcard) { int same = (card->cis.vendor == oldcard->cis.vendor && @@ -1177,14 +1197,36 @@ int mmc_attach_sdio(struct mmc_host *host) funcs = (ocr & 0x70000000) >> 28; card->sdio_funcs = 0; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.funcs) + card->sdio_funcs = funcs = host->embedded_sdio_data.num_funcs; +#endif + /* * Initialize (but don't add) all present functions. */ for (i = 0; i < funcs; i++, card->sdio_funcs++) { - err = sdio_init_func(host->card, i + 1); - if (err) - goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.funcs) { + struct sdio_func *tmp; + tmp = sdio_alloc_func(host->card); + if (IS_ERR(tmp)) + goto remove; + tmp->num = (i + 1); + card->sdio_func[i] = tmp; + tmp->class = host->embedded_sdio_data.funcs[i].f_class; + tmp->max_blksize = host->embedded_sdio_data.funcs[i].f_maxblksize; + tmp->vendor = card->cis.vendor; + tmp->device = card->cis.device; + } else { +#endif + err = sdio_init_func(host->card, i + 1); + if (err) + goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif /* * Enable Runtime PM for this func (if supported) */ diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 92d1ba8e8153..872f45dd7982 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -25,6 +25,10 @@ #include "sdio_cis.h" #include "sdio_bus.h" +#ifdef CONFIG_MMC_EMBEDDED_SDIO +#include +#endif + /* show configuration fields */ #define sdio_config_attr(field, format_string) \ static ssize_t \ @@ -273,7 +277,14 @@ static void sdio_release_func(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); - sdio_free_func_cis(func); +#ifdef CONFIG_MMC_EMBEDDED_SDIO + /* + * If this device is embedded then we never allocated + * cis tables for this func + */ + if (!func->card->host->embedded_sdio_data.funcs) +#endif + sdio_free_func_cis(func); kfree(func->info); diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 0926063bff9f..9d1d9caf2611 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -5,6 +5,15 @@ #define AMBA_MMCI_H #include +#include +#include + +struct embedded_sdio_data { + struct sdio_cis cis; + struct sdio_cccr cccr; + struct sdio_embedded_func *funcs; + int num_funcs; +}; /* @@ -74,6 +83,7 @@ struct mmci_platform_data { void *dma_rx_param; void *dma_tx_param; unsigned int status_irq; + struct embedded_sdio_data *embedded_sdio; int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); }; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 99f5709ac343..9b92af47577f 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -362,6 +362,15 @@ struct mmc_host { unsigned int slotno; /* used for sdio acpi binding */ +#ifdef CONFIG_MMC_EMBEDDED_SDIO + struct { + struct sdio_cis *cis; + struct sdio_cccr *cccr; + struct sdio_embedded_func *funcs; + int num_funcs; + } embedded_sdio_data; +#endif + unsigned long private[0] ____cacheline_aligned; }; @@ -371,6 +380,14 @@ void mmc_remove_host(struct mmc_host *); void mmc_free_host(struct mmc_host *); int mmc_of_parse(struct mmc_host *host); +#ifdef CONFIG_MMC_EMBEDDED_SDIO +extern void mmc_set_embedded_sdio_data(struct mmc_host *host, + struct sdio_cis *cis, + struct sdio_cccr *cccr, + struct sdio_embedded_func *funcs, + int num_funcs); +#endif + static inline void *mmc_priv(struct mmc_host *host) { return (void *)host->private; diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index 50f0bc952328..54600e927b06 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -22,6 +22,14 @@ struct sdio_func; typedef void (sdio_irq_handler_t)(struct sdio_func *); +/* + * Structure used to hold embedded SDIO device data from platform layer + */ +struct sdio_embedded_func { + uint8_t f_class; + uint32_t f_maxblksize; +}; + /* * SDIO function CIS tuple (unknown to the core) */ From 06c11cba796565240c4a1d14cb798cb4b25a2236 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 11 Nov 2008 11:22:38 -0800 Subject: [PATCH 039/358] trout: Add functions for WiFi --- drivers/mmc/core/sdio_io.c | 33 +++++++++++++++++++++++++++++++++ include/linux/mmc/sdio_func.h | 2 ++ 2 files changed, 35 insertions(+) mode change 100644 => 100755 drivers/mmc/core/sdio_io.c mode change 100644 => 100755 include/linux/mmc/sdio_func.h diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c old mode 100644 new mode 100755 index 78cb4d5d9d58..8fdeb07723a6 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -383,6 +383,39 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret) } EXPORT_SYMBOL_GPL(sdio_readb); +/** + * sdio_readb_ext - read a single byte from a SDIO function + * @func: SDIO function to access + * @addr: address to read + * @err_ret: optional status value from transfer + * @in: value to add to argument + * + * Reads a single byte from the address space of a given SDIO + * function. If there is a problem reading the address, 0xff + * is returned and @err_ret will contain the error code. + */ +unsigned char sdio_readb_ext(struct sdio_func *func, unsigned int addr, + int *err_ret, unsigned in) +{ + int ret; + unsigned char val; + + BUG_ON(!func); + + if (err_ret) + *err_ret = 0; + + ret = mmc_io_rw_direct(func->card, 0, func->num, addr, (u8)in, &val); + if (ret) { + if (err_ret) + *err_ret = ret; + return 0xFF; + } + + return val; +} +EXPORT_SYMBOL_GPL(sdio_readb_ext); + /** * sdio_writeb - write a single byte to a SDIO function * @func: SDIO function to access diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h old mode 100644 new mode 100755 index 54600e927b06..dc680c4b50d4 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -138,6 +138,8 @@ extern int sdio_release_irq(struct sdio_func *func); extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz); extern u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret); +extern u8 sdio_readb_ext(struct sdio_func *func, unsigned int addr, int *err_ret, + unsigned in); extern u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret); extern u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret); From ec3730bc563c33e6f326c8497876a855b3cfe8b5 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 15 May 2008 09:15:37 -0700 Subject: [PATCH 040/358] mmc: Add new API call 'sdio_reset_comm' for resetting communication with an SDIO device Signed-off-by: San Mehat --- drivers/mmc/core/sdio.c | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 360cd7109f74..0032e0ec9b6c 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1274,3 +1274,60 @@ err: return err; } +int sdio_reset_comm(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + u32 ocr; + int err; + + printk("%s():\n", __func__); + mmc_go_idle(host); + + mmc_set_clock(host, host->f_min); + + err = mmc_send_io_op_cond(host, 0, &ocr); + if (err) + goto err; + + host->ocr = mmc_select_voltage(host, ocr); + if (!host->ocr) { + err = -EINVAL; + goto err; + } + + err = mmc_send_io_op_cond(host, host->ocr, &ocr); + if (err) + goto err; + + if (mmc_host_is_spi(host)) { + err = mmc_spi_set_crc(host, use_spi_crc); + if (err) + goto err; + } + + if (!mmc_host_is_spi(host)) { + err = mmc_send_relative_addr(host, &card->rca); + if (err) + goto err; + mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); + } + if (!mmc_host_is_spi(host)) { + err = mmc_select_card(card); + if (err) + goto err; + } + + mmc_set_clock(host, card->cis.max_dtr); + err = sdio_enable_wide(card); + if (err) + goto err; + + return 0; + err: + printk("%s: Error resetting SDIO communications (%d)\n", + mmc_hostname(host), err); + return err; +} +EXPORT_SYMBOL(sdio_reset_comm); + + From b0c5c06400af391f7c56dc773e71c771346a167b Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 1 Dec 2008 08:52:34 -0800 Subject: [PATCH 041/358] mmc: sd: When resuming, try a little harder to init the card Signed-off-by: San Mehat --- drivers/mmc/core/sd.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 2c3020c03fb5..cb12ec11b6d6 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1145,6 +1145,9 @@ static int mmc_sd_suspend(struct mmc_host *host) static int _mmc_sd_resume(struct mmc_host *host) { int err = 0; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries; +#endif BUG_ON(!host); BUG_ON(!host->card); @@ -1155,7 +1158,23 @@ static int _mmc_sd_resume(struct mmc_host *host) goto out; mmc_power_up(host, host->card->ocr); +#ifdef CONFIG_MMC_PARANOID_SD_INIT + retries = 5; + while (retries) { + err = mmc_sd_init_card(host, host->card->ocr, host->card); + + if (err) { + printk(KERN_ERR "%s: Re-init card rc = %d (retries = %d)\n", + mmc_hostname(host), err, retries); + mdelay(5); + retries--; + continue; + } + break; + } +#else err = mmc_sd_init_card(host, host->card->ocr, host->card); +#endif mmc_card_clr_suspended(host->card); out: From 0ee3492a106444bfa088fc4582f9aad1b5bdfb33 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 4 Dec 2008 11:18:00 -0800 Subject: [PATCH 042/358] mmc: sd: Add retries in re-detection Signed-off-by: San Mehat mmc: sd: Remove debugging printk Signed-off-by: Dima Zavin --- drivers/mmc/core/sd.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index cb12ec11b6d6..9d016f6e29df 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1073,7 +1073,10 @@ static int mmc_sd_alive(struct mmc_host *host) */ static void mmc_sd_detect(struct mmc_host *host) { - int err; + int err = 0; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries = 5; +#endif BUG_ON(!host); BUG_ON(!host->card); @@ -1083,7 +1086,23 @@ static void mmc_sd_detect(struct mmc_host *host) /* * Just check if our card has been removed. */ +#ifdef CONFIG_MMC_PARANOID_SD_INIT + while(retries) { + err = mmc_send_status(host->card, NULL); + if (err) { + retries--; + udelay(5); + continue; + } + break; + } + if (!retries) { + printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n", + __func__, mmc_hostname(host), err); + } +#else err = _mmc_detect_card_removed(host); +#endif mmc_put_card(host->card); @@ -1287,6 +1306,9 @@ int mmc_attach_sd(struct mmc_host *host) { int err; u32 ocr, rocr; +#ifdef CONFIG_MMC_PARANOID_SD_INIT + int retries; +#endif BUG_ON(!host); WARN_ON(!host->claimed); @@ -1323,9 +1345,27 @@ int mmc_attach_sd(struct mmc_host *host) /* * Detect and init the card. */ +#ifdef CONFIG_MMC_PARANOID_SD_INIT + retries = 5; + while (retries) { + err = mmc_sd_init_card(host, rocr, NULL); + if (err) { + retries--; + continue; + } + break; + } + + if (!retries) { + printk(KERN_ERR "%s: mmc_sd_init_card() failure (err = %d)\n", + mmc_hostname(host), err); + goto err; + } +#else err = mmc_sd_init_card(host, rocr, NULL); if (err) goto err; +#endif mmc_release_host(host); err = mmc_add_card(host->card); From 08054072af22f5220571e4408ca27ea8e96a33e1 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 23 Mar 2009 12:20:37 -0700 Subject: [PATCH 043/358] mmc: core: Hold a wake lock accross delayed work + mmc rescan Signed-off-by: San Mehat mmc: core: Rework mmc_delayed_work wakelock so that the wakelock is only extended if a card is added or removed. Signed-off-by: San Mehat --- drivers/mmc/core/core.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index e4bb25efcba6..efa7be8f6453 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,7 @@ #define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */ static struct workqueue_struct *workqueue; +static struct wake_lock mmc_delayed_work_wake_lock; static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; /* @@ -87,6 +89,7 @@ MODULE_PARM_DESC( static int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) { + wake_lock(&mmc_delayed_work_wake_lock); return queue_delayed_work(workqueue, work, delay); } @@ -2420,6 +2423,7 @@ void mmc_rescan(struct work_struct *work) struct mmc_host *host = container_of(work, struct mmc_host, detect.work); int i; + bool extend_wakelock = false; if (host->rescan_disable) return; @@ -2470,14 +2474,20 @@ void mmc_rescan(struct work_struct *work) mmc_claim_host(host); for (i = 0; i < ARRAY_SIZE(freqs); i++) { - if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) + if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) { + extend_wakelock = true; break; + } if (freqs[i] <= host->f_min) break; } mmc_release_host(host); out: + if (extend_wakelock) + wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2); + else + wake_unlock(&mmc_delayed_work_wake_lock); if (host->caps & MMC_CAP_NEEDS_POLL) mmc_schedule_delayed_work(&host->detect, HZ); } @@ -2736,6 +2746,9 @@ static int __init mmc_init(void) if (!workqueue) return -ENOMEM; + wake_lock_init(&mmc_delayed_work_wake_lock, WAKE_LOCK_SUSPEND, + "mmc_delayed_work"); + ret = mmc_register_bus(); if (ret) goto destroy_workqueue; @@ -2756,6 +2769,7 @@ unregister_bus: mmc_unregister_bus(); destroy_workqueue: destroy_workqueue(workqueue); + wake_lock_destroy(&mmc_delayed_work_wake_lock); return ret; } @@ -2766,6 +2780,7 @@ static void __exit mmc_exit(void) mmc_unregister_host_class(); mmc_unregister_bus(); destroy_workqueue(workqueue); + wake_lock_destroy(&mmc_delayed_work_wake_lock); } subsys_initcall(mmc_init); From 4f4670678426135458fb5d80c8de71afb0cee2b9 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 30 Jul 2009 08:21:19 -0700 Subject: [PATCH 044/358] mmc: mmcblk: Add support for deferred SD bus resume Signed-off-by: San Mehat mmc: card: Add MMC_BLOCK_DEFERRED_RESUME option to Kconfig Signed-off-by: San Mehat --- drivers/mmc/card/Kconfig | 9 +++++++++ drivers/mmc/card/block.c | 16 ++++++++++++++++ drivers/mmc/core/core.c | 6 ++++++ 3 files changed, 31 insertions(+) diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig index 5562308699bc..79d82124413f 100644 --- a/drivers/mmc/card/Kconfig +++ b/drivers/mmc/card/Kconfig @@ -50,6 +50,15 @@ config MMC_BLOCK_BOUNCE If unsure, say Y here. +config MMC_BLOCK_DEFERRED_RESUME + bool "Deferr MMC layer resume until I/O is requested" + depends on MMC_BLOCK + default n + help + Say Y here to enable deferred MMC resume until I/O + is requested. This will reduce overall resume latency and + save power when theres an SD card inserted but not being used. + config SDIO_UART tristate "SDIO UART/GPS class support" depends on TTY diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 5bec76dd35bd..835d2100bcc8 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1948,6 +1948,9 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) return 0; } +static int +mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card); + static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { int ret; @@ -1957,6 +1960,13 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) unsigned long flags; unsigned int cmd_flags = req ? req->cmd_flags : 0; +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + if (mmc_bus_needs_resume(card->host)) { + mmc_resume_bus(card->host); + mmc_blk_set_blksize(md, card); + } +#endif + if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ mmc_get_card(card); @@ -2396,6 +2406,9 @@ static int mmc_blk_probe(struct mmc_card *card) mmc_set_drvdata(card, md); mmc_fixup_device(card, blk_fixups); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + mmc_set_bus_resume_policy(card->host, 1); +#endif if (mmc_add_disk(md)) goto out; @@ -2438,6 +2451,9 @@ static void mmc_blk_remove(struct mmc_card *card) pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); mmc_set_drvdata(card, NULL); +#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + mmc_set_bus_resume_policy(card->host, 0); +#endif } static int _mmc_blk_suspend(struct mmc_card *card) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index efa7be8f6453..e02d9bb10125 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2445,6 +2445,12 @@ void mmc_rescan(struct work_struct *work) host->detect_change = 0; + /* If the card was removed the bus will be marked + * as dead - extend the wakelock so userspace + * can respond */ + if (host->bus_dead) + extend_wakelock = 1; + /* * Let mmc_bus_put() free the bus/bus_ops if we've found that * the card is no longer present. From 92b61cdbcbac8ce15747e16c4736ebf862a37b71 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 29 Jul 2009 10:22:03 -0700 Subject: [PATCH 045/358] mmc: sdio: Claim host in sdio_reset_comm() Signed-off-by: Dmitry Shmidt --- drivers/mmc/core/sdio.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 0032e0ec9b6c..c335b4a28952 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -1281,6 +1282,8 @@ int sdio_reset_comm(struct mmc_card *card) int err; printk("%s():\n", __func__); + mmc_claim_host(host); + mmc_go_idle(host); mmc_set_clock(host, host->f_min); @@ -1321,13 +1324,12 @@ int sdio_reset_comm(struct mmc_card *card) err = sdio_enable_wide(card); if (err) goto err; - + mmc_release_host(host); return 0; - err: +err: printk("%s: Error resetting SDIO communications (%d)\n", mmc_hostname(host), err); + mmc_release_host(host); return err; } EXPORT_SYMBOL(sdio_reset_comm); - - From eab08317448c98291ddb6b644243b3c32bbc00d4 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Wed, 9 Dec 2009 09:45:36 -0800 Subject: [PATCH 046/358] mmc: sdio: Add high speed support to sdio_reset_comm() Signed-off-by: San Mehat --- drivers/mmc/core/sdio.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index c335b4a28952..86ea711d9bd1 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1320,7 +1320,28 @@ int sdio_reset_comm(struct mmc_card *card) goto err; } - mmc_set_clock(host, card->cis.max_dtr); + /* + * Switch to high-speed (if supported). + */ + err = sdio_enable_hs(card); + if (err) + goto err; + + /* + * Change to the card's maximum speed. + */ + if (mmc_card_highspeed(card)) { + /* + * The SDIO specification doesn't mention how + * the CIS transfer speed register relates to + * high-speed, but it seems that 50 MHz is + * mandatory. + */ + mmc_set_clock(host, 50000000); + } else { + mmc_set_clock(host, card->cis.max_dtr); + } + err = sdio_enable_wide(card); if (err) goto err; From fbfb4ccff0d83a95d732f5ea0aa1e4dfe611343d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 6 Oct 2010 17:25:02 -0700 Subject: [PATCH 047/358] mmc: sdio: Fix enable_hs and enable_wide in sdio_reset_comm() Signed-off-by: Dmitry Shmidt --- drivers/mmc/core/sdio.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 86ea711d9bd1..413063b04d5f 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1305,7 +1305,7 @@ int sdio_reset_comm(struct mmc_card *card) if (mmc_host_is_spi(host)) { err = mmc_spi_set_crc(host, use_spi_crc); if (err) - goto err; + goto err; } if (!mmc_host_is_spi(host)) { @@ -1324,27 +1324,22 @@ int sdio_reset_comm(struct mmc_card *card) * Switch to high-speed (if supported). */ err = sdio_enable_hs(card); - if (err) + if (err > 0) + mmc_sd_go_highspeed(card); + else if (err) goto err; /* * Change to the card's maximum speed. */ - if (mmc_card_highspeed(card)) { - /* - * The SDIO specification doesn't mention how - * the CIS transfer speed register relates to - * high-speed, but it seems that 50 MHz is - * mandatory. - */ - mmc_set_clock(host, 50000000); - } else { - mmc_set_clock(host, card->cis.max_dtr); - } + mmc_set_clock(host, mmc_sdio_get_max_clock(card)); - err = sdio_enable_wide(card); - if (err) + err = sdio_enable_4bit_bus(card); + if (err > 0) + mmc_set_bus_width(host, MMC_BUS_WIDTH_4); + else if (err) goto err; + mmc_release_host(host); return 0; err: From 95fa148bfbc413b6a49851f5cf5033c5acb89f18 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 7 Oct 2010 14:39:16 -0700 Subject: [PATCH 048/358] mmc: Add "ignore mmc pm notify" functionality Signed-off-by: Dmitry Shmidt --- drivers/mmc/core/host.c | 7 +++++-- include/linux/mmc/pm.h | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 49bc403e31f0..cc3164a939eb 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -533,7 +533,8 @@ int mmc_add_host(struct mmc_host *host) mmc_host_clk_sysfs_init(host); mmc_start_host(host); - register_pm_notifier(&host->pm_notify); + if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + register_pm_notifier(&host->pm_notify); return 0; } @@ -550,7 +551,9 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { - unregister_pm_notifier(&host->pm_notify); + if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + unregister_pm_notifier(&host->pm_notify); + mmc_stop_host(host); #ifdef CONFIG_DEBUG_FS diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h index 4a139204c20c..6e2d6a135c7e 100644 --- a/include/linux/mmc/pm.h +++ b/include/linux/mmc/pm.h @@ -26,5 +26,6 @@ typedef unsigned int mmc_pm_flag_t; #define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ #define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ +#define MMC_PM_IGNORE_PM_NOTIFY (1 << 2) /* ignore mmc pm notify */ #endif /* LINUX_MMC_PM_H */ From 33dddf786cc93b4e42aaf1034b9b610165989c70 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 8 Apr 2011 22:20:53 -0700 Subject: [PATCH 049/358] Recreate asm/mach/mmc.h include file Change-Id: I9f10244b0603f7842b8504a16124d40dc4a71ed2 Signed-off-by: Colin Cross --- arch/arm/include/asm/mach/mmc.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 arch/arm/include/asm/mach/mmc.h diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h new file mode 100644 index 000000000000..f8d391ad9203 --- /dev/null +++ b/arch/arm/include/asm/mach/mmc.h @@ -0,0 +1,27 @@ +/* + * arch/arm/include/asm/mach/mmc.h + */ +#ifndef ASMARM_MACH_MMC_H +#define ASMARM_MACH_MMC_H + +#include +#include +#include + +struct embedded_sdio_data { + struct sdio_cis cis; + struct sdio_cccr cccr; + struct sdio_embedded_func *funcs; + int num_funcs; +}; + +struct mmc_platform_data { + unsigned int ocr_mask; /* available voltages */ + int built_in; /* built-in device flag */ + u32 (*translate_vdd)(struct device *, unsigned int); + unsigned int (*status)(struct device *); + struct embedded_sdio_data *embedded_sdio; + int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); +}; + +#endif From 7e46e35c3ea614879baf48e6349462a894c03a96 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 3 May 2011 11:05:04 -0700 Subject: [PATCH 050/358] ARM: Add 'card_present' state to mmc_platfrom_data Signed-off-by: Dmitry Shmidt --- arch/arm/include/asm/mach/mmc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h index f8d391ad9203..bca864ac945f 100644 --- a/arch/arm/include/asm/mach/mmc.h +++ b/arch/arm/include/asm/mach/mmc.h @@ -18,6 +18,7 @@ struct embedded_sdio_data { struct mmc_platform_data { unsigned int ocr_mask; /* available voltages */ int built_in; /* built-in device flag */ + int card_present; /* card detect state */ u32 (*translate_vdd)(struct device *, unsigned int); unsigned int (*status)(struct device *); struct embedded_sdio_data *embedded_sdio; From 42c5a022d936961d1622c5db66b2b318e642d5f6 Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Tue, 25 Oct 2011 18:16:58 -0700 Subject: [PATCH 051/358] mmc: block: Improve logging of handling emmc timeouts Add some logging to make it clear just how the emmc timeout was handled. Change-Id: Id33fd28d8b9778dc4e85db829e2637a328eddab4 Signed-off-by: Ken Sumrall --- drivers/mmc/card/block.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 835d2100bcc8..717614933016 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -767,18 +767,22 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error, req->rq_disk->disk_name, "timed out", name, status); /* If the status cmd initially failed, retry the r/w cmd */ - if (!status_valid) + if (!status_valid) { + pr_err("%s: status not valid, retrying timeout\n", req->rq_disk->disk_name); return ERR_RETRY; - + } /* * If it was a r/w cmd crc error, or illegal command * (eg, issued in wrong state) then retry - we should * have corrected the state problem above. */ - if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) + if (status & (R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND)) { + pr_err("%s: command error, retrying timeout\n", req->rq_disk->disk_name); return ERR_RETRY; + } /* Otherwise abort the command */ + pr_err("%s: not retrying timeout\n", req->rq_disk->disk_name); return ERR_ABORT; default: From 4a8ed9351b772ead597c5dcb587e90cd0859e27d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 18 Dec 2008 09:55:20 -0800 Subject: [PATCH 052/358] tiwlan: Add abstract wifi control functions support --- include/linux/wifi_tiwlan.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 include/linux/wifi_tiwlan.h diff --git a/include/linux/wifi_tiwlan.h b/include/linux/wifi_tiwlan.h new file mode 100644 index 000000000000..5897e726c4f9 --- /dev/null +++ b/include/linux/wifi_tiwlan.h @@ -0,0 +1,32 @@ +/* include/linux/wifi_tiwlan.h + * + * Copyright (C) 2008 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_WIFI_TIWLAN_H_ +#define _LINUX_WIFI_TIWLAN_H_ + +#define WMPA_NUMBER_OF_SECTIONS 3 +#define WMPA_NUMBER_OF_BUFFERS 160 +#define WMPA_SECTION_HEADER 24 +#define WMPA_SECTION_SIZE_0 (WMPA_NUMBER_OF_BUFFERS * 64) +#define WMPA_SECTION_SIZE_1 (WMPA_NUMBER_OF_BUFFERS * 256) +#define WMPA_SECTION_SIZE_2 (WMPA_NUMBER_OF_BUFFERS * 2048) + +struct wifi_platform_data { + int (*set_power)(int val); + int (*set_reset)(int val); + int (*set_carddetect)(int val); + void *(*mem_prealloc)(int section, unsigned long size); +}; + +#endif From 16586560b6b0c6035f02c5a73929029b248cf348 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 21 Mar 2009 18:48:54 -0700 Subject: [PATCH 053/358] fs: block_dump: Don't display inode changes if block_dump < 2 Signed-off-by: San Mehat --- fs/fs-writeback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index d754e3cf99a8..5584bea88a77 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1141,7 +1141,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) if ((inode->i_state & flags) == flags) return; - if (unlikely(block_dump)) + if (unlikely(block_dump > 1)) block_dump___mark_inode_dirty(inode); spin_lock(&inode->i_lock); From 52c46e9afacbcdec587d44f598ef407ff0fe1169 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 8 May 2009 04:02:40 +0800 Subject: [PATCH 054/358] net: add PPP on L2TP Access Concentrator (PPPoLAC) driver. Change-Id: I3ae3ee7520951ae24269db0ef2898c6455cf6bcc Signed-off-by: Chia-chi Yeh ppolac: dont include px_proto define in if_pppolac.h Change-Id: I55bc9cf91ea0e9e8f7bf5d6e241d188e1269343a Signed-off-by: Dima Zavin --- drivers/net/ppp/Kconfig | 9 + drivers/net/ppp/Makefile | 1 + drivers/net/ppp/pppolac.c | 359 ++++++++++++++++++++++++++++++++++ include/linux/if_pppolac.h | 33 ++++ include/linux/if_pppox.h | 9 + include/uapi/linux/if_pppox.h | 4 +- 6 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ppp/pppolac.c create mode 100644 include/linux/if_pppolac.h diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig index 1373c6d7278d..b092b5e25c83 100644 --- a/drivers/net/ppp/Kconfig +++ b/drivers/net/ppp/Kconfig @@ -149,6 +149,15 @@ config PPPOL2TP tunnels. L2TP is replacing PPTP for VPN uses. if TTY +config PPPOLAC + tristate "PPP on L2TP Access Concentrator" + depends on PPP && INET + help + L2TP (RFC 2661) is a tunneling protocol widely used in virtual private + networks. This driver handles L2TP data packets between a UDP socket + and a PPP channel, but only permits one session per socket. Thus it is + fairly simple and suited for clients. + config PPP_ASYNC tristate "PPP support for async serial ports" depends on PPP diff --git a/drivers/net/ppp/Makefile b/drivers/net/ppp/Makefile index a6b6297b0066..f14406e0c388 100644 --- a/drivers/net/ppp/Makefile +++ b/drivers/net/ppp/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o obj-$(CONFIG_PPPOE) += pppox.o pppoe.o obj-$(CONFIG_PPPOL2TP) += pppox.o obj-$(CONFIG_PPTP) += pppox.o pptp.o +obj-$(CONFIG_PPPOLAC) += pppox.o pppolac.o diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c new file mode 100644 index 000000000000..8843a9d30911 --- /dev/null +++ b/drivers/net/ppp/pppolac.c @@ -0,0 +1,359 @@ +/* drivers/net/pppolac.c + * + * Driver for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) + * + * Copyright (C) 2009 Google, Inc. + * Author: Chia-chi Yeh + * + * 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. + */ + +/* This driver handles L2TP data packets between a UDP socket and a PPP channel. + * To keep things simple, only one session per socket is permitted. Packets are + * sent via the socket, so it must keep connected to the same address. One must + * not set sequencing in ICCN but let LNS controll it. Currently this driver + * only works on IPv4 due to the lack of UDP encapsulation support in IPv6. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define L2TP_CONTROL_MASK 0x80 +#define L2TP_VERSION_MASK 0x0F +#define L2TP_VERSION 0x02 +#define L2TP_LENGTH_MASK 0x40 +#define L2TP_OFFSET_MASK 0x02 +#define L2TP_SEQUENCE_MASK 0x08 + +#define PPP_ADDR 0xFF +#define PPP_CTRL 0x03 + +union unaligned { + __u32 u32; +} __attribute__((packed)); + +static inline union unaligned *unaligned(void *ptr) +{ + return (union unaligned *)ptr; +} + +static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb) +{ + struct sock *sk; + struct pppolac_opt *opt; + __u8 bits; + __u8 *ptr; + + /* Drop the packet if it is too short. */ + if (skb->len < sizeof(struct udphdr) + 6) + goto drop; + + /* Put it back if it is a control packet. */ + if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_MASK) + return 1; + + /* Now the packet is ours. Skip UDP header. */ + skb_pull(skb, sizeof(struct udphdr)); + + /* Check the version. */ + if ((skb->data[1] & L2TP_VERSION_MASK) != L2TP_VERSION) + goto drop; + bits = skb->data[0]; + ptr = &skb->data[2]; + + /* Check the length if it is present. */ + if (bits & L2TP_LENGTH_MASK) { + if ((ptr[0] << 8 | ptr[1]) != skb->len) + goto drop; + ptr += 2; + } + + /* Skip all fields including optional ones. */ + if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_MASK ? 4 : 0) + + (bits & L2TP_LENGTH_MASK ? 2 : 0) + + (bits & L2TP_OFFSET_MASK ? 2 : 0))) + goto drop; + + /* Skip the offset padding if it is present. */ + if (bits & L2TP_OFFSET_MASK && + !skb_pull(skb, skb->data[-2] << 8 | skb->data[-1])) + goto drop; + + /* Now ptr is pointing to the tunnel and skb is pointing to the payload. + * We have to lock sk_udp to prevent sk from being closed. */ + lock_sock(sk_udp); + sk = sk_udp->sk_user_data; + if (!sk) { + release_sock(sk_udp); + goto drop; + } + sock_hold(sk); + release_sock(sk_udp); + opt = &pppox_sk(sk)->proto.lac; + + /* Check the tunnel and the session. */ + if (unaligned(ptr)->u32 != opt->local) { + sock_put(sk); + goto drop; + } + + /* Check the sequence if it is present. According to RFC 2661 page 10 + * and 43, the only thing to do is updating opt->sequencing. */ + opt->sequencing = bits & L2TP_SEQUENCE_MASK; + + /* Skip PPP address and control if they are present. */ + if (skb->len >= 2 && skb->data[0] == PPP_ADDR && + skb->data[1] == PPP_CTRL) + skb_pull(skb, 2); + + /* Fix PPP protocol if it is compressed. */ + if (skb->len >= 1 && skb->data[0] & 1) + skb_push(skb, 1)[0] = 0; + + /* Finally, deliver the packet to PPP channel. We have to lock sk to + * prevent another thread from calling pppox_unbind_sock(). */ + skb_orphan(skb); + lock_sock(sk); + ppp_input(&pppox_sk(sk)->chan, skb); + release_sock(sk); + sock_put(sk); + return 0; + +drop: + kfree_skb(skb); + return 0; +} + +static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + struct sock *sk_udp = (struct sock *)chan->private; + struct pppolac_opt *opt = &pppox_sk(sk_udp->sk_user_data)->proto.lac; + struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; + struct kvec iov; + + /* Install PPP address and control. */ + skb_push(skb, 2); + skb->data[0] = PPP_ADDR; + skb->data[1] = PPP_CTRL; + + /* Install L2TP header. */ + if (opt->sequencing) { + skb_push(skb, 10); + skb->data[0] = L2TP_SEQUENCE_MASK; + skb->data[6] = opt->sequence >> 8; + skb->data[7] = opt->sequence; + skb->data[8] = 0; + skb->data[9] = 0; + opt->sequence++; + } else { + skb_push(skb, 6); + skb->data[0] = 0; + } + skb->data[1] = L2TP_VERSION; + unaligned(&skb->data[2])->u32 = opt->remote; + + /* Now send the packet via UDP socket. */ + iov.iov_base = skb->data; + iov.iov_len = skb->len; + kernel_sendmsg(sk_udp->sk_socket, &msg, &iov, 1, skb->len); + kfree_skb(skb); + return 1; +} + +/******************************************************************************/ + +static struct ppp_channel_ops pppolac_channel_ops = { + .start_xmit = pppolac_xmit, +}; + +static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, + int addrlen, int flags) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po = pppox_sk(sk); + struct sockaddr_pppolac *addr = (struct sockaddr_pppolac *)useraddr; + struct socket *sock_udp = NULL; + struct sock *sk_udp; + int error; + + if (addrlen != sizeof(struct sockaddr_pppolac) || + !addr->local.tunnel || !addr->local.session || + !addr->remote.tunnel || !addr->remote.session) { + return -EINVAL; + } + + lock_sock(sk); + error = -EALREADY; + if (sk->sk_state != PPPOX_NONE) + goto out; + + sock_udp = sockfd_lookup(addr->udp_socket, &error); + if (!sock_udp) + goto out; + sk_udp = sock_udp->sk; + lock_sock(sk_udp); + + /* Remove this check when IPv6 supports UDP encapsulation. */ + error = -EAFNOSUPPORT; + if (sk_udp->sk_family != AF_INET) + goto out; + error = -EPROTONOSUPPORT; + if (sk_udp->sk_protocol != IPPROTO_UDP) + goto out; + error = -EDESTADDRREQ; + if (sk_udp->sk_state != TCP_ESTABLISHED) + goto out; + error = -EBUSY; + if (udp_sk(sk_udp)->encap_type || sk_udp->sk_user_data) + goto out; + + po->chan.hdrlen = 12; + po->chan.private = sk_udp; + po->chan.ops = &pppolac_channel_ops; + po->chan.mtu = PPP_MTU - 80; + po->proto.lac.local = unaligned(&addr->local)->u32; + po->proto.lac.remote = unaligned(&addr->remote)->u32; + + error = ppp_register_channel(&po->chan); + if (error) + goto out; + + sk->sk_state = PPPOX_CONNECTED; + udp_sk(sk_udp)->encap_type = UDP_ENCAP_L2TPINUDP; + udp_sk(sk_udp)->encap_rcv = pppolac_recv; + sk_udp->sk_user_data = sk; + +out: + if (sock_udp) { + release_sock(sk_udp); + if (error) + sockfd_put(sock_udp); + } + release_sock(sk); + return error; +} + +static int pppolac_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (!sk) + return 0; + + lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD)) { + release_sock(sk); + return -EBADF; + } + + if (sk->sk_state != PPPOX_NONE) { + struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private; + lock_sock(sk_udp); + + pppox_unbind_sock(sk); + sk_udp->sk_user_data = NULL; + udp_sk(sk_udp)->encap_type = 0; + udp_sk(sk_udp)->encap_rcv = NULL; + + release_sock(sk_udp); + sockfd_put(sk_udp->sk_socket); + } + + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); + sock_put(sk); + return 0; +} + +/******************************************************************************/ + +static struct proto pppolac_proto = { + .name = "PPPOLAC", + .owner = THIS_MODULE, + .obj_size = sizeof(struct pppox_sock), +}; + +static struct proto_ops pppolac_proto_ops = { + .family = PF_PPPOX, + .owner = THIS_MODULE, + .release = pppolac_release, + .bind = sock_no_bind, + .connect = pppolac_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = sock_no_poll, + .ioctl = pppox_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .mmap = sock_no_mmap, +}; + +static int pppolac_create(struct net *net, struct socket *sock) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppolac_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sock->state = SS_UNCONNECTED; + sock->ops = &pppolac_proto_ops; + sk->sk_protocol = PX_PROTO_OLAC; + sk->sk_state = PPPOX_NONE; + return 0; +} + +/******************************************************************************/ + +static struct pppox_proto pppolac_pppox_proto = { + .create = pppolac_create, + .owner = THIS_MODULE, +}; + +static int __init pppolac_init(void) +{ + int error; + + error = proto_register(&pppolac_proto, 0); + if (error) + return error; + + error = register_pppox_proto(PX_PROTO_OLAC, &pppolac_pppox_proto); + if (error) + proto_unregister(&pppolac_proto); + return error; +} + +static void __exit pppolac_exit(void) +{ + unregister_pppox_proto(PX_PROTO_OLAC); + proto_unregister(&pppolac_proto); +} + +module_init(pppolac_init); +module_exit(pppolac_exit); + +MODULE_DESCRIPTION("PPP on L2TP Access Concentrator (PPPoLAC)"); +MODULE_AUTHOR("Chia-chi Yeh "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/if_pppolac.h b/include/linux/if_pppolac.h new file mode 100644 index 000000000000..c06bd6c8ba26 --- /dev/null +++ b/include/linux/if_pppolac.h @@ -0,0 +1,33 @@ +/* include/linux/if_pppolac.h + * + * Header for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) + * + * Copyright (C) 2009 Google, Inc. + * Author: Chia-chi Yeh + * + * 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_IF_PPPOLAC_H +#define __LINUX_IF_PPPOLAC_H + +#include +#include + +struct sockaddr_pppolac { + sa_family_t sa_family; /* AF_PPPOX */ + unsigned int sa_protocol; /* PX_PROTO_OLAC */ + int udp_socket; + struct __attribute__((packed)) { + __u16 tunnel, session; + } local, remote; +} __attribute__((packed)); + +#endif /* __LINUX_IF_PPPOLAC_H */ diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index aff7ad8a4ea3..28be91e30403 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -41,6 +41,14 @@ struct pptp_opt { u32 seq_sent, seq_recv; int ppp_flags; }; + +struct pppolac_opt { + __u32 local; + __u32 remote; + __u16 sequence; + __u8 sequencing; +}; + #include struct pppox_sock { @@ -51,6 +59,7 @@ struct pppox_sock { union { struct pppoe_opt pppoe; struct pptp_opt pptp; + struct pppolac_opt lac; } proto; __be16 num; }; diff --git a/include/uapi/linux/if_pppox.h b/include/uapi/linux/if_pppox.h index e128769331b5..8cf7e1c85345 100644 --- a/include/uapi/linux/if_pppox.h +++ b/include/uapi/linux/if_pppox.h @@ -23,6 +23,7 @@ #include #include #include +#include /* For user-space programs to pick up these definitions * which they wouldn't get otherwise without defining __KERNEL__ @@ -56,7 +57,8 @@ struct pptp_addr { #define PX_PROTO_OE 0 /* Currently just PPPoE */ #define PX_PROTO_OL2TP 1 /* Now L2TP also */ #define PX_PROTO_PPTP 2 -#define PX_MAX_PROTO 3 +#define PX_PROTO_OLAC 3 +#define PX_MAX_PROTO 4 struct sockaddr_pppox { __kernel_sa_family_t sa_family; /* address family, AF_PPPOX */ From c80c1b3740f87bfaaf1b7d204539840983dd07dc Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 12 Jun 2009 01:09:30 +0800 Subject: [PATCH 055/358] net: add PPP on PPTP Network Server (PPPoPNS) driver. Signed-off-by: Chia-chi Yeh ppopns: dont include px_proto define in if_pppopns.h Change-Id: I27e687667db5b45182562f4a517a2e6cec6b1350 Signed-off-by: Dima Zavin --- drivers/net/ppp/Kconfig | 8 + drivers/net/ppp/Makefile | 1 + drivers/net/ppp/pppopns.c | 322 ++++++++++++++++++++++++++++++++++ include/linux/if_pppopns.h | 32 ++++ include/linux/if_pppox.h | 7 + include/uapi/linux/if_pppox.h | 4 +- 6 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ppp/pppopns.c create mode 100644 include/linux/if_pppopns.h diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig index b092b5e25c83..282aec4860eb 100644 --- a/drivers/net/ppp/Kconfig +++ b/drivers/net/ppp/Kconfig @@ -158,6 +158,14 @@ config PPPOLAC and a PPP channel, but only permits one session per socket. Thus it is fairly simple and suited for clients. +config PPPOPNS + tristate "PPP on PPTP Network Server" + depends on PPP && INET + help + PPTP (RFC 2637) is a tunneling protocol widely used in virtual private + networks. This driver handles PPTP data packets between a RAW socket + and a PPP channel. It is fairly simple and easy to use. + config PPP_ASYNC tristate "PPP support for async serial ports" depends on PPP diff --git a/drivers/net/ppp/Makefile b/drivers/net/ppp/Makefile index f14406e0c388..d283d03c4683 100644 --- a/drivers/net/ppp/Makefile +++ b/drivers/net/ppp/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_PPPOE) += pppox.o pppoe.o obj-$(CONFIG_PPPOL2TP) += pppox.o obj-$(CONFIG_PPTP) += pppox.o pptp.o obj-$(CONFIG_PPPOLAC) += pppox.o pppolac.o +obj-$(CONFIG_PPPOPNS) += pppox.o pppopns.o diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c new file mode 100644 index 000000000000..8885eba8968e --- /dev/null +++ b/drivers/net/ppp/pppopns.c @@ -0,0 +1,322 @@ +/* drivers/net/pppopns.c + * + * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) + * + * Copyright (C) 2009 Google, Inc. + * Author: Chia-chi Yeh + * + * 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. + */ + +/* This driver handles PPTP data packets between a RAW socket and a PPP channel. + * The socket is created in the kernel space and connected to the same address + * of the control socket. To keep things simple, packets are always sent with + * sequence but without acknowledgement. This driver should work on both IPv4 + * and IPv6. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GRE_HEADER_SIZE 8 + +#define PPTP_GRE_MASK htons(0x2001) +#define PPTP_GRE_SEQ_MASK htons(0x1000) +#define PPTP_GRE_ACK_MASK htons(0x0080) +#define PPTP_GRE_TYPE htons(0x880B) + +#define PPP_ADDR 0xFF +#define PPP_CTRL 0x03 + +struct header { + __u16 bits; + __u16 type; + __u16 length; + __u16 call; + __u32 sequence; +} __attribute__((packed)); + +static void pppopns_recv(struct sock *sk_raw, int length) +{ + struct sock *sk; + struct pppopns_opt *opt; + struct sk_buff *skb; + struct header *hdr; + + /* Lock sk_raw to prevent sk from being closed. */ + lock_sock(sk_raw); + sk = (struct sock *)sk_raw->sk_user_data; + if (!sk) { + release_sock(sk_raw); + return; + } + sock_hold(sk); + release_sock(sk_raw); + opt = &pppox_sk(sk)->proto.pns; + + /* Process packets from the receive queue. */ + while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) { + skb_pull(skb, skb_transport_header(skb) - skb->data); + + /* Drop the packet if it is too short. */ + if (skb->len < GRE_HEADER_SIZE) + goto drop; + + /* Check the header. */ + hdr = (struct header *)skb->data; + if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || + (hdr->bits & PPTP_GRE_MASK) != PPTP_GRE_MASK) + goto drop; + + /* Skip all fields including optional ones. */ + if (!skb_pull(skb, GRE_HEADER_SIZE + + (hdr->bits & PPTP_GRE_SEQ_MASK ? 4 : 0) + + (hdr->bits & PPTP_GRE_ACK_MASK ? 4 : 0))) + goto drop; + + /* Check the length. */ + if (skb->len != ntohs(hdr->length)) + goto drop; + + /* Skip PPP address and control if they are present. */ + if (skb->len >= 2 && skb->data[0] == PPP_ADDR && + skb->data[1] == PPP_CTRL) + skb_pull(skb, 2); + + /* Fix PPP protocol if it is compressed. */ + if (skb->len >= 1 && skb->data[0] & 1) + skb_push(skb, 1)[0] = 0; + + /* Deliver the packet to PPP channel. We have to lock sk to + * prevent another thread from calling pppox_unbind_sock(). */ + skb_orphan(skb); + lock_sock(sk); + ppp_input(&pppox_sk(sk)->chan, skb); + release_sock(sk); + continue; +drop: + kfree_skb(skb); + } + sock_put(sk); +} + +static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + struct sock *sk_raw = (struct sock *)chan->private; + struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns; + struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; + struct kvec iov; + struct header *hdr; + __u16 length; + + /* Install PPP address and control. */ + skb_push(skb, 2); + skb->data[0] = PPP_ADDR; + skb->data[1] = PPP_CTRL; + length = skb->len; + + /* Install PPTP GRE header. */ + hdr = (struct header *)skb_push(skb, 12); + hdr->bits = PPTP_GRE_MASK | PPTP_GRE_SEQ_MASK; + hdr->type = PPTP_GRE_TYPE; + hdr->length = htons(length); + hdr->call = opt->remote; + hdr->sequence = htonl(opt->sequence); + opt->sequence++; + + /* Now send the packet via RAW socket. */ + iov.iov_base = skb->data; + iov.iov_len = skb->len; + kernel_sendmsg(sk_raw->sk_socket, &msg, &iov, 1, skb->len); + kfree_skb(skb); + return 1; +} + +/******************************************************************************/ + +static struct ppp_channel_ops pppopns_channel_ops = { + .start_xmit = pppopns_xmit, +}; + +static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, + int addrlen, int flags) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po = pppox_sk(sk); + struct sockaddr_pppopns *addr = (struct sockaddr_pppopns *)useraddr; + struct sockaddr_storage ss; + struct socket *sock_tcp = NULL; + struct socket *sock_raw = NULL; + struct sock *sk_raw; + int error; + + if (addrlen != sizeof(struct sockaddr_pppopns)) + return -EINVAL; + + lock_sock(sk); + error = -EALREADY; + if (sk->sk_state != PPPOX_NONE) + goto out; + + sock_tcp = sockfd_lookup(addr->tcp_socket, &error); + if (!sock_tcp) + goto out; + error = -EPROTONOSUPPORT; + if (sock_tcp->sk->sk_protocol != IPPROTO_TCP) + goto out; + addrlen = sizeof(struct sockaddr_storage); + error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen); + if (error) + goto out; + + error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw); + if (error) + goto out; + error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0); + if (error) + goto out; + sk_raw = sock_raw->sk; + + po->chan.hdrlen = 14; + po->chan.private = sk_raw; + po->chan.ops = &pppopns_channel_ops; + po->chan.mtu = PPP_MTU - 80; + po->proto.pns.local = addr->local; + po->proto.pns.remote = addr->remote; + + error = ppp_register_channel(&po->chan); + if (error) + goto out; + + sk->sk_state = PPPOX_CONNECTED; + sk_raw->sk_user_data = sk; + sk_raw->sk_data_ready = pppopns_recv; + +out: + if (sock_tcp) + sockfd_put(sock_tcp); + if (error && sock_raw) + sock_release(sock_raw); + release_sock(sk); + return error; +} + +static int pppopns_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (!sk) + return 0; + + lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD)) { + release_sock(sk); + return -EBADF; + } + + if (sk->sk_state != PPPOX_NONE) { + struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; + lock_sock(sk_raw); + pppox_unbind_sock(sk); + sk_raw->sk_user_data = NULL; + release_sock(sk_raw); + sock_release(sk_raw->sk_socket); + } + + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); + sock_put(sk); + return 0; +} + +/******************************************************************************/ + +static struct proto pppopns_proto = { + .name = "PPPOPNS", + .owner = THIS_MODULE, + .obj_size = sizeof(struct pppox_sock), +}; + +static struct proto_ops pppopns_proto_ops = { + .family = PF_PPPOX, + .owner = THIS_MODULE, + .release = pppopns_release, + .bind = sock_no_bind, + .connect = pppopns_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = sock_no_poll, + .ioctl = pppox_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .mmap = sock_no_mmap, +}; + +static int pppopns_create(struct net *net, struct socket *sock) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sock->state = SS_UNCONNECTED; + sock->ops = &pppopns_proto_ops; + sk->sk_protocol = PX_PROTO_OPNS; + sk->sk_state = PPPOX_NONE; + return 0; +} + +/******************************************************************************/ + +static struct pppox_proto pppopns_pppox_proto = { + .create = pppopns_create, + .owner = THIS_MODULE, +}; + +static int __init pppopns_init(void) +{ + int error; + + error = proto_register(&pppopns_proto, 0); + if (error) + return error; + + error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto); + if (error) + proto_unregister(&pppopns_proto); + return error; +} + +static void __exit pppopns_exit(void) +{ + unregister_pppox_proto(PX_PROTO_OPNS); + proto_unregister(&pppopns_proto); +} + +module_init(pppopns_init); +module_exit(pppopns_exit); + +MODULE_DESCRIPTION("PPP on PPTP Network Server (PPPoPNS)"); +MODULE_AUTHOR("Chia-chi Yeh "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/if_pppopns.h b/include/linux/if_pppopns.h new file mode 100644 index 000000000000..0cf34b4d551f --- /dev/null +++ b/include/linux/if_pppopns.h @@ -0,0 +1,32 @@ +/* include/linux/if_pppopns.h + * + * Header for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) + * + * Copyright (C) 2009 Google, Inc. + * Author: Chia-chi Yeh + * + * 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_IF_PPPOPNS_H +#define __LINUX_IF_PPPOPNS_H + +#include +#include + +struct sockaddr_pppopns { + sa_family_t sa_family; /* AF_PPPOX */ + unsigned int sa_protocol; /* PX_PROTO_OPNS */ + int tcp_socket; + __u16 local; + __u16 remote; +} __attribute__((packed)); + +#endif /* __LINUX_IF_PPPOPNS_H */ diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index 28be91e30403..e53107fdf859 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -49,6 +49,12 @@ struct pppolac_opt { __u8 sequencing; }; +struct pppopns_opt { + __u16 local; + __u16 remote; + __u32 sequence; +}; + #include struct pppox_sock { @@ -60,6 +66,7 @@ struct pppox_sock { struct pppoe_opt pppoe; struct pptp_opt pptp; struct pppolac_opt lac; + struct pppopns_opt pns; } proto; __be16 num; }; diff --git a/include/uapi/linux/if_pppox.h b/include/uapi/linux/if_pppox.h index 8cf7e1c85345..5861d45bc51d 100644 --- a/include/uapi/linux/if_pppox.h +++ b/include/uapi/linux/if_pppox.h @@ -24,6 +24,7 @@ #include #include #include +#include /* For user-space programs to pick up these definitions * which they wouldn't get otherwise without defining __KERNEL__ @@ -58,7 +59,8 @@ struct pptp_addr { #define PX_PROTO_OL2TP 1 /* Now L2TP also */ #define PX_PROTO_PPTP 2 #define PX_PROTO_OLAC 3 -#define PX_MAX_PROTO 4 +#define PX_PROTO_OPNS 4 +#define PX_MAX_PROTO 5 struct sockaddr_pppox { __kernel_sa_family_t sa_family; /* address family, AF_PPPOX */ From d01b16094cd680ac69533c6a905338a3e810559e Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Sat, 13 Jun 2009 02:29:04 +0800 Subject: [PATCH 056/358] net: PPPoPNS and PPPoLAC fixes. net: Fix a bitmask in PPPoPNS and rename constants in PPPoPNS and PPPoLAC. Signed-off-by: Chia-chi Yeh net: Fix a potential deadlock while releasing PPPoLAC/PPPoPNS socket. PPP driver guarantees that no thread will be executing start_xmit() after returning from ppp_unregister_channel(). To achieve this, a spinlock (downl) is used. In pppolac_release(), ppp_unregister_channel() is called after sk_udp is locked. At the same time, another thread might be running in pppolac_xmit() with downl. Thus a deadlock will occur if the thread tries to lock sk_udp. The same situation might happen on sk_raw in pppopns_release(). Signed-off-by: Chia-chi Yeh net: Force PPPoLAC and PPPoPNS to bind an interface before creating PPP channel. It is common to manipulate the routing table after configuring PPP device. Since both PPPoLAC and PPPoPNS run over IP, care must be taken to make sure that there is no loop in the routing table. Although this can be done by adding a host route, it might still cause problems when the interface is down for some reason. To solve this, this patch forces both drivers to bind an interface before creating PPP channel, so the system will not re-route the tunneling sockets to another interface when the original one is down. Another benefit is that now the host route is no longer required, so there is no need to remove it when PPP channel is closed. Signed-off-by: Chia-chi Yeh net: Avoid sleep-inside-spinlock in PPPoLAC and PPPoPNS. Since recv() and xmit() are called with a spinlock held, routines which might sleep cannot be used. This issue is solved by following changes: Incoming packets are now processed in backlog handler, recv_core(), instead of recv(). Since backlog handler is always executed with socket spinlock held, the requirement of ppp_input() is still satisfied. Outgoing packets are now processed in workqueue handler, xmit_core(), instead of xmit(). Note that kernel_sendmsg() is no longer used to prevent touching dead sockets. In release(), lock_sock() and pppox_unbind_sock() ensure that no thread is in recv_core() or xmit(). Then socket handlers are restored before release_sock(), so no packets will leak in backlog queue. Signed-off-by: Chia-chi Yeh net: Fix msg_iovlen in PPPoLAC and PPPoPNS. Although any positive value should work (which is always true in both drivers), the correct value should be 1. Signed-off-by: Chia-chi Yeh --- drivers/net/ppp/pppolac.c | 129 ++++++++++++++++------------- drivers/net/ppp/pppopns.c | 165 +++++++++++++++++++++++--------------- include/linux/if_pppox.h | 3 + 3 files changed, 178 insertions(+), 119 deletions(-) diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c index 8843a9d30911..af3202a920a0 100644 --- a/drivers/net/ppp/pppolac.c +++ b/drivers/net/ppp/pppolac.c @@ -3,7 +3,6 @@ * Driver for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) * * Copyright (C) 2009 Google, Inc. - * Author: Chia-chi Yeh * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -22,8 +21,10 @@ * only works on IPv4 due to the lack of UDP encapsulation support in IPv6. */ #include +#include #include #include +#include #include #include #include @@ -31,13 +32,14 @@ #include #include #include +#include -#define L2TP_CONTROL_MASK 0x80 -#define L2TP_VERSION_MASK 0x0F +#define L2TP_CONTROL_BIT 0x80 +#define L2TP_LENGTH_BIT 0x40 +#define L2TP_SEQUENCE_BIT 0x08 +#define L2TP_OFFSET_BIT 0x02 #define L2TP_VERSION 0x02 -#define L2TP_LENGTH_MASK 0x40 -#define L2TP_OFFSET_MASK 0x02 -#define L2TP_SEQUENCE_MASK 0x08 +#define L2TP_VERSION_MASK 0x0F #define PPP_ADDR 0xFF #define PPP_CTRL 0x03 @@ -51,10 +53,10 @@ static inline union unaligned *unaligned(void *ptr) return (union unaligned *)ptr; } -static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb) +static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) { - struct sock *sk; - struct pppolac_opt *opt; + struct sock *sk = (struct sock *)sk_udp->sk_user_data; + struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac; __u8 bits; __u8 *ptr; @@ -63,10 +65,10 @@ static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb) goto drop; /* Put it back if it is a control packet. */ - if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_MASK) - return 1; + if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_BIT) + return opt->backlog_rcv(sk_udp, skb); - /* Now the packet is ours. Skip UDP header. */ + /* Skip UDP header. */ skb_pull(skb, sizeof(struct udphdr)); /* Check the version. */ @@ -76,44 +78,30 @@ static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb) ptr = &skb->data[2]; /* Check the length if it is present. */ - if (bits & L2TP_LENGTH_MASK) { + if (bits & L2TP_LENGTH_BIT) { if ((ptr[0] << 8 | ptr[1]) != skb->len) goto drop; ptr += 2; } /* Skip all fields including optional ones. */ - if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_MASK ? 4 : 0) + - (bits & L2TP_LENGTH_MASK ? 2 : 0) + - (bits & L2TP_OFFSET_MASK ? 2 : 0))) + if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_BIT ? 4 : 0) + + (bits & L2TP_LENGTH_BIT ? 2 : 0) + + (bits & L2TP_OFFSET_BIT ? 2 : 0))) goto drop; /* Skip the offset padding if it is present. */ - if (bits & L2TP_OFFSET_MASK && + if (bits & L2TP_OFFSET_BIT && !skb_pull(skb, skb->data[-2] << 8 | skb->data[-1])) goto drop; - /* Now ptr is pointing to the tunnel and skb is pointing to the payload. - * We have to lock sk_udp to prevent sk from being closed. */ - lock_sock(sk_udp); - sk = sk_udp->sk_user_data; - if (!sk) { - release_sock(sk_udp); - goto drop; - } - sock_hold(sk); - release_sock(sk_udp); - opt = &pppox_sk(sk)->proto.lac; - /* Check the tunnel and the session. */ - if (unaligned(ptr)->u32 != opt->local) { - sock_put(sk); + if (unaligned(ptr)->u32 != opt->local) goto drop; - } - /* Check the sequence if it is present. According to RFC 2661 page 10 - * and 43, the only thing to do is updating opt->sequencing. */ - opt->sequencing = bits & L2TP_SEQUENCE_MASK; + /* Check the sequence if it is present. According to RFC 2661 section + * 5.4, the only thing to do is to update opt->sequencing. */ + opt->sequencing = bits & L2TP_SEQUENCE_BIT; /* Skip PPP address and control if they are present. */ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && @@ -124,26 +112,50 @@ static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb) if (skb->len >= 1 && skb->data[0] & 1) skb_push(skb, 1)[0] = 0; - /* Finally, deliver the packet to PPP channel. We have to lock sk to - * prevent another thread from calling pppox_unbind_sock(). */ + /* Finally, deliver the packet to PPP channel. */ skb_orphan(skb); - lock_sock(sk); ppp_input(&pppox_sk(sk)->chan, skb); - release_sock(sk); - sock_put(sk); - return 0; - + return NET_RX_SUCCESS; drop: kfree_skb(skb); + return NET_RX_DROP; +} + +static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb) +{ + sock_hold(sk_udp); + sk_receive_skb(sk_udp, skb, 0); return 0; } +static struct sk_buff_head delivery_queue; + +static void pppolac_xmit_core(struct work_struct *delivery_work) +{ + mm_segment_t old_fs = get_fs(); + struct sk_buff *skb; + + set_fs(KERNEL_DS); + while ((skb = skb_dequeue(&delivery_queue))) { + struct sock *sk_udp = skb->sk; + struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; + struct msghdr msg = { + .msg_iov = (struct iovec *)&iov, + .msg_iovlen = 1, + .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, + }; + sk_udp->sk_prot->sendmsg(NULL, sk_udp, &msg, skb->len); + kfree_skb(skb); + } + set_fs(old_fs); +} + +static DECLARE_WORK(delivery_work, pppolac_xmit_core); + static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) { struct sock *sk_udp = (struct sock *)chan->private; struct pppolac_opt *opt = &pppox_sk(sk_udp->sk_user_data)->proto.lac; - struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; - struct kvec iov; /* Install PPP address and control. */ skb_push(skb, 2); @@ -153,7 +165,7 @@ static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) /* Install L2TP header. */ if (opt->sequencing) { skb_push(skb, 10); - skb->data[0] = L2TP_SEQUENCE_MASK; + skb->data[0] = L2TP_SEQUENCE_BIT; skb->data[6] = opt->sequence >> 8; skb->data[7] = opt->sequence; skb->data[8] = 0; @@ -166,11 +178,10 @@ static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) skb->data[1] = L2TP_VERSION; unaligned(&skb->data[2])->u32 = opt->remote; - /* Now send the packet via UDP socket. */ - iov.iov_base = skb->data; - iov.iov_len = skb->len; - kernel_sendmsg(sk_udp->sk_socket, &msg, &iov, 1, skb->len); - kfree_skb(skb); + /* Now send the packet via the delivery queue. */ + skb_set_owner_w(skb, sk_udp); + skb_queue_tail(&delivery_queue, skb); + schedule_work(&delivery_work); return 1; } @@ -220,6 +231,14 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, error = -EBUSY; if (udp_sk(sk_udp)->encap_type || sk_udp->sk_user_data) goto out; + if (!sk_udp->sk_bound_dev_if) { + struct dst_entry *dst = sk_dst_get(sk_udp); + error = -ENODEV; + if (!dst) + goto out; + sk_udp->sk_bound_dev_if = dst->dev->ifindex; + dst_release(dst); + } po->chan.hdrlen = 12; po->chan.private = sk_udp; @@ -227,6 +246,7 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, po->chan.mtu = PPP_MTU - 80; po->proto.lac.local = unaligned(&addr->local)->u32; po->proto.lac.remote = unaligned(&addr->remote)->u32; + po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv; error = ppp_register_channel(&po->chan); if (error) @@ -235,8 +255,8 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, sk->sk_state = PPPOX_CONNECTED; udp_sk(sk_udp)->encap_type = UDP_ENCAP_L2TPINUDP; udp_sk(sk_udp)->encap_rcv = pppolac_recv; + sk_udp->sk_backlog_rcv = pppolac_recv_core; sk_udp->sk_user_data = sk; - out: if (sock_udp) { release_sock(sk_udp); @@ -263,12 +283,11 @@ static int pppolac_release(struct socket *sock) if (sk->sk_state != PPPOX_NONE) { struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private; lock_sock(sk_udp); - pppox_unbind_sock(sk); - sk_udp->sk_user_data = NULL; udp_sk(sk_udp)->encap_type = 0; udp_sk(sk_udp)->encap_rcv = NULL; - + sk_udp->sk_backlog_rcv = pppox_sk(sk)->proto.lac.backlog_rcv; + sk_udp->sk_user_data = NULL; release_sock(sk_udp); sockfd_put(sk_udp->sk_socket); } @@ -342,6 +361,8 @@ static int __init pppolac_init(void) error = register_pppox_proto(PX_PROTO_OLAC, &pppolac_pppox_proto); if (error) proto_unregister(&pppolac_proto); + else + skb_queue_head_init(&delivery_queue); return error; } diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c index 8885eba8968e..298097127c90 100644 --- a/drivers/net/ppp/pppopns.c +++ b/drivers/net/ppp/pppopns.c @@ -3,7 +3,6 @@ * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) * * Copyright (C) 2009 Google, Inc. - * Author: Chia-chi Yeh * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -22,20 +21,24 @@ * and IPv6. */ #include +#include #include #include +#include #include #include #include #include #include #include +#include #define GRE_HEADER_SIZE 8 -#define PPTP_GRE_MASK htons(0x2001) -#define PPTP_GRE_SEQ_MASK htons(0x1000) -#define PPTP_GRE_ACK_MASK htons(0x0080) +#define PPTP_GRE_BITS htons(0x2001) +#define PPTP_GRE_BITS_MASK htons(0xEF7F) +#define PPTP_GRE_SEQ_BIT htons(0x1000) +#define PPTP_GRE_ACK_BIT htons(0x0080) #define PPTP_GRE_TYPE htons(0x880B) #define PPP_ADDR 0xFF @@ -49,76 +52,90 @@ struct header { __u32 sequence; } __attribute__((packed)); -static void pppopns_recv(struct sock *sk_raw, int length) +static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) { - struct sock *sk; - struct pppopns_opt *opt; - struct sk_buff *skb; + struct sock *sk = (struct sock *)sk_raw->sk_user_data; + struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns; struct header *hdr; - /* Lock sk_raw to prevent sk from being closed. */ - lock_sock(sk_raw); - sk = (struct sock *)sk_raw->sk_user_data; - if (!sk) { - release_sock(sk_raw); - return; - } - sock_hold(sk); - release_sock(sk_raw); - opt = &pppox_sk(sk)->proto.pns; + /* Skip transport header */ + skb_pull(skb, skb_transport_header(skb) - skb->data); - /* Process packets from the receive queue. */ - while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) { - skb_pull(skb, skb_transport_header(skb) - skb->data); + /* Drop the packet if it is too short. */ + if (skb->len < GRE_HEADER_SIZE) + goto drop; - /* Drop the packet if it is too short. */ - if (skb->len < GRE_HEADER_SIZE) - goto drop; + /* Check the header. */ + hdr = (struct header *)skb->data; + if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || + (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS) + goto drop; - /* Check the header. */ - hdr = (struct header *)skb->data; - if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || - (hdr->bits & PPTP_GRE_MASK) != PPTP_GRE_MASK) - goto drop; + /* Skip all fields including optional ones. */ + if (!skb_pull(skb, GRE_HEADER_SIZE + + (hdr->bits & PPTP_GRE_SEQ_BIT ? 4 : 0) + + (hdr->bits & PPTP_GRE_ACK_BIT ? 4 : 0))) + goto drop; - /* Skip all fields including optional ones. */ - if (!skb_pull(skb, GRE_HEADER_SIZE + - (hdr->bits & PPTP_GRE_SEQ_MASK ? 4 : 0) + - (hdr->bits & PPTP_GRE_ACK_MASK ? 4 : 0))) - goto drop; + /* Check the length. */ + if (skb->len != ntohs(hdr->length)) + goto drop; - /* Check the length. */ - if (skb->len != ntohs(hdr->length)) - goto drop; + /* Skip PPP address and control if they are present. */ + if (skb->len >= 2 && skb->data[0] == PPP_ADDR && + skb->data[1] == PPP_CTRL) + skb_pull(skb, 2); - /* Skip PPP address and control if they are present. */ - if (skb->len >= 2 && skb->data[0] == PPP_ADDR && - skb->data[1] == PPP_CTRL) - skb_pull(skb, 2); + /* Fix PPP protocol if it is compressed. */ + if (skb->len >= 1 && skb->data[0] & 1) + skb_push(skb, 1)[0] = 0; - /* Fix PPP protocol if it is compressed. */ - if (skb->len >= 1 && skb->data[0] & 1) - skb_push(skb, 1)[0] = 0; - - /* Deliver the packet to PPP channel. We have to lock sk to - * prevent another thread from calling pppox_unbind_sock(). */ - skb_orphan(skb); - lock_sock(sk); - ppp_input(&pppox_sk(sk)->chan, skb); - release_sock(sk); - continue; + /* Finally, deliver the packet to PPP channel. */ + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + return NET_RX_SUCCESS; drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static void pppopns_recv(struct sock *sk_raw, int length) +{ + struct sk_buff *skb; + while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) { + sock_hold(sk_raw); + sk_receive_skb(sk_raw, skb, 0); + } +} + +static struct sk_buff_head delivery_queue; + +static void pppopns_xmit_core(struct work_struct *delivery_work) +{ + mm_segment_t old_fs = get_fs(); + struct sk_buff *skb; + + set_fs(KERNEL_DS); + while ((skb = skb_dequeue(&delivery_queue))) { + struct sock *sk_raw = skb->sk; + struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; + struct msghdr msg = { + .msg_iov = (struct iovec *)&iov, + .msg_iovlen = 1, + .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, + }; + sk_raw->sk_prot->sendmsg(NULL, sk_raw, &msg, skb->len); kfree_skb(skb); } - sock_put(sk); + set_fs(old_fs); } +static DECLARE_WORK(delivery_work, pppopns_xmit_core); + static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) { struct sock *sk_raw = (struct sock *)chan->private; struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns; - struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; - struct kvec iov; struct header *hdr; __u16 length; @@ -130,18 +147,17 @@ static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) /* Install PPTP GRE header. */ hdr = (struct header *)skb_push(skb, 12); - hdr->bits = PPTP_GRE_MASK | PPTP_GRE_SEQ_MASK; + hdr->bits = PPTP_GRE_BITS | PPTP_GRE_SEQ_BIT; hdr->type = PPTP_GRE_TYPE; hdr->length = htons(length); hdr->call = opt->remote; hdr->sequence = htonl(opt->sequence); opt->sequence++; - /* Now send the packet via RAW socket. */ - iov.iov_base = skb->data; - iov.iov_len = skb->len; - kernel_sendmsg(sk_raw->sk_socket, &msg, &iov, 1, skb->len); - kfree_skb(skb); + /* Now send the packet via the delivery queue. */ + skb_set_owner_w(skb, sk_raw); + skb_queue_tail(&delivery_queue, skb); + schedule_work(&delivery_work); return 1; } @@ -160,6 +176,7 @@ static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, struct sockaddr_storage ss; struct socket *sock_tcp = NULL; struct socket *sock_raw = NULL; + struct sock *sk_tcp; struct sock *sk_raw; int error; @@ -174,21 +191,31 @@ static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, sock_tcp = sockfd_lookup(addr->tcp_socket, &error); if (!sock_tcp) goto out; + sk_tcp = sock_tcp->sk; error = -EPROTONOSUPPORT; - if (sock_tcp->sk->sk_protocol != IPPROTO_TCP) + if (sk_tcp->sk_protocol != IPPROTO_TCP) goto out; addrlen = sizeof(struct sockaddr_storage); error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen); if (error) goto out; + if (!sk_tcp->sk_bound_dev_if) { + struct dst_entry *dst = sk_dst_get(sk_tcp); + error = -ENODEV; + if (!dst) + goto out; + sk_tcp->sk_bound_dev_if = dst->dev->ifindex; + dst_release(dst); + } error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw); if (error) goto out; + sk_raw = sock_raw->sk; + sk_raw->sk_bound_dev_if = sk_tcp->sk_bound_dev_if; error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0); if (error) goto out; - sk_raw = sock_raw->sk; po->chan.hdrlen = 14; po->chan.private = sk_raw; @@ -196,15 +223,19 @@ static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, po->chan.mtu = PPP_MTU - 80; po->proto.pns.local = addr->local; po->proto.pns.remote = addr->remote; + po->proto.pns.data_ready = sk_raw->sk_data_ready; + po->proto.pns.backlog_rcv = sk_raw->sk_backlog_rcv; error = ppp_register_channel(&po->chan); if (error) goto out; sk->sk_state = PPPOX_CONNECTED; - sk_raw->sk_user_data = sk; + lock_sock(sk_raw); sk_raw->sk_data_ready = pppopns_recv; - + sk_raw->sk_backlog_rcv = pppopns_recv_core; + sk_raw->sk_user_data = sk; + release_sock(sk_raw); out: if (sock_tcp) sockfd_put(sock_tcp); @@ -231,6 +262,8 @@ static int pppopns_release(struct socket *sock) struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; lock_sock(sk_raw); pppox_unbind_sock(sk); + sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready; + sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv; sk_raw->sk_user_data = NULL; release_sock(sk_raw); sock_release(sk_raw->sk_socket); @@ -305,6 +338,8 @@ static int __init pppopns_init(void) error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto); if (error) proto_unregister(&pppopns_proto); + else + skb_queue_head_init(&delivery_queue); return error; } diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index e53107fdf859..bc4f57df265b 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -47,12 +47,15 @@ struct pppolac_opt { __u32 remote; __u16 sequence; __u8 sequencing; + int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb); }; struct pppopns_opt { __u16 local; __u16 remote; __u32 sequence; + void (*data_ready)(struct sock *sk_raw, int length); + int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb); }; #include From d1f1fc1cfdb0c3279e4ef97bdc06e01a081316e2 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 19 Jun 2009 07:15:05 +0800 Subject: [PATCH 057/358] security: Add AID_NET_RAW and AID_NET_ADMIN capability check in cap_capable(). Signed-off-by: Chia-chi Yeh --- include/linux/android_aid.h | 1 + security/commoncap.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h index dc66530e5fc7..3d7a5ead1200 100644 --- a/include/linux/android_aid.h +++ b/include/linux/android_aid.h @@ -21,5 +21,6 @@ #define AID_OBSOLETE_001 KGIDT_INIT(3002) /* was NET_BT */ #define AID_INET KGIDT_INIT(3003) #define AID_NET_RAW KGIDT_INIT(3004) +#define AID_NET_ADMIN KGIDT_INIT(3005) #endif diff --git a/security/commoncap.c b/security/commoncap.c index b9d613e0ef14..6c4e3ea459d3 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -31,6 +31,10 @@ #include #include +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif + /* * If a non-root user executes a setuid-root binary in * !secure(SECURE_NOROOT) mode, then we raise capabilities. @@ -78,6 +82,11 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, { struct user_namespace *ns = targ_ns; + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 0; + if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) + return 0; + /* See if cred has the capability in the target user namespace * by examining the target user namespace and all of the target * user namespace's parents. From 4a7cd990b348f99d5209d09419e866ffc698ade1 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Tue, 30 Jun 2009 11:23:04 +0800 Subject: [PATCH 058/358] net: Replace AID_NET_RAW checks with capable(CAP_NET_RAW). Signed-off-by: Chia-chi Yeh --- net/ipv4/af_inet.c | 36 +++++++++++------------------------- net/ipv6/af_inet6.c | 36 +++++++++++------------------------- 2 files changed, 22 insertions(+), 50 deletions(-) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index ebce2373f75e..09f2500fcb5e 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -121,6 +121,16 @@ #ifdef CONFIG_ANDROID_PARANOID_NETWORK #include + +static inline int current_has_network(void) +{ + return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); +} +#else +static inline int current_has_network(void) +{ + return 1; +} #endif /* The inetsw table contains everything that inet_create needs to @@ -245,29 +255,6 @@ out: } EXPORT_SYMBOL(inet_listen); -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -static inline int current_has_network(void) -{ - return (!current_euid() || in_egroup_p(AID_INET) || - in_egroup_p(AID_NET_RAW)); -} -static inline int current_has_cap(struct net *net, int cap) -{ - if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) - return 1; - return ns_capable(net->user_ns, cap); -} -# else -static inline int current_has_network(void) -{ - return 1; -} -static inline int current_has_cap(struct net *net, int cap) -{ - return ns_capable(net->user_ns, cap); -} -#endif - /* * Create an inet socket. */ @@ -335,8 +322,7 @@ lookup_protocol: } err = -EPERM; - if (sock->type == SOCK_RAW && !kern && - !current_has_cap(net, CAP_NET_RAW)) + if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index b256c72f8163..db23fbdf357c 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -66,6 +66,16 @@ #ifdef CONFIG_ANDROID_PARANOID_NETWORK #include + +static inline int current_has_network(void) +{ + return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); +} +#else +static inline int current_has_network(void) +{ + return 1; +} #endif MODULE_AUTHOR("Cast of dozens"); @@ -101,29 +111,6 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) return (struct ipv6_pinfo *)(((u8 *)sk) + offset); } -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -static inline int current_has_network(void) -{ - return (!current_euid() || in_egroup_p(AID_INET) || - in_egroup_p(AID_NET_RAW)); -} -static inline int current_has_cap(struct net *net, int cap) -{ - if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) - return 1; - return ns_capable(net->user_ns, cap); -} -# else -static inline int current_has_network(void) -{ - return 1; -} -static inline int current_has_cap(struct net *net, int cap) -{ - return ns_capable(net->user_ns, cap); -} -#endif - static int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) { @@ -186,8 +173,7 @@ lookup_protocol: } err = -EPERM; - if (sock->type == SOCK_RAW && !kern && - !current_has_cap(net, CAP_NET_RAW)) + if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; From eb503b324210115388fc5c6096da2bcea610796f Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 4 Dec 2008 17:37:05 -0800 Subject: [PATCH 059/358] rfkill: Introduce CONFIG_RFKILL_PM and use instead of CONFIG_PM to power down Some platforms do not want to power down rfkill devices on suspend. Change-Id: I62a11630521c636d54a4a02ab9037a43435925f5 Signed-off-by: Nick Pelly --- net/rfkill/Kconfig | 5 +++++ net/rfkill/core.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 4c10e7e6c9f6..77aa1dfc3f88 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -10,6 +10,11 @@ menuconfig RFKILL To compile this driver as a module, choose M here: the module will be called rfkill. +config RFKILL_PM + bool "Power off on suspend" + depends on RFKILL && PM + default y + # LED trigger support config RFKILL_LEDS bool diff --git a/net/rfkill/core.c b/net/rfkill/core.c index ed7e0b4e7f90..04bd9c4dfb2a 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -782,6 +782,7 @@ void rfkill_pause_polling(struct rfkill *rfkill) } EXPORT_SYMBOL(rfkill_pause_polling); +#ifdef CONFIG_RFKILL_PM void rfkill_resume_polling(struct rfkill *rfkill) { BUG_ON(!rfkill); @@ -816,14 +817,17 @@ static int rfkill_resume(struct device *dev) return 0; } +#endif static struct class rfkill_class = { .name = "rfkill", .dev_release = rfkill_release, .dev_groups = rfkill_dev_groups, .dev_uevent = rfkill_dev_uevent, +#ifdef CONFIG_RFKILL_PM .suspend = rfkill_suspend, .resume = rfkill_resume, +#endif }; bool rfkill_blocked(struct rfkill *rfkill) From 4eac1df4fe521eeb999c91bc3539d97cefc17273 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 29 Jul 2009 20:21:28 -0700 Subject: [PATCH 060/358] serial_core: Add wake_peer uart operation Add wake_peer which is called before starting UART TX. The idea here is to provide a mechanism where we can wakeup our peer before sending data. Change-Id: I42e0779b635f64ca99184b45d5b028de80197491 Signed-off-by: San Mehat --- drivers/tty/serial/serial_core.c | 3 +++ include/linux/serial_core.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index ece2049bd270..7f3d7a3564ac 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -89,6 +89,9 @@ static void __uart_start(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; + if (port->ops->wake_peer) + port->ops->wake_peer(port); + if (!uart_circ_empty(&state->xmit) && state->xmit.buf && !tty->stopped && !tty->hw_stopped) port->ops->start_tx(port); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index f729be981da0..318f629ce0af 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -66,6 +66,7 @@ struct uart_ops { void (*set_ldisc)(struct uart_port *, int new); void (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate); + void (*wake_peer)(struct uart_port *); /* * Return a string describing the type of the port From 4b152645bab71f3d36c4c3169a7c49d7948e0de8 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 16 Sep 2009 12:39:10 -0700 Subject: [PATCH 061/358] proc: smaps: Allow smaps access for CAP_SYS_RESOURCE Signed-off-by: San Mehat --- kernel/fork.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/fork.c b/kernel/fork.c index a17621c6cd42..dfb713910e32 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -690,7 +690,8 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) mm = get_task_mm(task); if (mm && mm != current->mm && - !ptrace_may_access(task, mode)) { + !ptrace_may_access(task, mode) && + !capable(CAP_SYS_RESOURCE)) { mmput(mm); mm = ERR_PTR(-EACCES); } From 8ae3e16bac26f02fb49ad694cb22a5adc7b02510 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 10 Oct 2009 09:35:24 -0700 Subject: [PATCH 062/358] block: genhd: Add disk/partition specific uevent callbacks for partition info For disk devices, a new uevent parameter 'NPARTS' specifies the number of partitions detected by the kernel. Partition devices get 'PARTN' which specifies the partitions index in the table, and 'PARTNAME', which specifies PARTNAME specifices the partition name of a partition device Signed-off-by: Dima Zavin --- block/genhd.c | 17 +++++++++++++++++ block/partition-generic.c | 11 +++++++++++ 2 files changed, 28 insertions(+) diff --git a/block/genhd.c b/block/genhd.c index 791f41943132..3bceeb40aec2 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1107,6 +1107,22 @@ static void disk_release(struct device *dev) blk_put_queue(disk->queue); kfree(disk); } + +static int disk_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct gendisk *disk = dev_to_disk(dev); + struct disk_part_iter piter; + struct hd_struct *part; + int cnt = 0; + + disk_part_iter_init(&piter, disk, 0); + while((part = disk_part_iter_next(&piter))) + cnt++; + disk_part_iter_exit(&piter); + add_uevent_var(env, "NPARTS=%u", cnt); + return 0; +} + struct class block_class = { .name = "block", }; @@ -1126,6 +1142,7 @@ static struct device_type disk_type = { .groups = disk_attr_groups, .release = disk_release, .devnode = block_devnode, + .uevent = disk_uevent, }; #ifdef CONFIG_PROC_FS diff --git a/block/partition-generic.c b/block/partition-generic.c index 789cdea05893..c7942acf1379 100644 --- a/block/partition-generic.c +++ b/block/partition-generic.c @@ -216,10 +216,21 @@ static void part_release(struct device *dev) kfree(p); } +static int part_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct hd_struct *part = dev_to_part(dev); + + add_uevent_var(env, "PARTN=%u", part->partno); + if (part->info && part->info->volname[0]) + add_uevent_var(env, "PARTNAME=%s", part->info->volname); + return 0; +} + struct device_type part_type = { .name = "partition", .groups = part_attr_groups, .release = part_release, + .uevent = part_uevent, }; static void delete_partition_rcu_cb(struct rcu_head *head) From 4f977a3d81f8a197215f3376ebb20cdda60ed854 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 18 Mar 2010 16:04:18 -0700 Subject: [PATCH 063/358] wlan: Extract generic wlan platform data from tiwlan specific header Signed-off-by: Dmitry Shmidt --- include/linux/wifi_tiwlan.h | 9 ++------- include/linux/wlan_plat.h | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 include/linux/wlan_plat.h diff --git a/include/linux/wifi_tiwlan.h b/include/linux/wifi_tiwlan.h index 5897e726c4f9..f07e0679fb82 100644 --- a/include/linux/wifi_tiwlan.h +++ b/include/linux/wifi_tiwlan.h @@ -15,6 +15,8 @@ #ifndef _LINUX_WIFI_TIWLAN_H_ #define _LINUX_WIFI_TIWLAN_H_ +#include + #define WMPA_NUMBER_OF_SECTIONS 3 #define WMPA_NUMBER_OF_BUFFERS 160 #define WMPA_SECTION_HEADER 24 @@ -22,11 +24,4 @@ #define WMPA_SECTION_SIZE_1 (WMPA_NUMBER_OF_BUFFERS * 256) #define WMPA_SECTION_SIZE_2 (WMPA_NUMBER_OF_BUFFERS * 2048) -struct wifi_platform_data { - int (*set_power)(int val); - int (*set_reset)(int val); - int (*set_carddetect)(int val); - void *(*mem_prealloc)(int section, unsigned long size); -}; - #endif diff --git a/include/linux/wlan_plat.h b/include/linux/wlan_plat.h new file mode 100644 index 000000000000..70ee63b44ad6 --- /dev/null +++ b/include/linux/wlan_plat.h @@ -0,0 +1,25 @@ +/* include/linux/wlan_plat.h + * + * Copyright (C) 2010 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_WLAN_PLAT_H_ +#define _LINUX_WLAN_PLAT_H_ + +struct wifi_platform_data { + int (*set_power)(int val); + int (*set_reset)(int val); + int (*set_carddetect)(int val); + void *(*mem_prealloc)(int section, unsigned long size); +}; + +#endif From 764dec42a0edae099e10cd928f081b7aeee6aae7 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 6 May 2010 15:37:55 -0700 Subject: [PATCH 064/358] sched: Add a generic notifier when a task struct is about to be freed This patch adds a notifier which can be used by subsystems that may be interested in when a task has completely died and is about to have it's last resource freed. The Android lowmemory killer uses this to determine when a task it has killed has finally given up its goods. Signed-off-by: San Mehat --- include/linux/sched.h | 3 +++ kernel/fork.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/linux/sched.h b/include/linux/sched.h index a781dec1cd0b..2956e8cd550c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1792,6 +1792,9 @@ static inline cputime_t task_gtime(struct task_struct *t) extern void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st); extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st); +extern int task_free_register(struct notifier_block *n); +extern int task_free_unregister(struct notifier_block *n); + /* * Per process flags */ diff --git a/kernel/fork.c b/kernel/fork.c index dfb713910e32..cc081f25626e 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -198,6 +198,9 @@ struct kmem_cache *vm_area_cachep; /* SLAB cache for mm_struct structures (tsk->mm) */ static struct kmem_cache *mm_cachep; +/* Notifier list called when a task struct is freed */ +static ATOMIC_NOTIFIER_HEAD(task_free_notifier); + static void account_kernel_stack(struct thread_info *ti, int account) { struct zone *zone = page_zone(virt_to_page(ti)); @@ -231,6 +234,18 @@ static inline void put_signal_struct(struct signal_struct *sig) free_signal_struct(sig); } +int task_free_register(struct notifier_block *n) +{ + return atomic_notifier_chain_register(&task_free_notifier, n); +} +EXPORT_SYMBOL(task_free_register); + +int task_free_unregister(struct notifier_block *n) +{ + return atomic_notifier_chain_unregister(&task_free_notifier, n); +} +EXPORT_SYMBOL(task_free_unregister); + void __put_task_struct(struct task_struct *tsk) { WARN_ON(!tsk->exit_state); @@ -242,6 +257,7 @@ void __put_task_struct(struct task_struct *tsk) delayacct_tsk_free(tsk); put_signal_struct(tsk->signal); + atomic_notifier_call_chain(&task_free_notifier, 0, tsk); if (!profile_handoff_task(tsk)) free_task(tsk); } From 30afe7cff18389c8c60cf8d3ce9aac08f9caa4e5 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 6 May 2010 15:40:07 -0700 Subject: [PATCH 065/358] staging: android: lowmemorykiller: Fix task_struct leak As it turns out, the CONFIG_PROFILING interfaces leak a task struct if the notifier chain returns NOTIFY_OK.. doh. This patch reworks lowmemkiller to use the new generic task free notifier chain. Signed-off-by: San Mehat --- drivers/staging/android/lowmemorykiller.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 6f094b37f1f1..8b49b3dd0978 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -39,7 +39,6 @@ #include #include #include -#include #include static uint32_t lowmem_debug_level = 1; From c4edd8f916fe8e76641aed09d4de0218cc25d696 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Fri, 28 May 2010 14:32:19 -0700 Subject: [PATCH 066/358] net: activity_stats: Add statistics for network transmission activity When enabled, tracks the frequency of network transmissions (inbound and outbound) and buckets them accordingly. Buckets are determined by time between network activity. Each bucket represents the number of network transmisions that were N sec or longer apart. Where N is defined as 1 << bucket index. This network pattern tracking is particularly useful for wireless networks (ie: 3G) where batching network activity closely together is more power efficient than far apart. New file: /proc/net/stat/activity output: Min Bucket(sec) Count 1 7 2 0 4 1 8 0 16 0 32 2 64 1 128 0 Change-Id: I4c4cd8627b872a55f326b1715c51bc3bdd6e8d92 Signed-off-by: Mike Chan --- drivers/misc/uid_stat.c | 3 + include/net/activity_stats.h | 25 ++++++++ net/Kconfig | 8 +++ net/Makefile | 1 + net/activity_stats.c | 115 +++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 include/net/activity_stats.h create mode 100644 net/activity_stats.c diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c index e6760b56083c..2141124a6c12 100644 --- a/drivers/misc/uid_stat.c +++ b/drivers/misc/uid_stat.c @@ -24,6 +24,7 @@ #include #include #include +#include static DEFINE_SPINLOCK(uid_lock); static LIST_HEAD(uid_list); @@ -122,6 +123,7 @@ static struct uid_stat *create_stat(uid_t uid) { int uid_stat_tcp_snd(uid_t uid, int size) { struct uid_stat *entry; + activity_stats_update(); if ((entry = find_uid_stat(uid)) == NULL && ((entry = create_stat(uid)) == NULL)) { return -1; @@ -132,6 +134,7 @@ int uid_stat_tcp_snd(uid_t uid, int size) { int uid_stat_tcp_rcv(uid_t uid, int size) { struct uid_stat *entry; + activity_stats_update(); if ((entry = find_uid_stat(uid)) == NULL && ((entry = create_stat(uid)) == NULL)) { return -1; diff --git a/include/net/activity_stats.h b/include/net/activity_stats.h new file mode 100644 index 000000000000..10e4c1506eeb --- /dev/null +++ b/include/net/activity_stats.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010 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. + * + * Author: Mike Chan (mike@android.com) + */ + +#ifndef __activity_stats_h +#define __activity_stats_h + +#ifdef CONFIG_NET_ACTIVITY_STATS +void activity_stats_update(void); +#else +#define activity_stats_update(void) {} +#endif + +#endif /* _NET_ACTIVITY_STATS_H */ diff --git a/net/Kconfig b/net/Kconfig index 7adb9da06827..6cca0ad88a57 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -88,6 +88,14 @@ config ANDROID_PARANOID_NETWORK help none +config NET_ACTIVITY_STATS + bool "Network activity statistics tracking" + default y + help + Network activity statistics are useful for tracking wireless + modem activity on 2G, 3G, 4G wireless networks. Counts number of + transmissions and groups them in specified time buckets. + config NETWORK_SECMARK bool "Security Marking" help diff --git a/net/Makefile b/net/Makefile index cbbbe6d657ca..74862871aa13 100644 --- a/net/Makefile +++ b/net/Makefile @@ -72,3 +72,4 @@ obj-$(CONFIG_OPENVSWITCH) += openvswitch/ obj-$(CONFIG_VSOCKETS) += vmw_vsock/ obj-$(CONFIG_NET_MPLS_GSO) += mpls/ obj-$(CONFIG_HSR) += hsr/ +obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o diff --git a/net/activity_stats.c b/net/activity_stats.c new file mode 100644 index 000000000000..8a3e93470069 --- /dev/null +++ b/net/activity_stats.c @@ -0,0 +1,115 @@ +/* net/activity_stats.c + * + * Copyright (C) 2010 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. + * + * Author: Mike Chan (mike@android.com) + */ + +#include +#include +#include + +/* + * Track transmission rates in buckets (power of 2). + * 1,2,4,8...512 seconds. + * + * Buckets represent the count of network transmissions at least + * N seconds apart, where N is 1 << bucket index. + */ +#define BUCKET_MAX 10 + +/* Track network activity frequency */ +static unsigned long activity_stats[BUCKET_MAX]; +static ktime_t last_transmit; +static ktime_t suspend_time; +static DEFINE_SPINLOCK(activity_lock); + +void activity_stats_update(void) +{ + int i; + unsigned long flags; + ktime_t now; + s64 delta; + + spin_lock_irqsave(&activity_lock, flags); + now = ktime_get(); + delta = ktime_to_ns(ktime_sub(now, last_transmit)); + + for (i = BUCKET_MAX - 1; i >= 0; i--) { + /* + * Check if the time delta between network activity is within the + * minimum bucket range. + */ + if (delta < (1000000000ULL << i)) + continue; + + activity_stats[i]++; + last_transmit = now; + break; + } + spin_unlock_irqrestore(&activity_lock, flags); +} + +static int activity_stats_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int i; + int len; + char *p = page; + + /* Only print if offset is 0, or we have enough buffer space */ + if (off || count < (30 * BUCKET_MAX + 22)) + return -ENOMEM; + + len = snprintf(p, count, "Min Bucket(sec) Count\n"); + count -= len; + p += len; + + for (i = 0; i < BUCKET_MAX; i++) { + len = snprintf(p, count, "%15d %lu\n", 1 << i, activity_stats[i]); + count -= len; + p += len; + } + *eof = 1; + + return p - page; +} + +static int activity_stats_notifier(struct notifier_block *nb, + unsigned long event, void *dummy) +{ + switch (event) { + case PM_SUSPEND_PREPARE: + suspend_time = ktime_get_real(); + break; + + case PM_POST_SUSPEND: + suspend_time = ktime_sub(ktime_get_real(), suspend_time); + last_transmit = ktime_sub(last_transmit, suspend_time); + } + + return 0; +} + +static struct notifier_block activity_stats_notifier_block = { + .notifier_call = activity_stats_notifier, +}; + +static int __init activity_stats_init(void) +{ + create_proc_read_entry("activity", S_IRUGO, + init_net.proc_net_stat, activity_stats_read_proc, NULL); + return register_pm_notifier(&activity_stats_notifier_block); +} + +subsys_initcall(activity_stats_init); + From b866f6cb0e308f354b0cdcd37a256642d063340c Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 30 Aug 2010 10:29:37 -0700 Subject: [PATCH 067/358] net: Fix CONFIG_RPS option to be turned off Signed-off-by: Dmitry Shmidt --- net/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/Kconfig b/net/Kconfig index 6cca0ad88a57..219a8c1c80fe 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -237,7 +237,7 @@ source "net/mpls/Kconfig" source "net/hsr/Kconfig" config RPS - boolean + boolean "RPS" depends on SMP && SYSFS default y From ebb2ed762d02b8b68eade340867d9df678d746e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 3 Jun 2010 16:33:07 -0700 Subject: [PATCH 068/358] Allow CONFIG_STACKTRACE to be enabled by itself. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to get a kernel stacktrace for a thread though /proc. Also enable it by default. Change-Id: If8c21cd02feaf9863f4841ace524fa30c7328d49 Signed-off-by: Arve Hjønnevåg --- lib/Kconfig.debug | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index a48abeac753f..5e2e2344b246 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -989,8 +989,9 @@ config TRACE_IRQFLAGS either tracing or lock debugging. config STACKTRACE - bool + bool "Stacktrace" depends on STACKTRACE_SUPPORT + default y config DEBUG_KOBJECT bool "kobject debugging" From c4a48130f32b7c4826e7355f4ad3bbcb7e748672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 27 Sep 2010 17:50:00 -0700 Subject: [PATCH 069/358] ARM: Add fiq_glue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I27d2554e07d9de204e0a06696d38db51608d9f6b Signed-off-by: Arve Hjønnevåg Signed-off-by: Colin Cross --- arch/arm/common/Kconfig | 4 ++ arch/arm/common/Makefile | 1 + arch/arm/common/fiq_glue.S | 111 +++++++++++++++++++++++++++++++ arch/arm/common/fiq_glue_setup.c | 100 ++++++++++++++++++++++++++++ arch/arm/include/asm/fiq_glue.h | 30 +++++++++ 5 files changed, 246 insertions(+) create mode 100644 arch/arm/common/fiq_glue.S create mode 100644 arch/arm/common/fiq_glue_setup.c create mode 100644 arch/arm/include/asm/fiq_glue.h diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index c3a4e9ceba34..f4a38a76db64 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -20,3 +20,7 @@ config SHARP_SCOOP config TI_PRIV_EDMA bool + +config FIQ_GLUE + bool + select FIQ diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 4bdc41622c36..f51fd9b5d590 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -4,6 +4,7 @@ obj-y += firmware.o +obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_ICST) += icst.o obj-$(CONFIG_SA1111) += sa1111.o obj-$(CONFIG_DMABOUNCE) += dmabounce.o diff --git a/arch/arm/common/fiq_glue.S b/arch/arm/common/fiq_glue.S new file mode 100644 index 000000000000..9e3455a09f8f --- /dev/null +++ b/arch/arm/common/fiq_glue.S @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2008 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 +#include + + .text + + .global fiq_glue_end + + /* fiq stack: r0-r15,cpsr,spsr of interrupted mode */ + +ENTRY(fiq_glue) + /* store pc, cpsr from previous mode */ + mrs r12, spsr + sub r11, lr, #4 + subs r10, #1 + bne nested_fiq + + stmfd sp!, {r11-r12, lr} + + /* store r8-r14 from previous mode */ + sub sp, sp, #(7 * 4) + stmia sp, {r8-r14}^ + nop + + /* store r0-r7 from previous mode */ + stmfd sp!, {r0-r7} + + /* setup func(data,regs) arguments */ + mov r0, r9 + mov r1, sp + mov r3, r8 + + mov r7, sp + + /* Get sp and lr from non-user modes */ + and r4, r12, #MODE_MASK + cmp r4, #USR_MODE + beq fiq_from_usr_mode + + mov r7, sp + orr r4, r4, #(PSR_I_BIT | PSR_F_BIT) + msr cpsr_c, r4 + str sp, [r7, #(4 * 13)] + str lr, [r7, #(4 * 14)] + mrs r5, spsr + str r5, [r7, #(4 * 17)] + + cmp r4, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) + /* use fiq stack if we reenter this mode */ + subne sp, r7, #(4 * 3) + +fiq_from_usr_mode: + msr cpsr_c, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) + mov r2, sp + sub sp, r7, #12 + stmfd sp!, {r2, ip, lr} + /* call func(data,regs) */ + blx r3 + ldmfd sp, {r2, ip, lr} + mov sp, r2 + + /* restore/discard saved state */ + cmp r4, #USR_MODE + beq fiq_from_usr_mode_exit + + msr cpsr_c, r4 + ldr sp, [r7, #(4 * 13)] + ldr lr, [r7, #(4 * 14)] + msr spsr_cxsf, r5 + +fiq_from_usr_mode_exit: + msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) + + ldmfd sp!, {r0-r7} + add sp, sp, #(7 * 4) + ldmfd sp!, {r11-r12, lr} +exit_fiq: + msr spsr_cxsf, r12 + add r10, #1 + movs pc, r11 + +nested_fiq: + orr r12, r12, #(PSR_F_BIT) + b exit_fiq + +fiq_glue_end: + +ENTRY(fiq_glue_setup) /* func, data, sp */ + mrs r3, cpsr + msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) + movs r8, r0 + mov r9, r1 + mov sp, r2 + moveq r10, #0 + movne r10, #1 + msr cpsr_c, r3 + bx lr + diff --git a/arch/arm/common/fiq_glue_setup.c b/arch/arm/common/fiq_glue_setup.c new file mode 100644 index 000000000000..4044c7db95c8 --- /dev/null +++ b/arch/arm/common/fiq_glue_setup.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 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 +#include +#include +#include +#include + +extern unsigned char fiq_glue, fiq_glue_end; +extern void fiq_glue_setup(void *func, void *data, void *sp); + +static struct fiq_handler fiq_debbuger_fiq_handler = { + .name = "fiq_glue", +}; +DEFINE_PER_CPU(void *, fiq_stack); +static struct fiq_glue_handler *current_handler; +static DEFINE_MUTEX(fiq_glue_lock); + +static void fiq_glue_setup_helper(void *info) +{ + struct fiq_glue_handler *handler = info; + fiq_glue_setup(handler->fiq, handler, + __get_cpu_var(fiq_stack) + THREAD_START_SP); +} + +int fiq_glue_register_handler(struct fiq_glue_handler *handler) +{ + int ret; + int cpu; + + if (!handler || !handler->fiq) + return -EINVAL; + + mutex_lock(&fiq_glue_lock); + if (fiq_stack) { + ret = -EBUSY; + goto err_busy; + } + + for_each_possible_cpu(cpu) { + void *stack; + stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); + if (WARN_ON(!stack)) { + ret = -ENOMEM; + goto err_alloc_fiq_stack; + } + per_cpu(fiq_stack, cpu) = stack; + } + + ret = claim_fiq(&fiq_debbuger_fiq_handler); + if (WARN_ON(ret)) + goto err_claim_fiq; + + current_handler = handler; + on_each_cpu(fiq_glue_setup_helper, handler, true); + set_fiq_handler(&fiq_glue, &fiq_glue_end - &fiq_glue); + + mutex_unlock(&fiq_glue_lock); + return 0; + +err_claim_fiq: +err_alloc_fiq_stack: + for_each_possible_cpu(cpu) { + __free_pages(per_cpu(fiq_stack, cpu), THREAD_SIZE_ORDER); + per_cpu(fiq_stack, cpu) = NULL; + } +err_busy: + mutex_unlock(&fiq_glue_lock); + return ret; +} + +/** + * fiq_glue_resume - Restore fiqs after suspend or low power idle states + * + * This must be called before calling local_fiq_enable after returning from a + * power state where the fiq mode registers were lost. If a driver provided + * a resume hook when it registered the handler it will be called. + */ + +void fiq_glue_resume(void) +{ + if (!current_handler) + return; + fiq_glue_setup(current_handler->fiq, current_handler, + __get_cpu_var(fiq_stack) + THREAD_START_SP); + if (current_handler->resume) + current_handler->resume(current_handler); +} + diff --git a/arch/arm/include/asm/fiq_glue.h b/arch/arm/include/asm/fiq_glue.h new file mode 100644 index 000000000000..d54c29db97a8 --- /dev/null +++ b/arch/arm/include/asm/fiq_glue.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010 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 __ASM_FIQ_GLUE_H +#define __ASM_FIQ_GLUE_H + +struct fiq_glue_handler { + void (*fiq)(struct fiq_glue_handler *h, void *regs, void *svc_sp); + void (*resume)(struct fiq_glue_handler *h); +}; + +int fiq_glue_register_handler(struct fiq_glue_handler *handler); + +#ifdef CONFIG_FIQ_GLUE +void fiq_glue_resume(void); +#else +static inline void fiq_glue_resume(void) {} +#endif + +#endif From ed3116e0b9e84e32b479dbc1720543a4d3274b0b Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Sat, 5 Jun 2010 17:36:24 -0700 Subject: [PATCH 070/358] ARM: Add generic fiq serial debugger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ibb536c88f0dbaf4766d0599296907e35e42cbfd6 Signed-off-by: Iliyan Malchev Signed-off-by: Arve Hjønnevåg --- arch/arm/common/Kconfig | 46 + arch/arm/common/Makefile | 1 + arch/arm/common/fiq_debugger.c | 1196 ++++++++++++++++++++++++ arch/arm/common/fiq_debugger_ringbuf.h | 94 ++ arch/arm/include/asm/fiq_debugger.h | 64 ++ 5 files changed, 1401 insertions(+) create mode 100644 arch/arm/common/fiq_debugger.c create mode 100644 arch/arm/common/fiq_debugger_ringbuf.h create mode 100644 arch/arm/include/asm/fiq_debugger.h diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index f4a38a76db64..4fb337ca0cb7 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -24,3 +24,49 @@ config TI_PRIV_EDMA 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. diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index f51fd9b5d590..4ff5d4c226bc 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -4,6 +4,7 @@ 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 diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c new file mode 100644 index 000000000000..3ed18ae2ed80 --- /dev/null +++ b/arch/arm/common/fiq_debugger.c @@ -0,0 +1,1196 @@ +/* + * arch/arm/common/fiq_debugger.c + * + * Serial Debugger Interface accessed through an FIQ interrupt. + * + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include "fiq_debugger_ringbuf.h" + +#define DEBUG_MAX 64 +#define MAX_UNHANDLED_FIQ_COUNT 1000000 + +#define THREAD_INFO(sp) ((struct thread_info *) \ + ((unsigned long)(sp) & ~(THREAD_SIZE - 1))) + +struct fiq_debugger_state { + struct fiq_glue_handler handler; + + int fiq; + int uart_irq; + int signal_irq; + int wakeup_irq; + bool wakeup_irq_no_set_wake; + struct clk *clk; + struct fiq_debugger_pdata *pdata; + struct platform_device *pdev; + + char debug_cmd[DEBUG_MAX]; + int debug_busy; + int debug_abort; + + char debug_buf[DEBUG_MAX]; + int debug_count; + + bool no_sleep; + bool debug_enable; + bool ignore_next_wakeup_irq; + struct timer_list sleep_timer; + spinlock_t sleep_timer_lock; + bool uart_enabled; + struct wake_lock debugger_wake_lock; + bool console_enable; + int current_cpu; + atomic_t unhandled_fiq_count; + bool in_fiq; + +#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE + struct console console; + struct tty_driver *tty_driver; + struct tty_struct *tty; + int tty_open_count; + struct fiq_debugger_ringbuf *tty_rbuf; + bool syslog_dumping; +#endif + + unsigned int last_irqs[NR_IRQS]; + unsigned int last_local_timer_irqs[NR_CPUS]; +}; + +#ifdef CONFIG_FIQ_DEBUGGER_NO_SLEEP +static bool initial_no_sleep = true; +#else +static bool initial_no_sleep; +#endif + +#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE +static bool initial_debug_enable = true; +static bool initial_console_enable = true; +#else +static bool initial_debug_enable; +static bool initial_console_enable; +#endif + +module_param_named(no_sleep, initial_no_sleep, bool, 0644); +module_param_named(debug_enable, initial_debug_enable, bool, 0644); +module_param_named(console_enable, initial_console_enable, bool, 0644); + +#ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON +static inline void enable_wakeup_irq(struct fiq_debugger_state *state) {} +static inline void disable_wakeup_irq(struct fiq_debugger_state *state) {} +#else +static inline void enable_wakeup_irq(struct fiq_debugger_state *state) +{ + if (state->wakeup_irq < 0) + return; + enable_irq(state->wakeup_irq); + if (!state->wakeup_irq_no_set_wake) + enable_irq_wake(state->wakeup_irq); +} +static inline void disable_wakeup_irq(struct fiq_debugger_state *state) +{ + if (state->wakeup_irq < 0) + return; + disable_irq_nosync(state->wakeup_irq); + if (!state->wakeup_irq_no_set_wake) + disable_irq_wake(state->wakeup_irq); +} +#endif + +static bool inline debug_have_fiq(struct fiq_debugger_state *state) +{ + return (state->fiq >= 0); +} + +static void debug_force_irq(struct fiq_debugger_state *state) +{ + unsigned int irq = state->signal_irq; + + if (WARN_ON(!debug_have_fiq(state))) + return; + if (state->pdata->force_irq) { + state->pdata->force_irq(state->pdev, irq); + } else { + struct irq_chip *chip = irq_get_chip(irq); + if (chip && chip->irq_retrigger) + chip->irq_retrigger(irq_get_irq_data(irq)); + } +} + +static void debug_uart_enable(struct fiq_debugger_state *state) +{ + if (state->clk) + clk_enable(state->clk); + if (state->pdata->uart_enable) + state->pdata->uart_enable(state->pdev); +} + +static void debug_uart_disable(struct fiq_debugger_state *state) +{ + if (state->pdata->uart_disable) + state->pdata->uart_disable(state->pdev); + if (state->clk) + clk_disable(state->clk); +} + +static void debug_uart_flush(struct fiq_debugger_state *state) +{ + if (state->pdata->uart_flush) + state->pdata->uart_flush(state->pdev); +} + +static void debug_puts(struct fiq_debugger_state *state, char *s) +{ + unsigned c; + while ((c = *s++)) { + if (c == '\n') + state->pdata->uart_putc(state->pdev, '\r'); + state->pdata->uart_putc(state->pdev, c); + } +} + +static void debug_prompt(struct fiq_debugger_state *state) +{ + debug_puts(state, "debug> "); +} + +int log_buf_copy(char *dest, int idx, int len); +static void dump_kernel_log(struct fiq_debugger_state *state) +{ + char buf[1024]; + int idx = 0; + int ret; + int saved_oip; + + /* setting oops_in_progress prevents log_buf_copy() + * from trying to take a spinlock which will make it + * very unhappy in some cases... + */ + saved_oip = oops_in_progress; + oops_in_progress = 1; + for (;;) { + ret = log_buf_copy(buf, idx, 1023); + if (ret <= 0) + break; + buf[ret] = 0; + debug_puts(state, buf); + idx += ret; + } + oops_in_progress = saved_oip; +} + +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 "???"; + } +} + +static int debug_printf(void *cookie, const char *fmt, ...) +{ + struct fiq_debugger_state *state = cookie; + char buf[256]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + debug_puts(state, buf); + return state->debug_abort; +} + +/* Safe outside fiq context */ +static int debug_printf_nfiq(void *cookie, const char *fmt, ...) +{ + struct fiq_debugger_state *state = cookie; + char buf[256]; + va_list ap; + unsigned long irq_flags; + + va_start(ap, fmt); + vsnprintf(buf, 128, fmt, ap); + va_end(ap); + + local_irq_save(irq_flags); + debug_puts(state, buf); + debug_uart_flush(state); + local_irq_restore(irq_flags); + return state->debug_abort; +} + +static void dump_regs(struct fiq_debugger_state *state, unsigned *regs) +{ + debug_printf(state, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + regs[0], regs[1], regs[2], regs[3]); + debug_printf(state, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + regs[4], regs[5], regs[6], regs[7]); + debug_printf(state, " r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n", + regs[8], regs[9], regs[10], regs[11], + mode_name(regs[16])); + if ((regs[16] & MODE_MASK) == USR_MODE) + debug_printf(state, " ip %08x sp %08x lr %08x pc %08x " + "cpsr %08x\n", regs[12], regs[13], regs[14], + regs[15], regs[16]); + else + debug_printf(state, " ip %08x sp %08x lr %08x pc %08x " + "cpsr %08x spsr %08x\n", regs[12], regs[13], + regs[14], regs[15], regs[16], regs[17]); +} + +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; +}; + +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"); +} + + +static void dump_allregs(struct fiq_debugger_state *state, unsigned *regs) +{ + struct mode_regs mode_regs; + dump_regs(state, regs); + get_mode_regs(&mode_regs); + debug_printf(state, " svc: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc); + debug_printf(state, " abt: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt); + debug_printf(state, " und: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und); + debug_printf(state, " irq: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq); + debug_printf(state, " fiq: r8 %08x r9 %08x r10 %08x r11 %08x " + "r12 %08x\n", + mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq, + mode_regs.r11_fiq, mode_regs.r12_fiq); + debug_printf(state, " fiq: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq); +} + +static void dump_irqs(struct fiq_debugger_state *state) +{ + int n; + unsigned int cpu; + + debug_printf(state, "irqnr total since-last status name\n"); + for (n = 0; n < NR_IRQS; n++) { + struct irqaction *act = irq_desc[n].action; + if (!act && !kstat_irqs(n)) + continue; + debug_printf(state, "%5d: %10u %11u %8x %s\n", n, + kstat_irqs(n), + kstat_irqs(n) - state->last_irqs[n], + irq_desc[n].status_use_accessors, + (act && act->name) ? act->name : "???"); + state->last_irqs[n] = kstat_irqs(n); + } + + for (cpu = 0; cpu < NR_CPUS; cpu++) { + + debug_printf(state, "LOC %d: %10u %11u\n", cpu, + __IRQ_STAT(cpu, local_timer_irqs), + __IRQ_STAT(cpu, local_timer_irqs) - + state->last_local_timer_irqs[cpu]); + state->last_local_timer_irqs[cpu] = + __IRQ_STAT(cpu, local_timer_irqs); + } +} + +struct stacktrace_state { + struct fiq_debugger_state *state; + unsigned int depth; +}; + +static int report_trace(struct stackframe *frame, void *d) +{ + struct stacktrace_state *sts = d; + + if (sts->depth) { + debug_printf(sts->state, + " 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; + } + debug_printf(sts->state, " ...\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_state *state, + 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))) { + debug_printf(state, " invalid frame pointer %p\n", tail); + return NULL; + } + if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) { + debug_printf(state, + " failed to copy frame pointer %p\n", tail); + return NULL; + } + + debug_printf(state, " %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 dump_stacktrace(struct fiq_debugger_state *state, + struct pt_regs * const 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.state = state; + *current_thread_info() = *real_thread_info; + + if (!current) + debug_printf(state, "current NULL\n"); + else + debug_printf(state, "pid: %d comm: %s\n", + current->pid, current->comm); + dump_regs(state, (unsigned *)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; + debug_printf(state, + " 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(state, tail); +} + +static void do_ps(struct fiq_debugger_state *state) +{ + struct task_struct *g; + struct task_struct *p; + unsigned task_state; + static const char stat_nam[] = "RSDTtZX"; + + debug_printf(state, "pid ppid prio task pc\n"); + read_lock(&tasklist_lock); + do_each_thread(g, p) { + task_state = p->state ? __ffs(p->state) + 1 : 0; + debug_printf(state, + "%5d %5d %4d ", p->pid, p->parent->pid, p->prio); + debug_printf(state, "%-13.13s %c", p->comm, + task_state >= sizeof(stat_nam) ? '?' : stat_nam[task_state]); + if (task_state == TASK_RUNNING) + debug_printf(state, " running\n"); + else + debug_printf(state, " %08lx\n", thread_saved_pc(p)); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); +} + +#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE +static void begin_syslog_dump(struct fiq_debugger_state *state) +{ + state->syslog_dumping = true; +} + +static void end_syslog_dump(struct fiq_debugger_state *state) +{ + state->syslog_dumping = false; +} +#else +extern int do_syslog(int type, char __user *bug, int count); +static void begin_syslog_dump(struct fiq_debugger_state *state) +{ + do_syslog(5 /* clear */, NULL, 0); +} + +static void end_syslog_dump(struct fiq_debugger_state *state) +{ + char buf[128]; + int ret; + int idx = 0; + + while (1) { + ret = log_buf_copy(buf, idx, sizeof(buf) - 1); + if (ret <= 0) + break; + buf[ret] = 0; + debug_printf(state, "%s", buf); + idx += ret; + } +} +#endif + +static void do_sysrq(struct fiq_debugger_state *state, char rq) +{ + begin_syslog_dump(state); + handle_sysrq(rq); + end_syslog_dump(state); +} + +/* This function CANNOT be called in FIQ context */ +static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd) +{ + if (!strcmp(cmd, "ps")) + do_ps(state); + if (!strcmp(cmd, "sysrq")) + do_sysrq(state, 'h'); + if (!strncmp(cmd, "sysrq ", 6)) + do_sysrq(state, cmd[6]); +} + +static void debug_help(struct fiq_debugger_state *state) +{ + debug_printf(state, "FIQ Debugger commands:\n" + " pc PC status\n" + " regs Register dump\n" + " allregs Extended Register dump\n" + " bt Stack trace\n" + " reboot Reboot\n" + " irqs Interupt status\n" + " kmsg Kernel log\n" + " version Kernel version\n"); + debug_printf(state, " sleep Allow sleep while in FIQ\n" + " nosleep Disable sleep while in FIQ\n" + " console Switch terminal to console\n" + " cpu Current CPU\n" + " cpu Switch to CPU\n"); + debug_printf(state, " ps Process list\n" + " sysrq sysrq options\n" + " sysrq Execute sysrq with \n"); +} + +static void take_affinity(void *info) +{ + struct fiq_debugger_state *state = info; + struct cpumask cpumask; + + cpumask_clear(&cpumask); + cpumask_set_cpu(get_cpu(), &cpumask); + + irq_set_affinity(state->uart_irq, &cpumask); +} + +static void switch_cpu(struct fiq_debugger_state *state, int cpu) +{ + if (!debug_have_fiq(state)) + smp_call_function_single(cpu, take_affinity, state, false); + state->current_cpu = cpu; +} + +static bool debug_fiq_exec(struct fiq_debugger_state *state, + const char *cmd, unsigned *regs, void *svc_sp) +{ + bool signal_helper = false; + + if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) { + debug_help(state); + } else if (!strcmp(cmd, "pc")) { + debug_printf(state, " pc %08x cpsr %08x mode %s\n", + regs[15], regs[16], mode_name(regs[16])); + } else if (!strcmp(cmd, "regs")) { + dump_regs(state, regs); + } else if (!strcmp(cmd, "allregs")) { + dump_allregs(state, regs); + } else if (!strcmp(cmd, "bt")) { + dump_stacktrace(state, (struct pt_regs *)regs, 100, svc_sp); + } else if (!strcmp(cmd, "reboot")) { + arch_reset(0, 0); + } else if (!strcmp(cmd, "irqs")) { + dump_irqs(state); + } else if (!strcmp(cmd, "kmsg")) { + dump_kernel_log(state); + } else if (!strcmp(cmd, "version")) { + debug_printf(state, "%s\n", linux_banner); + } else if (!strcmp(cmd, "sleep")) { + state->no_sleep = false; + debug_printf(state, "enabling sleep\n"); + } else if (!strcmp(cmd, "nosleep")) { + state->no_sleep = true; + debug_printf(state, "disabling sleep\n"); + } else if (!strcmp(cmd, "console")) { + state->console_enable = true; + debug_printf(state, "console mode\n"); + } else if (!strcmp(cmd, "cpu")) { + debug_printf(state, "cpu %d\n", state->current_cpu); + } else if (!strncmp(cmd, "cpu ", 4)) { + unsigned long cpu = 0; + if (strict_strtoul(cmd + 4, 10, &cpu) == 0) + switch_cpu(state, cpu); + else + debug_printf(state, "invalid cpu\n"); + debug_printf(state, "cpu %d\n", state->current_cpu); + } else { + if (state->debug_busy) { + debug_printf(state, + "command processor busy. trying to abort.\n"); + state->debug_abort = -1; + } else { + strcpy(state->debug_cmd, cmd); + state->debug_busy = 1; + } + + return true; + } + if (!state->console_enable) + debug_prompt(state); + + return signal_helper; +} + +static void sleep_timer_expired(unsigned long data) +{ + struct fiq_debugger_state *state = (struct fiq_debugger_state *)data; + unsigned long flags; + + spin_lock_irqsave(&state->sleep_timer_lock, flags); + if (state->uart_enabled && !state->no_sleep) { + if (state->debug_enable && !state->console_enable) { + state->debug_enable = false; + debug_printf_nfiq(state, "suspending fiq debugger\n"); + } + state->ignore_next_wakeup_irq = true; + debug_uart_disable(state); + state->uart_enabled = false; + enable_wakeup_irq(state); + } + wake_unlock(&state->debugger_wake_lock); + spin_unlock_irqrestore(&state->sleep_timer_lock, flags); +} + +static void handle_wakeup(struct fiq_debugger_state *state) +{ + unsigned long flags; + + spin_lock_irqsave(&state->sleep_timer_lock, flags); + if (state->wakeup_irq >= 0 && state->ignore_next_wakeup_irq) { + state->ignore_next_wakeup_irq = false; + } else if (!state->uart_enabled) { + wake_lock(&state->debugger_wake_lock); + debug_uart_enable(state); + state->uart_enabled = true; + disable_wakeup_irq(state); + mod_timer(&state->sleep_timer, jiffies + HZ / 2); + } + spin_unlock_irqrestore(&state->sleep_timer_lock, flags); +} + +static irqreturn_t wakeup_irq_handler(int irq, void *dev) +{ + struct fiq_debugger_state *state = dev; + + if (!state->no_sleep) + debug_puts(state, "WAKEUP\n"); + handle_wakeup(state); + + return IRQ_HANDLED; +} + + +static void debug_handle_irq_context(struct fiq_debugger_state *state) +{ + if (!state->no_sleep) { + unsigned long flags; + + spin_lock_irqsave(&state->sleep_timer_lock, flags); + wake_lock(&state->debugger_wake_lock); + mod_timer(&state->sleep_timer, jiffies + HZ * 5); + spin_unlock_irqrestore(&state->sleep_timer_lock, flags); + } +#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) + if (state->tty) { + int i; + int count = fiq_debugger_ringbuf_level(state->tty_rbuf); + for (i = 0; i < count; i++) { + int c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0); + tty_insert_flip_char(state->tty, c, TTY_NORMAL); + if (!fiq_debugger_ringbuf_consume(state->tty_rbuf, 1)) + pr_warn("fiq tty failed to consume byte\n"); + } + tty_flip_buffer_push(state->tty); + } +#endif + if (state->debug_busy) { + debug_irq_exec(state, state->debug_cmd); + debug_prompt(state); + state->debug_busy = 0; + } +} + +static int debug_getc(struct fiq_debugger_state *state) +{ + return state->pdata->uart_getc(state->pdev); +} + +static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state, + int this_cpu, void *regs, void *svc_sp) +{ + int c; + static int last_c; + int count = 0; + bool signal_helper = false; + + if (this_cpu != state->current_cpu) { + if (state->in_fiq) + return false; + + if (atomic_inc_return(&state->unhandled_fiq_count) != + MAX_UNHANDLED_FIQ_COUNT) + return false; + + debug_printf(state, "fiq_debugger: cpu %d not responding, " + "reverting to cpu %d\n", state->current_cpu, + this_cpu); + + atomic_set(&state->unhandled_fiq_count, 0); + switch_cpu(state, this_cpu); + return false; + } + + state->in_fiq = true; + + while ((c = debug_getc(state)) != FIQ_DEBUGGER_NO_CHAR) { + count++; + if (!state->debug_enable) { + if ((c == 13) || (c == 10)) { + state->debug_enable = true; + state->debug_count = 0; + debug_prompt(state); + } + } else if (c == FIQ_DEBUGGER_BREAK) { + state->console_enable = false; + debug_puts(state, "fiq debugger mode\n"); + state->debug_count = 0; + debug_prompt(state); +#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE + } else if (state->console_enable && state->tty_rbuf) { + fiq_debugger_ringbuf_push(state->tty_rbuf, c); + signal_helper = true; +#endif + } else if ((c >= ' ') && (c < 127)) { + if (state->debug_count < (DEBUG_MAX - 1)) { + state->debug_buf[state->debug_count++] = c; + state->pdata->uart_putc(state->pdev, c); + } + } else if ((c == 8) || (c == 127)) { + if (state->debug_count > 0) { + state->debug_count--; + state->pdata->uart_putc(state->pdev, 8); + state->pdata->uart_putc(state->pdev, ' '); + state->pdata->uart_putc(state->pdev, 8); + } + } else if ((c == 13) || (c == 10)) { + if (c == '\r' || (c == '\n' && last_c != '\r')) { + state->pdata->uart_putc(state->pdev, '\r'); + state->pdata->uart_putc(state->pdev, '\n'); + } + if (state->debug_count) { + state->debug_buf[state->debug_count] = 0; + state->debug_count = 0; + signal_helper |= + debug_fiq_exec(state, state->debug_buf, + regs, svc_sp); + } else { + debug_prompt(state); + } + } + last_c = c; + } + debug_uart_flush(state); + if (state->pdata->fiq_ack) + state->pdata->fiq_ack(state->pdev, state->fiq); + + /* poke sleep timer if necessary */ + if (state->debug_enable && !state->no_sleep) + signal_helper = true; + + atomic_set(&state->unhandled_fiq_count, 0); + state->in_fiq = false; + + return signal_helper; +} + +static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp) +{ + struct fiq_debugger_state *state = + container_of(h, struct fiq_debugger_state, handler); + unsigned int this_cpu = THREAD_INFO(svc_sp)->cpu; + bool need_irq; + + need_irq = debug_handle_uart_interrupt(state, this_cpu, regs, svc_sp); + if (need_irq) + debug_force_irq(state); +} + +/* + * When not using FIQs, we only use this single interrupt as an entry point. + * This just effectively takes over the UART interrupt and does all the work + * in this context. + */ +static irqreturn_t debug_uart_irq(int irq, void *dev) +{ + struct fiq_debugger_state *state = dev; + bool not_done; + + handle_wakeup(state); + + /* handle the debugger irq in regular context */ + not_done = debug_handle_uart_interrupt(state, smp_processor_id(), + get_irq_regs(), + current_thread_info()); + if (not_done) + debug_handle_irq_context(state); + + return IRQ_HANDLED; +} + +/* + * If FIQs are used, not everything can happen in fiq context. + * FIQ handler does what it can and then signals this interrupt to finish the + * job in irq context. + */ +static irqreturn_t debug_signal_irq(int irq, void *dev) +{ + struct fiq_debugger_state *state = dev; + + if (state->pdata->force_irq_ack) + state->pdata->force_irq_ack(state->pdev, state->signal_irq); + + debug_handle_irq_context(state); + + return IRQ_HANDLED; +} + +static void debug_resume(struct fiq_glue_handler *h) +{ + struct fiq_debugger_state *state = + container_of(h, struct fiq_debugger_state, handler); + if (state->pdata->uart_resume) + state->pdata->uart_resume(state->pdev); +} + +#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) +struct tty_driver *debug_console_device(struct console *co, int *index) +{ + struct fiq_debugger_state *state; + state = container_of(co, struct fiq_debugger_state, console); + *index = 0; + return state->tty_driver; +} + +static void debug_console_write(struct console *co, + const char *s, unsigned int count) +{ + struct fiq_debugger_state *state; + + state = container_of(co, struct fiq_debugger_state, console); + + if (!state->console_enable && !state->syslog_dumping) + return; + + debug_uart_enable(state); + while (count--) { + if (*s == '\n') + state->pdata->uart_putc(state->pdev, '\r'); + state->pdata->uart_putc(state->pdev, *s++); + } + debug_uart_flush(state); + debug_uart_disable(state); +} + +static struct console fiq_debugger_console = { + .name = "ttyFIQ", + .device = debug_console_device, + .write = debug_console_write, + .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, +}; + +int fiq_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct fiq_debugger_state *state = tty->driver->driver_state; + if (state->tty_open_count++) + return 0; + + tty->driver_data = state; + state->tty = tty; + return 0; +} + +void fiq_tty_close(struct tty_struct *tty, struct file *filp) +{ + struct fiq_debugger_state *state = tty->driver_data; + if (--state->tty_open_count) + return; + state->tty = NULL; +} + +int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + int i; + struct fiq_debugger_state *state = tty->driver_data; + + if (!state->console_enable) + return count; + + debug_uart_enable(state); + for (i = 0; i < count; i++) + state->pdata->uart_putc(state->pdev, *buf++); + debug_uart_disable(state); + + return count; +} + +int fiq_tty_write_room(struct tty_struct *tty) +{ + return 1024; +} + +static const struct tty_operations fiq_tty_driver_ops = { + .write = fiq_tty_write, + .write_room = fiq_tty_write_room, + .open = fiq_tty_open, + .close = fiq_tty_close, +}; + +static int fiq_debugger_tty_init(struct fiq_debugger_state *state) +{ + int ret = -EINVAL; + + state->tty_driver = alloc_tty_driver(1); + if (!state->tty_driver) { + pr_err("Failed to allocate fiq debugger tty\n"); + return -ENOMEM; + } + + state->tty_driver->owner = THIS_MODULE; + state->tty_driver->driver_name = "fiq-debugger"; + state->tty_driver->name = "ttyFIQ"; + state->tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + state->tty_driver->subtype = SERIAL_TYPE_NORMAL; + state->tty_driver->init_termios = tty_std_termios; + state->tty_driver->init_termios.c_cflag = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; + state->tty_driver->init_termios.c_ispeed = + state->tty_driver->init_termios.c_ospeed = 115200; + state->tty_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(state->tty_driver, &fiq_tty_driver_ops); + state->tty_driver->driver_state = state; + + ret = tty_register_driver(state->tty_driver); + if (ret) { + pr_err("Failed to register fiq tty: %d\n", ret); + goto err; + } + + state->tty_rbuf = fiq_debugger_ringbuf_alloc(1024); + if (!state->tty_rbuf) { + pr_err("Failed to allocate fiq debugger ringbuf\n"); + ret = -ENOMEM; + goto err; + } + + pr_info("Registered FIQ tty driver %p\n", state->tty_driver); + return 0; + +err: + fiq_debugger_ringbuf_free(state->tty_rbuf); + state->tty_rbuf = NULL; + put_tty_driver(state->tty_driver); + return ret; +} +#endif + +static int fiq_debugger_dev_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fiq_debugger_state *state = platform_get_drvdata(pdev); + + if (state->pdata->uart_dev_suspend) + return state->pdata->uart_dev_suspend(pdev); + return 0; +} + +static int fiq_debugger_dev_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fiq_debugger_state *state = platform_get_drvdata(pdev); + + if (state->pdata->uart_dev_resume) + return state->pdata->uart_dev_resume(pdev); + return 0; +} + +static int fiq_debugger_probe(struct platform_device *pdev) +{ + int ret; + struct fiq_debugger_pdata *pdata = dev_get_platdata(&pdev->dev); + struct fiq_debugger_state *state; + int fiq; + int uart_irq; + + if (!pdata->uart_getc || !pdata->uart_putc) + return -EINVAL; + if ((pdata->uart_enable && !pdata->uart_disable) || + (!pdata->uart_enable && pdata->uart_disable)) + return -EINVAL; + + fiq = platform_get_irq_byname(pdev, "fiq"); + uart_irq = platform_get_irq_byname(pdev, "uart_irq"); + + /* uart_irq mode and fiq mode are mutually exclusive, but one of them + * is required */ + if ((uart_irq < 0 && fiq < 0) || (uart_irq >= 0 && fiq >= 0)) + return -EINVAL; + if (fiq >= 0 && !pdata->fiq_enable) + return -EINVAL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + setup_timer(&state->sleep_timer, sleep_timer_expired, + (unsigned long)state); + state->pdata = pdata; + state->pdev = pdev; + state->no_sleep = initial_no_sleep; + state->debug_enable = initial_debug_enable; + state->console_enable = initial_console_enable; + + state->fiq = fiq; + state->uart_irq = uart_irq; + state->signal_irq = platform_get_irq_byname(pdev, "signal"); + state->wakeup_irq = platform_get_irq_byname(pdev, "wakeup"); + + platform_set_drvdata(pdev, state); + + spin_lock_init(&state->sleep_timer_lock); + + if (state->wakeup_irq < 0 && debug_have_fiq(state)) + state->no_sleep = true; + state->ignore_next_wakeup_irq = !state->no_sleep; + + wake_lock_init(&state->debugger_wake_lock, + WAKE_LOCK_SUSPEND, "serial-debug"); + + state->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(state->clk)) + state->clk = NULL; + + /* do not call pdata->uart_enable here since uart_init may still + * need to do some initialization before uart_enable can work. + * So, only try to manage the clock during init. + */ + if (state->clk) + clk_enable(state->clk); + + if (pdata->uart_init) { + ret = pdata->uart_init(pdev); + if (ret) + goto err_uart_init; + } + + debug_printf_nfiq(state, "\n", + state->no_sleep ? "" : "twice "); + + if (debug_have_fiq(state)) { + state->handler.fiq = debug_fiq; + state->handler.resume = debug_resume; + ret = fiq_glue_register_handler(&state->handler); + if (ret) { + pr_err("%s: could not install fiq handler\n", __func__); + goto err_register_fiq; + } + + pdata->fiq_enable(pdev, state->fiq, 1); + } else { + ret = request_irq(state->uart_irq, debug_uart_irq, + IRQF_NO_SUSPEND, "debug", state); + if (ret) { + pr_err("%s: could not install irq handler\n", __func__); + goto err_register_irq; + } + + /* for irq-only mode, we want this irq to wake us up, if it + * can. + */ + enable_irq_wake(state->uart_irq); + } + + if (state->clk) + clk_disable(state->clk); + + if (state->signal_irq >= 0) { + ret = request_irq(state->signal_irq, debug_signal_irq, + IRQF_TRIGGER_RISING, "debug-signal", state); + if (ret) + pr_err("serial_debugger: could not install signal_irq"); + } + + if (state->wakeup_irq >= 0) { + ret = request_irq(state->wakeup_irq, wakeup_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_DISABLED, + "debug-wakeup", state); + if (ret) { + pr_err("serial_debugger: " + "could not install wakeup irq\n"); + state->wakeup_irq = -1; + } else { + ret = enable_irq_wake(state->wakeup_irq); + if (ret) { + pr_err("serial_debugger: " + "could not enable wakeup\n"); + state->wakeup_irq_no_set_wake = true; + } + } + } + if (state->no_sleep) + handle_wakeup(state); + +#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) + state->console = fiq_debugger_console; + register_console(&state->console); + fiq_debugger_tty_init(state); +#endif + return 0; + +err_register_irq: +err_register_fiq: + if (pdata->uart_free) + pdata->uart_free(pdev); +err_uart_init: + if (state->clk) + clk_disable(state->clk); + if (state->clk) + clk_put(state->clk); + wake_lock_destroy(&state->debugger_wake_lock); + platform_set_drvdata(pdev, NULL); + kfree(state); + return ret; +} + +static const struct dev_pm_ops fiq_debugger_dev_pm_ops = { + .suspend = fiq_debugger_dev_suspend, + .resume = fiq_debugger_dev_resume, +}; + +static struct platform_driver fiq_debugger_driver = { + .probe = fiq_debugger_probe, + .driver = { + .name = "fiq_debugger", + .pm = &fiq_debugger_dev_pm_ops, + }, +}; + +static int __init fiq_debugger_init(void) +{ + return platform_driver_register(&fiq_debugger_driver); +} + +postcore_initcall(fiq_debugger_init); diff --git a/arch/arm/common/fiq_debugger_ringbuf.h b/arch/arm/common/fiq_debugger_ringbuf.h new file mode 100644 index 000000000000..2649b5581088 --- /dev/null +++ b/arch/arm/common/fiq_debugger_ringbuf.h @@ -0,0 +1,94 @@ +/* + * arch/arm/common/fiq_debugger_ringbuf.c + * + * simple lockless ringbuffer + * + * Copyright (C) 2010 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 +#include + +struct fiq_debugger_ringbuf { + int len; + int head; + int tail; + u8 buf[]; +}; + + +static inline struct fiq_debugger_ringbuf *fiq_debugger_ringbuf_alloc(int len) +{ + struct fiq_debugger_ringbuf *rbuf; + + rbuf = kzalloc(sizeof(*rbuf) + len, GFP_KERNEL); + if (rbuf == NULL) + return NULL; + + rbuf->len = len; + rbuf->head = 0; + rbuf->tail = 0; + smp_mb(); + + return rbuf; +} + +static inline void fiq_debugger_ringbuf_free(struct fiq_debugger_ringbuf *rbuf) +{ + kfree(rbuf); +} + +static inline int fiq_debugger_ringbuf_level(struct fiq_debugger_ringbuf *rbuf) +{ + int level = rbuf->head - rbuf->tail; + + if (level < 0) + level = rbuf->len + level; + + return level; +} + +static inline int fiq_debugger_ringbuf_room(struct fiq_debugger_ringbuf *rbuf) +{ + return rbuf->len - fiq_debugger_ringbuf_level(rbuf) - 1; +} + +static inline u8 +fiq_debugger_ringbuf_peek(struct fiq_debugger_ringbuf *rbuf, int i) +{ + return rbuf->buf[(rbuf->tail + i) % rbuf->len]; +} + +static inline int +fiq_debugger_ringbuf_consume(struct fiq_debugger_ringbuf *rbuf, int count) +{ + count = min(count, fiq_debugger_ringbuf_level(rbuf)); + + rbuf->tail = (rbuf->tail + count) % rbuf->len; + smp_mb(); + + return count; +} + +static inline int +fiq_debugger_ringbuf_push(struct fiq_debugger_ringbuf *rbuf, u8 datum) +{ + if (fiq_debugger_ringbuf_room(rbuf) == 0) + return 0; + + rbuf->buf[rbuf->head] = datum; + smp_mb(); + rbuf->head = (rbuf->head + 1) % rbuf->len; + smp_mb(); + + return 1; +} diff --git a/arch/arm/include/asm/fiq_debugger.h b/arch/arm/include/asm/fiq_debugger.h new file mode 100644 index 000000000000..4d274883ba6a --- /dev/null +++ b/arch/arm/include/asm/fiq_debugger.h @@ -0,0 +1,64 @@ +/* + * arch/arm/include/asm/fiq_debugger.h + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross + * + * 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 _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_ +#define _ARCH_ARM_MACH_TEGRA_FIQ_DEBUGGER_H_ + +#include + +#define FIQ_DEBUGGER_NO_CHAR NO_POLL_CHAR +#define FIQ_DEBUGGER_BREAK 0x00ff0100 + +#define FIQ_DEBUGGER_FIQ_IRQ_NAME "fiq" +#define FIQ_DEBUGGER_SIGNAL_IRQ_NAME "signal" +#define FIQ_DEBUGGER_WAKEUP_IRQ_NAME "wakeup" + +/** + * struct fiq_debugger_pdata - fiq debugger platform data + * @uart_resume: used to restore uart state right before enabling + * the fiq. + * @uart_enable: Do the work necessary to communicate with the uart + * hw (enable clocks, etc.). This must be ref-counted. + * @uart_disable: Do the work necessary to disable the uart hw + * (disable clocks, etc.). This must be ref-counted. + * @uart_dev_suspend: called during PM suspend, generally not needed + * for real fiq mode debugger. + * @uart_dev_resume: called during PM resume, generally not needed + * for real fiq mode debugger. + */ +struct fiq_debugger_pdata { + int (*uart_init)(struct platform_device *pdev); + void (*uart_free)(struct platform_device *pdev); + int (*uart_resume)(struct platform_device *pdev); + int (*uart_getc)(struct platform_device *pdev); + void (*uart_putc)(struct platform_device *pdev, unsigned int c); + void (*uart_flush)(struct platform_device *pdev); + void (*uart_enable)(struct platform_device *pdev); + void (*uart_disable)(struct platform_device *pdev); + + int (*uart_dev_suspend)(struct platform_device *pdev); + int (*uart_dev_resume)(struct platform_device *pdev); + + void (*fiq_enable)(struct platform_device *pdev, unsigned int fiq, + bool enable); + void (*fiq_ack)(struct platform_device *pdev, unsigned int fiq); + + void (*force_irq)(struct platform_device *pdev, unsigned int irq); + void (*force_irq_ack)(struct platform_device *pdev, unsigned int irq); +}; + +#endif From b17599de9c62257fbd85f4fc250f5b848ba8d5eb Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Thu, 21 Oct 2010 14:19:31 -0700 Subject: [PATCH 071/358] staging: remove Greg's TODO, now obsolete. Signed-off-by: Brian Swetland --- drivers/staging/android/TODO | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 drivers/staging/android/TODO diff --git a/drivers/staging/android/TODO b/drivers/staging/android/TODO deleted file mode 100644 index b15fb0d6b152..000000000000 --- a/drivers/staging/android/TODO +++ /dev/null @@ -1,10 +0,0 @@ -TODO: - - checkpatch.pl cleanups - - sparse fixes - - rename files to be not so "generic" - - make sure things build as modules properly - - add proper arch dependencies as needed - - audit userspace interfaces to make sure they are sane - -Please send patches to Greg Kroah-Hartman and Cc: -Brian Swetland From 9edb824ebe13a16a17b989e07495c6f2e46982fa Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 15 Jun 2011 17:21:57 -0700 Subject: [PATCH 072/358] Move x86_64 idle notifiers to generic Move the x86_64 idle notifiers originally by Andi Kleen and Venkatesh Pallipadi to generic. Change-Id: Idf29cda15be151f494ff245933c12462643388d5 Acked-by: Nicolas Pitre Signed-off-by: Todd Poynor --- arch/x86/include/asm/idle.h | 7 ------- arch/x86/kernel/process.c | 17 ++--------------- include/linux/cpu.h | 7 +++++++ kernel/cpu.c | 20 ++++++++++++++++++++ 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/arch/x86/include/asm/idle.h b/arch/x86/include/asm/idle.h index c5d1785373ed..02bab09707f2 100644 --- a/arch/x86/include/asm/idle.h +++ b/arch/x86/include/asm/idle.h @@ -1,13 +1,6 @@ #ifndef _ASM_X86_IDLE_H #define _ASM_X86_IDLE_H -#define IDLE_START 1 -#define IDLE_END 2 - -struct notifier_block; -void idle_notifier_register(struct notifier_block *n); -void idle_notifier_unregister(struct notifier_block *n); - #ifdef CONFIG_X86_64 void enter_idle(void); void exit_idle(void); diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 3fb8d95ab8b5..ed8975a3538e 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -40,19 +40,6 @@ __visible DEFINE_PER_CPU_SHARED_ALIGNED(struct tss_struct, init_tss) = INIT_TSS; #ifdef CONFIG_X86_64 static DEFINE_PER_CPU(unsigned char, is_idle); -static ATOMIC_NOTIFIER_HEAD(idle_notifier); - -void idle_notifier_register(struct notifier_block *n) -{ - atomic_notifier_chain_register(&idle_notifier, n); -} -EXPORT_SYMBOL_GPL(idle_notifier_register); - -void idle_notifier_unregister(struct notifier_block *n) -{ - atomic_notifier_chain_unregister(&idle_notifier, n); -} -EXPORT_SYMBOL_GPL(idle_notifier_unregister); #endif struct kmem_cache *task_xstate_cachep; @@ -257,14 +244,14 @@ static inline void play_dead(void) void enter_idle(void) { this_cpu_write(is_idle, 1); - atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL); + idle_notifier_call_chain(IDLE_START); } static void __exit_idle(void) { if (x86_test_and_clear_bit_percpu(0, is_idle) == 0) return; - atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL); + idle_notifier_call_chain(IDLE_END); } /* Called from interrupts to signify idle end */ diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 03e235ad1bba..6b1d2ddfb0e2 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -226,4 +226,11 @@ void arch_cpu_idle_enter(void); void arch_cpu_idle_exit(void); void arch_cpu_idle_dead(void); +#define IDLE_START 1 +#define IDLE_END 2 + +void idle_notifier_register(struct notifier_block *n); +void idle_notifier_unregister(struct notifier_block *n); +void idle_notifier_call_chain(unsigned long val); + #endif /* _LINUX_CPU_H_ */ diff --git a/kernel/cpu.c b/kernel/cpu.c index deff2e693766..2434ed358ee7 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -720,3 +720,23 @@ void init_cpu_online(const struct cpumask *src) { cpumask_copy(to_cpumask(cpu_online_bits), src); } + +static ATOMIC_NOTIFIER_HEAD(idle_notifier); + +void idle_notifier_register(struct notifier_block *n) +{ + atomic_notifier_chain_register(&idle_notifier, n); +} +EXPORT_SYMBOL_GPL(idle_notifier_register); + +void idle_notifier_unregister(struct notifier_block *n) +{ + atomic_notifier_chain_unregister(&idle_notifier, n); +} +EXPORT_SYMBOL_GPL(idle_notifier_unregister); + +void idle_notifier_call_chain(unsigned long val) +{ + atomic_notifier_call_chain(&idle_notifier, val, NULL); +} +EXPORT_SYMBOL_GPL(idle_notifier_call_chain); From 2fe6a44b9a75dd77d60c43b5e337f92f62f46974 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 15 Jun 2011 17:44:50 -0700 Subject: [PATCH 073/358] ARM: Call idle notifiers Change-Id: Id833e61c13baa1783705ac9e9046d1f0cc90c95e Acked-by: Nicolas Pitre Signed-off-by: Todd Poynor --- arch/arm/kernel/process.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index e49bf644d703..dc7eda3b4ee4 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -187,6 +187,7 @@ void arch_cpu_idle_prepare(void) void arch_cpu_idle_enter(void) { + idle_notifier_call_chain(IDLE_START); ledtrig_cpu(CPU_LED_IDLE_START); #ifdef CONFIG_PL310_ERRATA_769419 wmb(); @@ -196,6 +197,7 @@ void arch_cpu_idle_enter(void) void arch_cpu_idle_exit(void) { ledtrig_cpu(CPU_LED_IDLE_END); + idle_notifier_call_chain(IDLE_END); } #ifdef CONFIG_HOTPLUG_CPU From 4686353ef9f4b08097249171278dd95775f8b991 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Tue, 22 Jun 2010 11:26:45 -0700 Subject: [PATCH 074/358] cpufreq: interactive: New 'interactive' governor This governor is designed for latency-sensitive workloads, such as interactive user interfaces. The interactive governor aims to be significantly more responsive to ramp CPU quickly up when CPU-intensive activity begins. Existing governors sample CPU load at a particular rate, typically every X ms. This can lead to under-powering UI threads for the period of time during which the user begins interacting with a previously-idle system until the next sample period happens. The 'interactive' governor uses a different approach. Instead of sampling the CPU at a specified rate, the governor will check whether to scale the CPU frequency up soon after coming out of idle. When the CPU comes out of idle, a timer is configured to fire within 1-2 ticks. If the CPU is very busy from exiting idle to when the timer fires then we assume the CPU is underpowered and ramp to MAX speed. If the CPU was not sufficiently busy to immediately ramp to MAX speed, then the governor evaluates the CPU load since the last speed adjustment, choosing the highest value between that longer-term load or the short-term load since idle exit to determine the CPU speed to ramp to. A realtime thread is used for scaling up, giving the remaining tasks the CPU performance benefit, unlike existing governors which are more likely to schedule rampup work to occur after your performance starved tasks have completed. The tuneables for this governor are: /sys/devices/system/cpu/cpufreq/interactive/min_sample_time: The minimum amount of time to spend at the current frequency before ramping down. This is to ensure that the governor has seen enough historic CPU load data to determine the appropriate workload. Default is 80000 uS. /sys/devices/system/cpu/cpufreq/interactive/go_maxspeed_load The CPU load at which to ramp to max speed. Default is 85. Change-Id: Ib2b362607c62f7c56d35f44a9ef3280f98c17585 Signed-off-by: Mike Chan Signed-off-by: Todd Poynor Bug: 3152864 --- Documentation/cpu-freq/governors.txt | 37 ++ drivers/cpufreq/Kconfig | 27 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/cpufreq_interactive.c | 706 ++++++++++++++++++++++++++ include/linux/cpufreq.h | 3 + 5 files changed, 774 insertions(+) create mode 100644 drivers/cpufreq/cpufreq_interactive.c diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index 77ec21574fb1..5726f1563640 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -28,6 +28,7 @@ Contents: 2.3 Userspace 2.4 Ondemand 2.5 Conservative +2.6 Interactive 3. The Governor Interface in the CPUfreq Core @@ -218,6 +219,42 @@ a decision on when to decrease the frequency while running in any speed. Load for frequency increase is still evaluated every sampling rate. +2.6 Interactive +--------------- + +The CPUfreq governor "interactive" is designed for latency-sensitive, +interactive workloads. This governor sets the CPU speed depending on +usage, similar to "ondemand" and "conservative" governors. However, +the governor is more aggressive about scaling the CPU speed up in +response to CPU-intensive activity. + +Sampling the CPU load every X ms can lead to under-powering the CPU +for X ms, leading to dropped frames, stuttering UI, etc. Instead of +sampling the cpu at a specified rate, the interactive governor will +check whether to scale the cpu frequency up soon after coming out of +idle. When the cpu comes out of idle, a timer is configured to fire +within 1-2 ticks. If the cpu is very busy between exiting idle and +when the timer fires then we assume the cpu is underpowered and ramp +to MAX speed. + +If the cpu was not sufficiently busy to immediately ramp to MAX speed, +then governor evaluates the cpu load since the last speed adjustment, +choosing the highest value between that longer-term load or the +short-term load since idle exit to determine the cpu speed to ramp to. + +The tuneable values for this governor are: + +min_sample_time: The minimum amount of time to spend at the current +frequency before ramping down. This is to ensure that the governor has +seen enough historic cpu load data to determine the appropriate +workload. Default is 80000 uS. + +go_maxspeed_load: The CPU load at which to ramp to max speed. Default +is 85. + +timer_rate: Sample rate for reevaluating cpu load when the system is +not idle. Default is 30000 uS. + 3. The Governor Interface in the CPUfreq Core ============================================= diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 4b029c0944af..f74f60340709 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -102,6 +102,16 @@ config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE Be aware that not all cpufreq drivers support the conservative governor. If unsure have a look at the help section of the driver. Fallback governor will be the performance governor. + +config CPU_FREQ_DEFAULT_GOV_INTERACTIVE + bool "interactive" + select CPU_FREQ_GOV_INTERACTIVE + help + Use the CPUFreq governor 'interactive' as default. This allows + you to get a full dynamic cpu frequency capable system by simply + loading your cpufreq low-level hardware driver, using the + 'interactive' governor for latency-sensitive workloads. + endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -159,6 +169,23 @@ config CPU_FREQ_GOV_ONDEMAND If in doubt, say N. +config CPU_FREQ_GOV_INTERACTIVE + tristate "'interactive' cpufreq policy governor" + help + 'interactive' - This driver adds a dynamic cpufreq policy governor + designed for latency-sensitive workloads. + + This governor attempts to reduce the latency of clock + increases so that the system is more responsive to + interactive workloads. + + To compile this driver as a module, choose M here: the + module will be called cpufreq_interactive. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + config CPU_FREQ_GOV_CONSERVATIVE tristate "'conservative' cpufreq governor" depends on CPU_FREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 74945652dd7a..0fd80cbcd730 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o +obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c new file mode 100644 index 000000000000..8fc147c66f95 --- /dev/null +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -0,0 +1,706 @@ +/* + * drivers/cpufreq/cpufreq_interactive.c + * + * Copyright (C) 2010 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. + * + * Author: Mike Chan (mike@android.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static atomic_t active_count = ATOMIC_INIT(0); + +struct cpufreq_interactive_cpuinfo { + struct timer_list cpu_timer; + int timer_idlecancel; + u64 time_in_idle; + u64 idle_exit_time; + u64 timer_run_time; + int idling; + u64 freq_change_time; + u64 freq_change_time_in_idle; + struct cpufreq_policy *policy; + struct cpufreq_frequency_table *freq_table; + unsigned int target_freq; + int governor_enabled; +}; + +static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); + +/* Workqueues handle frequency scaling */ +static struct task_struct *up_task; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_down_work; +static cpumask_t up_cpumask; +static spinlock_t up_cpumask_lock; +static cpumask_t down_cpumask; +static spinlock_t down_cpumask_lock; +static struct mutex set_speed_lock; + +/* Hi speed to bump to from lo speed when load burst (default max) */ +static u64 hispeed_freq; + +/* Go to hi speed when CPU load at or above this value. */ +#define DEFAULT_GO_HISPEED_LOAD 95 +static unsigned long go_hispeed_load; + +/* + * The minimum amount of time to spend at a frequency before we can ramp down. + */ +#define DEFAULT_MIN_SAMPLE_TIME 20 * USEC_PER_MSEC +static unsigned long min_sample_time; + +/* + * The sample rate of the timer used to increase frequency + */ +#define DEFAULT_TIMER_RATE 20 * USEC_PER_MSEC +static unsigned long timer_rate; + +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +static +#endif +struct cpufreq_governor cpufreq_gov_interactive = { + .name = "interactive", + .governor = cpufreq_governor_interactive, + .max_transition_latency = 10000000, + .owner = THIS_MODULE, +}; + +static void cpufreq_interactive_timer(unsigned long data) +{ + unsigned int delta_idle; + unsigned int delta_time; + int cpu_load; + int load_since_change; + u64 time_in_idle; + u64 idle_exit_time; + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, data); + u64 now_idle; + unsigned int new_freq; + unsigned int index; + unsigned long flags; + + smp_rmb(); + + if (!pcpu->governor_enabled) + goto exit; + + /* + * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time, + * this lets idle exit know the current idle time sample has + * been processed, and idle exit can generate a new sample and + * re-arm the timer. This prevents a concurrent idle + * exit on that CPU from writing a new set of info at the same time + * the timer function runs (the timer function can't use that info + * until more time passes). + */ + time_in_idle = pcpu->time_in_idle; + idle_exit_time = pcpu->idle_exit_time; + now_idle = get_cpu_idle_time_us(data, &pcpu->timer_run_time); + smp_wmb(); + + /* If we raced with cancelling a timer, skip. */ + if (!idle_exit_time) + goto exit; + + delta_idle = (unsigned int)(now_idle - time_in_idle); + delta_time = (unsigned int)(pcpu->timer_run_time - idle_exit_time); + + /* + * If timer ran less than 1ms after short-term sample started, retry. + */ + if (delta_time < 1000) + goto rearm; + + if (delta_idle > delta_time) + cpu_load = 0; + else + cpu_load = 100 * (delta_time - delta_idle) / delta_time; + + delta_idle = (unsigned int)(now_idle - pcpu->freq_change_time_in_idle); + delta_time = (unsigned int)(pcpu->timer_run_time - pcpu->freq_change_time); + + if ((delta_time == 0) || (delta_idle > delta_time)) + load_since_change = 0; + else + load_since_change = + 100 * (delta_time - delta_idle) / delta_time; + + /* + * Choose greater of short-term load (since last idle timer + * started or timer function re-armed itself) or long-term load + * (since last frequency change). + */ + if (load_since_change > cpu_load) + cpu_load = load_since_change; + + if (cpu_load >= go_hispeed_load) { + if (pcpu->policy->cur == pcpu->policy->min) + new_freq = hispeed_freq; + else + new_freq = pcpu->policy->max * cpu_load / 100; + } else { + new_freq = pcpu->policy->cur * cpu_load / 100; + } + + if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, + new_freq, CPUFREQ_RELATION_H, + &index)) { + pr_warn_once("timer %d: cpufreq_frequency_table_target error\n", + (int) data); + goto rearm; + } + + new_freq = pcpu->freq_table[index].frequency; + + if (pcpu->target_freq == new_freq) + goto rearm_if_notmax; + + /* + * Do not scale down unless we have been at this frequency for the + * minimum sample time. + */ + if (new_freq < pcpu->target_freq) { + if (pcpu->timer_run_time - pcpu->freq_change_time + < min_sample_time) + goto rearm; + } + + if (new_freq < pcpu->target_freq) { + pcpu->target_freq = new_freq; + spin_lock_irqsave(&down_cpumask_lock, flags); + cpumask_set_cpu(data, &down_cpumask); + spin_unlock_irqrestore(&down_cpumask_lock, flags); + queue_work(down_wq, &freq_scale_down_work); + } else { + pcpu->target_freq = new_freq; + spin_lock_irqsave(&up_cpumask_lock, flags); + cpumask_set_cpu(data, &up_cpumask); + spin_unlock_irqrestore(&up_cpumask_lock, flags); + wake_up_process(up_task); + } + +rearm_if_notmax: + /* + * Already set max speed and don't see a need to change that, + * wait until next idle to re-evaluate, don't need timer. + */ + if (pcpu->target_freq == pcpu->policy->max) + goto exit; + +rearm: + if (!timer_pending(&pcpu->cpu_timer)) { + /* + * If already at min: if that CPU is idle, don't set timer. + * Else cancel the timer if that CPU goes idle. We don't + * need to re-evaluate speed until the next idle exit. + */ + if (pcpu->target_freq == pcpu->policy->min) { + smp_rmb(); + + if (pcpu->idling) + goto exit; + + pcpu->timer_idlecancel = 1; + } + + pcpu->time_in_idle = get_cpu_idle_time_us( + data, &pcpu->idle_exit_time); + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); + } + +exit: + return; +} + +static void cpufreq_interactive_idle_start(void) +{ + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + int pending; + + if (!pcpu->governor_enabled) + return; + + pcpu->idling = 1; + smp_wmb(); + pending = timer_pending(&pcpu->cpu_timer); + + if (pcpu->target_freq != pcpu->policy->min) { +#ifdef CONFIG_SMP + /* + * Entering idle while not at lowest speed. On some + * platforms this can hold the other CPU(s) at that speed + * even though the CPU is idle. Set a timer to re-evaluate + * speed so this idle CPU doesn't hold the other CPUs above + * min indefinitely. This should probably be a quirk of + * the CPUFreq driver. + */ + if (!pending) { + pcpu->time_in_idle = get_cpu_idle_time_us( + smp_processor_id(), &pcpu->idle_exit_time); + pcpu->timer_idlecancel = 0; + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); + } +#endif + } else { + /* + * If at min speed and entering idle after load has + * already been evaluated, and a timer has been set just in + * case the CPU suddenly goes busy, cancel that timer. The + * CPU didn't go busy; we'll recheck things upon idle exit. + */ + if (pending && pcpu->timer_idlecancel) { + del_timer(&pcpu->cpu_timer); + /* + * Ensure last timer run time is after current idle + * sample start time, so next idle exit will always + * start a new idle sampling period. + */ + pcpu->idle_exit_time = 0; + pcpu->timer_idlecancel = 0; + } + } + +} + +static void cpufreq_interactive_idle_end(void) +{ + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + + pcpu->idling = 0; + smp_wmb(); + + /* + * Arm the timer for 1-2 ticks later if not already, and if the timer + * function has already processed the previous load sampling + * interval. (If the timer is not pending but has not processed + * the previous interval, it is probably racing with us on another + * CPU. Let it compute load based on the previous sample and then + * re-arm the timer for another interval when it's done, rather + * than updating the interval start time to be "now", which doesn't + * give the timer function enough time to make a decision on this + * run.) + */ + if (timer_pending(&pcpu->cpu_timer) == 0 && + pcpu->timer_run_time >= pcpu->idle_exit_time && + pcpu->governor_enabled) { + pcpu->time_in_idle = + get_cpu_idle_time_us(smp_processor_id(), + &pcpu->idle_exit_time); + pcpu->timer_idlecancel = 0; + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); + } + +} + +static int cpufreq_interactive_up_task(void *data) +{ + unsigned int cpu; + cpumask_t tmp_mask; + unsigned long flags; + struct cpufreq_interactive_cpuinfo *pcpu; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&up_cpumask_lock, flags); + + if (cpumask_empty(&up_cpumask)) { + spin_unlock_irqrestore(&up_cpumask_lock, flags); + schedule(); + + if (kthread_should_stop()) + break; + + spin_lock_irqsave(&up_cpumask_lock, flags); + } + + set_current_state(TASK_RUNNING); + tmp_mask = up_cpumask; + cpumask_clear(&up_cpumask); + spin_unlock_irqrestore(&up_cpumask_lock, flags); + + for_each_cpu(cpu, &tmp_mask) { + unsigned int j; + unsigned int max_freq = 0; + + pcpu = &per_cpu(cpuinfo, cpu); + smp_rmb(); + + if (!pcpu->governor_enabled) + continue; + + mutex_lock(&set_speed_lock); + + for_each_cpu(j, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, j); + + if (pjcpu->target_freq > max_freq) + max_freq = pjcpu->target_freq; + } + + if (max_freq != pcpu->policy->cur) + __cpufreq_driver_target(pcpu->policy, + max_freq, + CPUFREQ_RELATION_H); + mutex_unlock(&set_speed_lock); + + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu, + &pcpu->freq_change_time); + } + } + + return 0; +} + +static void cpufreq_interactive_freq_down(struct work_struct *work) +{ + unsigned int cpu; + cpumask_t tmp_mask; + unsigned long flags; + struct cpufreq_interactive_cpuinfo *pcpu; + + spin_lock_irqsave(&down_cpumask_lock, flags); + tmp_mask = down_cpumask; + cpumask_clear(&down_cpumask); + spin_unlock_irqrestore(&down_cpumask_lock, flags); + + for_each_cpu(cpu, &tmp_mask) { + unsigned int j; + unsigned int max_freq = 0; + + pcpu = &per_cpu(cpuinfo, cpu); + smp_rmb(); + + if (!pcpu->governor_enabled) + continue; + + mutex_lock(&set_speed_lock); + + for_each_cpu(j, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, j); + + if (pjcpu->target_freq > max_freq) + max_freq = pjcpu->target_freq; + } + + if (max_freq != pcpu->policy->cur) + __cpufreq_driver_target(pcpu->policy, max_freq, + CPUFREQ_RELATION_H); + + mutex_unlock(&set_speed_lock); + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu, + &pcpu->freq_change_time); + } +} + +static ssize_t show_hispeed_freq(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%llu\n", hispeed_freq); +} + +static ssize_t store_hispeed_freq(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + u64 val; + + ret = strict_strtoull(buf, 0, &val); + if (ret < 0) + return ret; + hispeed_freq = val; + return count; +} + +static struct global_attr hispeed_freq_attr = __ATTR(hispeed_freq, 0644, + show_hispeed_freq, store_hispeed_freq); + + +static ssize_t show_go_hispeed_load(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", go_hispeed_load); +} + +static ssize_t store_go_hispeed_load(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + go_hispeed_load = val; + return count; +} + +static struct global_attr go_hispeed_load_attr = __ATTR(go_hispeed_load, 0644, + show_go_hispeed_load, store_go_hispeed_load); + +static ssize_t show_min_sample_time(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", min_sample_time); +} + +static ssize_t store_min_sample_time(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + min_sample_time = val; + return count; +} + +static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, + show_min_sample_time, store_min_sample_time); + +static ssize_t show_timer_rate(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", timer_rate); +} + +static ssize_t store_timer_rate(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + timer_rate = val; + return count; +} + +static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, + show_timer_rate, store_timer_rate); + +static struct attribute *interactive_attributes[] = { + &hispeed_freq_attr.attr, + &go_hispeed_load_attr.attr, + &min_sample_time_attr.attr, + &timer_rate_attr.attr, + NULL, +}; + +static struct attribute_group interactive_attr_group = { + .attrs = interactive_attributes, + .name = "interactive", +}; + +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, + unsigned int event) +{ + int rc; + unsigned int j; + struct cpufreq_interactive_cpuinfo *pcpu; + struct cpufreq_frequency_table *freq_table; + + switch (event) { + case CPUFREQ_GOV_START: + if (!cpu_online(policy->cpu)) + return -EINVAL; + + freq_table = + cpufreq_frequency_get_table(policy->cpu); + + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + pcpu->policy = policy; + pcpu->target_freq = policy->cur; + pcpu->freq_table = freq_table; + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(j, + &pcpu->freq_change_time); + pcpu->governor_enabled = 1; + smp_wmb(); + } + + if (!hispeed_freq) + hispeed_freq = policy->max; + + /* + * Do not register the idle hook and create sysfs + * entries if we have already done so. + */ + if (atomic_inc_return(&active_count) > 1) + return 0; + + rc = sysfs_create_group(cpufreq_global_kobject, + &interactive_attr_group); + if (rc) + return rc; + + break; + + case CPUFREQ_GOV_STOP: + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + pcpu->governor_enabled = 0; + smp_wmb(); + del_timer_sync(&pcpu->cpu_timer); + + /* + * Reset idle exit time since we may cancel the timer + * before it can run after the last idle exit time, + * to avoid tripping the check in idle exit for a timer + * that is trying to run. + */ + pcpu->idle_exit_time = 0; + } + + flush_work(&freq_scale_down_work); + if (atomic_dec_return(&active_count) > 0) + return 0; + + sysfs_remove_group(cpufreq_global_kobject, + &interactive_attr_group); + + break; + + case CPUFREQ_GOV_LIMITS: + if (policy->max < policy->cur) + __cpufreq_driver_target(policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > policy->cur) + __cpufreq_driver_target(policy, + policy->min, CPUFREQ_RELATION_L); + break; + } + return 0; +} + +static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, + unsigned long val, + void *data) +{ + switch (val) { + case IDLE_START: + cpufreq_interactive_idle_start(); + break; + case IDLE_END: + cpufreq_interactive_idle_end(); + break; + } + + return 0; +} + +static struct notifier_block cpufreq_interactive_idle_nb = { + .notifier_call = cpufreq_interactive_idle_notifier, +}; + +static int __init cpufreq_interactive_init(void) +{ + unsigned int i; + struct cpufreq_interactive_cpuinfo *pcpu; + struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; + + go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; + min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + timer_rate = DEFAULT_TIMER_RATE; + + /* Initalize per-cpu timers */ + for_each_possible_cpu(i) { + pcpu = &per_cpu(cpuinfo, i); + init_timer(&pcpu->cpu_timer); + pcpu->cpu_timer.function = cpufreq_interactive_timer; + pcpu->cpu_timer.data = i; + } + + up_task = kthread_create(cpufreq_interactive_up_task, NULL, + "kinteractiveup"); + if (IS_ERR(up_task)) + return PTR_ERR(up_task); + + sched_setscheduler_nocheck(up_task, SCHED_FIFO, ¶m); + get_task_struct(up_task); + + /* No rescuer thread, bind to CPU queuing the work for possibly + warm cache (probably doesn't matter much). */ + down_wq = alloc_workqueue("knteractive_down", 0, 1); + + if (!down_wq) + goto err_freeuptask; + + INIT_WORK(&freq_scale_down_work, + cpufreq_interactive_freq_down); + + spin_lock_init(&up_cpumask_lock); + spin_lock_init(&down_cpumask_lock); + mutex_init(&set_speed_lock); + + idle_notifier_register(&cpufreq_interactive_idle_nb); + + return cpufreq_register_governor(&cpufreq_gov_interactive); + +err_freeuptask: + put_task_struct(up_task); + return -ENOMEM; +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE +fs_initcall(cpufreq_interactive_init); +#else +module_init(cpufreq_interactive_init); +#endif + +static void __exit cpufreq_interactive_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_interactive); + kthread_stop(up_task); + put_task_struct(up_task); + destroy_workqueue(down_wq); +} + +module_exit(cpufreq_interactive_exit); + +MODULE_AUTHOR("Mike Chan "); +MODULE_DESCRIPTION("'cpufreq_interactive' - A cpufreq governor for " + "Latency sensitive workloads"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 4d89e0e6f9cc..b0d360c87602 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -432,6 +432,9 @@ extern struct cpufreq_governor cpufreq_gov_ondemand; #elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE) extern struct cpufreq_governor cpufreq_gov_conservative; #define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_conservative) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE) +extern struct cpufreq_governor cpufreq_gov_interactive; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_interactive) #endif /********************************************************************* From e789079a1ce421e430a1b44a7d5f06f3e6e07c43 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 15 Apr 2011 15:22:09 -0700 Subject: [PATCH 075/358] net: Reorder incoming packets in PPPoLAC and PPPoPNS. PPP handles packet loss but does not work with out of order packets. This change performs reordering of incoming data packets within a sliding window of one second. Since sequence number is optional, receiving a packet without it will drop all queued packets. Currently the logic is triggered by incoming packets, so queued packets have to wait till another packet is arrived. It is done for simplicity since no additional locks or threads are required. For reliable protocols, a retransmission will kick it. For unreliable protocols, queued packets just seem like packet loss. Time-critical protocols might be broken, but they never work with queueing anyway. Signed-off-by: Chia-chi Yeh --- drivers/net/ppp/pppolac.c | 95 +++++++++++++++++++++++++++++++++------ drivers/net/ppp/pppopns.c | 87 +++++++++++++++++++++++++++++++---- include/linux/if_pppox.h | 22 ++++----- 3 files changed, 173 insertions(+), 31 deletions(-) diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c index af3202a920a0..c94b8507d92b 100644 --- a/drivers/net/ppp/pppolac.c +++ b/drivers/net/ppp/pppolac.c @@ -15,12 +15,15 @@ */ /* This driver handles L2TP data packets between a UDP socket and a PPP channel. - * To keep things simple, only one session per socket is permitted. Packets are - * sent via the socket, so it must keep connected to the same address. One must - * not set sequencing in ICCN but let LNS controll it. Currently this driver - * only works on IPv4 due to the lack of UDP encapsulation support in IPv6. */ + * The socket must keep connected, and only one session per socket is permitted. + * Sequencing of outgoing packets is controlled by LNS. Incoming packets with + * sequences are reordered within a sliding window of one second. Currently + * reordering only happens when a packet is received. It is done for simplicity + * since no additional locks or threads are required. This driver only works on + * IPv4 due to the lack of UDP encapsulation support in IPv6. */ #include +#include #include #include #include @@ -53,14 +56,28 @@ static inline union unaligned *unaligned(void *ptr) return (union unaligned *)ptr; } +struct meta { + __u32 sequence; + __u32 timestamp; +}; + +static inline struct meta *skb_meta(struct sk_buff *skb) +{ + return (struct meta *)skb->cb; +} + +/******************************************************************************/ + static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) { struct sock *sk = (struct sock *)sk_udp->sk_user_data; struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac; + struct meta *meta = skb_meta(skb); + __u32 now = jiffies; __u8 bits; __u8 *ptr; - /* Drop the packet if it is too short. */ + /* Drop the packet if L2TP header is missing. */ if (skb->len < sizeof(struct udphdr) + 6) goto drop; @@ -99,9 +116,12 @@ static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) if (unaligned(ptr)->u32 != opt->local) goto drop; - /* Check the sequence if it is present. According to RFC 2661 section - * 5.4, the only thing to do is to update opt->sequencing. */ - opt->sequencing = bits & L2TP_SEQUENCE_BIT; + /* Check the sequence if it is present. */ + if (bits & L2TP_SEQUENCE_BIT) { + meta->sequence = ptr[4] << 8 | ptr[5]; + if ((__s16)(meta->sequence - opt->recv_sequence) < 0) + goto drop; + } /* Skip PPP address and control if they are present. */ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && @@ -112,7 +132,54 @@ static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) if (skb->len >= 1 && skb->data[0] & 1) skb_push(skb, 1)[0] = 0; - /* Finally, deliver the packet to PPP channel. */ + /* Drop the packet if PPP protocol is missing. */ + if (skb->len < 2) + goto drop; + + /* Perform reordering if sequencing is enabled. */ + atomic_set(&opt->sequencing, bits & L2TP_SEQUENCE_BIT); + if (bits & L2TP_SEQUENCE_BIT) { + struct sk_buff *skb1; + + /* Insert the packet into receive queue in order. */ + skb_set_owner_r(skb, sk); + skb_queue_walk(&sk->sk_receive_queue, skb1) { + struct meta *meta1 = skb_meta(skb1); + __s16 order = meta->sequence - meta1->sequence; + if (order == 0) + goto drop; + if (order < 0) { + meta->timestamp = meta1->timestamp; + skb_insert(skb1, skb, &sk->sk_receive_queue); + skb = NULL; + break; + } + } + if (skb) { + meta->timestamp = now; + skb_queue_tail(&sk->sk_receive_queue, skb); + } + + /* Remove packets from receive queue as long as + * 1. the receive buffer is full, + * 2. they are queued longer than one second, or + * 3. there are no missing packets before them. */ + skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { + meta = skb_meta(skb); + if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && + now - meta->timestamp < HZ && + meta->sequence != opt->recv_sequence) + break; + skb_unlink(skb, &sk->sk_receive_queue); + opt->recv_sequence = (__u16)(meta->sequence + 1); + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + } + return NET_RX_SUCCESS; + } + + /* Flush receive queue if sequencing is disabled. */ + skb_queue_purge(&sk->sk_receive_queue); skb_orphan(skb); ppp_input(&pppox_sk(sk)->chan, skb); return NET_RX_SUCCESS; @@ -163,14 +230,14 @@ static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) skb->data[1] = PPP_CTRL; /* Install L2TP header. */ - if (opt->sequencing) { + if (atomic_read(&opt->sequencing)) { skb_push(skb, 10); skb->data[0] = L2TP_SEQUENCE_BIT; - skb->data[6] = opt->sequence >> 8; - skb->data[7] = opt->sequence; + skb->data[6] = opt->xmit_sequence >> 8; + skb->data[7] = opt->xmit_sequence; skb->data[8] = 0; skb->data[9] = 0; - opt->sequence++; + opt->xmit_sequence++; } else { skb_push(skb, 6); skb->data[0] = 0; @@ -246,6 +313,7 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, po->chan.mtu = PPP_MTU - 80; po->proto.lac.local = unaligned(&addr->local)->u32; po->proto.lac.remote = unaligned(&addr->remote)->u32; + atomic_set(&po->proto.lac.sequencing, 1); po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv; error = ppp_register_channel(&po->chan); @@ -283,6 +351,7 @@ static int pppolac_release(struct socket *sock) if (sk->sk_state != PPPOX_NONE) { struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private; lock_sock(sk_udp); + skb_queue_purge(&sk->sk_receive_queue); pppox_unbind_sock(sk); udp_sk(sk_udp)->encap_type = 0; udp_sk(sk_udp)->encap_rcv = NULL; diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c index 298097127c90..fb8198447938 100644 --- a/drivers/net/ppp/pppopns.c +++ b/drivers/net/ppp/pppopns.c @@ -16,11 +16,14 @@ /* This driver handles PPTP data packets between a RAW socket and a PPP channel. * The socket is created in the kernel space and connected to the same address - * of the control socket. To keep things simple, packets are always sent with - * sequence but without acknowledgement. This driver should work on both IPv4 - * and IPv6. */ + * of the control socket. Outgoing packets are always sent with sequences but + * without acknowledgements. Incoming packets with sequences are reordered + * within a sliding window of one second. Currently reordering only happens when + * a packet is received. It is done for simplicity since no additional locks or + * threads are required. This driver should work on both IPv4 and IPv6. */ #include +#include #include #include #include @@ -52,21 +55,35 @@ struct header { __u32 sequence; } __attribute__((packed)); +struct meta { + __u32 sequence; + __u32 timestamp; +}; + +static inline struct meta *skb_meta(struct sk_buff *skb) +{ + return (struct meta *)skb->cb; +} + +/******************************************************************************/ + static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) { struct sock *sk = (struct sock *)sk_raw->sk_user_data; struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns; + struct meta *meta = skb_meta(skb); + __u32 now = jiffies; struct header *hdr; /* Skip transport header */ skb_pull(skb, skb_transport_header(skb) - skb->data); - /* Drop the packet if it is too short. */ + /* Drop the packet if GRE header is missing. */ if (skb->len < GRE_HEADER_SIZE) goto drop; + hdr = (struct header *)skb->data; /* Check the header. */ - hdr = (struct header *)skb->data; if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS) goto drop; @@ -81,6 +98,13 @@ static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) if (skb->len != ntohs(hdr->length)) goto drop; + /* Check the sequence if it is present. */ + if (hdr->bits & PPTP_GRE_SEQ_BIT) { + meta->sequence = ntohl(hdr->sequence); + if ((__s32)(meta->sequence - opt->recv_sequence) < 0) + goto drop; + } + /* Skip PPP address and control if they are present. */ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && skb->data[1] == PPP_CTRL) @@ -90,7 +114,53 @@ static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) if (skb->len >= 1 && skb->data[0] & 1) skb_push(skb, 1)[0] = 0; - /* Finally, deliver the packet to PPP channel. */ + /* Drop the packet if PPP protocol is missing. */ + if (skb->len < 2) + goto drop; + + /* Perform reordering if sequencing is enabled. */ + if (hdr->bits & PPTP_GRE_SEQ_BIT) { + struct sk_buff *skb1; + + /* Insert the packet into receive queue in order. */ + skb_set_owner_r(skb, sk); + skb_queue_walk(&sk->sk_receive_queue, skb1) { + struct meta *meta1 = skb_meta(skb1); + __s32 order = meta->sequence - meta1->sequence; + if (order == 0) + goto drop; + if (order < 0) { + meta->timestamp = meta1->timestamp; + skb_insert(skb1, skb, &sk->sk_receive_queue); + skb = NULL; + break; + } + } + if (skb) { + meta->timestamp = now; + skb_queue_tail(&sk->sk_receive_queue, skb); + } + + /* Remove packets from receive queue as long as + * 1. the receive buffer is full, + * 2. they are queued longer than one second, or + * 3. there are no missing packets before them. */ + skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { + meta = skb_meta(skb); + if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && + now - meta->timestamp < HZ && + meta->sequence != opt->recv_sequence) + break; + skb_unlink(skb, &sk->sk_receive_queue); + opt->recv_sequence = meta->sequence + 1; + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + } + return NET_RX_SUCCESS; + } + + /* Flush receive queue if sequencing is disabled. */ + skb_queue_purge(&sk->sk_receive_queue); skb_orphan(skb); ppp_input(&pppox_sk(sk)->chan, skb); return NET_RX_SUCCESS; @@ -151,8 +221,8 @@ static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) hdr->type = PPTP_GRE_TYPE; hdr->length = htons(length); hdr->call = opt->remote; - hdr->sequence = htonl(opt->sequence); - opt->sequence++; + hdr->sequence = htonl(opt->xmit_sequence); + opt->xmit_sequence++; /* Now send the packet via the delivery queue. */ skb_set_owner_w(skb, sk_raw); @@ -261,6 +331,7 @@ static int pppopns_release(struct socket *sock) if (sk->sk_state != PPPOX_NONE) { struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; lock_sock(sk_raw); + skb_queue_purge(&sk->sk_receive_queue); pppox_unbind_sock(sk); sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready; sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv; diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index bc4f57df265b..dccd621d6377 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -43,19 +43,21 @@ struct pptp_opt { }; struct pppolac_opt { - __u32 local; - __u32 remote; - __u16 sequence; - __u8 sequencing; - int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb); + __u32 local; + __u32 remote; + __u32 recv_sequence; + __u32 xmit_sequence; + atomic_t sequencing; + int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb); }; struct pppopns_opt { - __u16 local; - __u16 remote; - __u32 sequence; - void (*data_ready)(struct sock *sk_raw, int length); - int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb); + __u16 local; + __u16 remote; + __u32 recv_sequence; + __u32 xmit_sequence; + void (*data_ready)(struct sock *sk_raw, int length); + int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb); }; #include From 59254114298ae63bb22581eb63d56f7a6646e9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 28 Jan 2011 23:12:32 -0800 Subject: [PATCH 076/358] ARM: etm: Don't require clock control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If clk_get fail, assume the etb does not need a separate clock. Change-Id: Ia0bf3f5391e94a60ea45876aa7afc8a88a7ec3bf Signed-off-by: Arve Hjønnevåg --- arch/arm/kernel/etm.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 131a6ab5f355..52d9ce4a1e07 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -362,13 +362,12 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id) if (ret) goto out_unmap; + /* Get optional clock. Currently used to select clock source on omap3 */ t->emu_clk = clk_get(&dev->dev, "emu_src_ck"); - if (IS_ERR(t->emu_clk)) { + if (IS_ERR(t->emu_clk)) dev_dbg(&dev->dev, "Failed to obtain emu_src_ck.\n"); - return -EFAULT; - } - - clk_enable(t->emu_clk); + else + clk_enable(t->emu_clk); etb_unlock(t); t->etb_bufsz = etb_readl(t, ETBR_DEPTH); @@ -400,8 +399,10 @@ static int etb_remove(struct amba_device *dev) iounmap(t->etb_regs); t->etb_regs = NULL; - clk_disable(t->emu_clk); - clk_put(t->emu_clk); + if (!IS_ERR(t->emu_clk)) { + clk_disable(t->emu_clk); + clk_put(t->emu_clk); + } amba_release_regions(dev); From a6ab901ca2ea3043039c6d47030475d94d39e3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 28 Jan 2011 23:33:11 -0800 Subject: [PATCH 077/358] ARM: etm: Don't limit tracing to only non-secure code. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some systems kernel code is considered secure, and this code already limits tracing to the kernel text segment which results in no trace data. Change-Id: I098a0753e874859446d098e1ee209f67fc13cd5d Signed-off-by: Arve Hjønnevåg --- arch/arm/kernel/etm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 52d9ce4a1e07..bb044ca981f7 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -56,7 +56,7 @@ static inline bool trace_isrunning(struct tracectx *t) static int etm_setup_address_range(struct tracectx *t, int n, unsigned long start, unsigned long end, int exclude, int data) { - u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_NSONLY | \ + u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY | ETMAAT_NOVALCMP; if (n < 1 || n > t->ncmppairs) From 18f1af0ee93603751e94f04e00e2917efc9b13ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 31 Jan 2011 21:34:47 -0800 Subject: [PATCH 078/358] ARM: etm: Don't try to clear the buffer full status after reading the buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the write address was at the end of the buffer, toggling the trace capture bit would set the RAM-full status instead of clearing it, and if any of the stop bits in the formatter is set toggling the trace capture bit may not do anything. Instead use the read position to find out if the data has already been returned. This also fixes the read function so it works when the trace buffer is larger than the buffer passed in from user space. The old version would reset the trace buffer pointers after every read, so the second call to read would always return 0. Change-Id: I75256abe2556adfd66fd5963e46f9e84ae4645e1 Signed-off-by: Arve Hjønnevåg --- arch/arm/kernel/etm.c | 58 +++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index bb044ca981f7..867eb2fa58f8 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -92,6 +92,7 @@ static int trace_start(struct tracectx *t) etb_unlock(t); + etb_writel(t, 0, ETBR_WRITEADDR); etb_writel(t, 0, ETBR_FORMATTERCTRL); etb_writel(t, 1, ETBR_CTRL); @@ -185,24 +186,15 @@ static int trace_stop(struct tracectx *t) static int etb_getdatalen(struct tracectx *t) { u32 v; - int rp, wp; + int wp; v = etb_readl(t, ETBR_STATUS); if (v & 1) return t->etb_bufsz; - rp = etb_readl(t, ETBR_READADDR); wp = etb_readl(t, ETBR_WRITEADDR); - - if (rp > wp) { - etb_writel(t, 0, ETBR_READADDR); - etb_writel(t, 0, ETBR_WRITEADDR); - - return 0; - } - - return wp - rp; + return wp; } /* sysrq+v will always stop the running trace and leave it at that */ @@ -235,14 +227,6 @@ static void etm_dump(void) printk("%08x", cpu_to_be32(etb_readl(t, ETBR_READMEM))); printk(KERN_INFO "\n--- ETB buffer end ---\n"); - /* deassert the overflow bit */ - etb_writel(t, 1, ETBR_CTRL); - etb_writel(t, 0, ETBR_CTRL); - - etb_writel(t, 0, ETBR_TRIGGERCOUNT); - etb_writel(t, 0, ETBR_READADDR); - etb_writel(t, 0, ETBR_WRITEADDR); - etb_lock(t); } @@ -276,6 +260,10 @@ static ssize_t etb_read(struct file *file, char __user *data, struct tracectx *t = file->private_data; u32 first = 0; u32 *buf; + int wpos; + int skip; + long wlength; + loff_t pos = *ppos; mutex_lock(&t->mutex); @@ -290,28 +278,34 @@ static ssize_t etb_read(struct file *file, char __user *data, if (total == t->etb_bufsz) first = etb_readl(t, ETBR_WRITEADDR); + if (pos > total * 4) { + skip = 0; + wpos = total; + } else { + skip = (int)pos % 4; + wpos = (int)pos / 4; + } + total -= wpos; + first = (first + wpos) % t->etb_bufsz; + etb_writel(t, first, ETBR_READADDR); - length = min(total * 4, (int)len); - buf = vmalloc(length); + wlength = min(total, DIV_ROUND_UP(skip + (int)len, 4)); + length = min(total * 4 - skip, (int)len); + buf = vmalloc(wlength * 4); - dev_dbg(t->dev, "ETB buffer length: %d\n", total); + dev_dbg(t->dev, "ETB read %ld bytes to %lld from %ld words at %d\n", + length, pos, wlength, first); + dev_dbg(t->dev, "ETB buffer length: %d\n", total + wpos); dev_dbg(t->dev, "ETB status reg: %x\n", etb_readl(t, ETBR_STATUS)); - for (i = 0; i < length / 4; i++) + for (i = 0; i < wlength; i++) buf[i] = etb_readl(t, ETBR_READMEM); - /* the only way to deassert overflow bit in ETB status is this */ - etb_writel(t, 1, ETBR_CTRL); - etb_writel(t, 0, ETBR_CTRL); - - etb_writel(t, 0, ETBR_WRITEADDR); - etb_writel(t, 0, ETBR_READADDR); - etb_writel(t, 0, ETBR_TRIGGERCOUNT); - etb_lock(t); - length -= copy_to_user(data, buf, length); + length -= copy_to_user(data, (u8 *)buf + skip, length); vfree(buf); + *ppos = pos + length; out: mutex_unlock(&t->mutex); From 9d87d4816d30c514a7030aa70fa305c361b8294a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 28 Jan 2011 23:44:43 -0800 Subject: [PATCH 079/358] ARM: etm: Allow range selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trace kernel text segment by default as before, allow tracing of other ranges by writing a range to /sys/devices/etm/trace_range, or to trace everything by writing 0 0. Change-Id: Ibb734ca820fedf79560b20536247f1e1700cdc71 Signed-off-by: Arve Hjønnevåg --- arch/arm/kernel/etm.c | 49 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 867eb2fa58f8..416cc6bd9153 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -41,12 +41,17 @@ struct tracectx { unsigned long flags; int ncmppairs; int etm_portsz; + unsigned long range_start; + unsigned long range_end; struct device *dev; struct clk *emu_clk; struct mutex mutex; }; -static struct tracectx tracer; +static struct tracectx tracer = { + .range_start = (unsigned long)_stext, + .range_end = (unsigned long)_etext, +}; static inline bool trace_isrunning(struct tracectx *t) { @@ -116,8 +121,12 @@ static int trace_start(struct tracectx *t) return -EFAULT; } - etm_setup_address_range(t, 1, (unsigned long)_stext, - (unsigned long)_etext, 0, 0); + if (t->range_start || t->range_end) + etm_setup_address_range(t, 1, + t->range_start, t->range_end, 0, 0); + else + etm_writel(t, ETMTE_INCLEXCL, ETMR_TRACEENCTRL); + etm_writel(t, 0, ETMR_TRACEENCTRL2); etm_writel(t, 0, ETMR_TRACESSCTRL); etm_writel(t, 0x6f, ETMR_TRACEENEVT); @@ -523,6 +532,35 @@ static ssize_t trace_mode_store(struct kobject *kobj, static struct kobj_attribute trace_mode_attr = __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); +static ssize_t trace_range_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%08lx %08lx\n", + tracer.range_start, tracer.range_end); +} + +static ssize_t trace_range_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long range_start, range_end; + + if (sscanf(buf, "%lx %lx", &range_start, &range_end) != 2) + return -EINVAL; + + mutex_lock(&tracer.mutex); + tracer.range_start = range_start; + tracer.range_end = range_end; + mutex_unlock(&tracer.mutex); + + return n; +} + + +static struct kobj_attribute trace_range_attr = + __ATTR(trace_range, 0644, trace_range_show, trace_range_store); + static int etm_probe(struct amba_device *dev, const struct amba_id *id) { struct tracectx *t = &tracer; @@ -574,6 +612,10 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) if (ret) dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); + ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr); + if (ret) + dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n"); + dev_dbg(t->dev, "ETM AMBA driver initialized.\n"); out: @@ -600,6 +642,7 @@ static int etm_remove(struct amba_device *dev) sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); + sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr); return 0; } From 4edf87412ce89f3cade9ad1ad804365e6fbb2fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 31 Jan 2011 18:33:55 -0800 Subject: [PATCH 080/358] ARM: etm: Configure data tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old code enabled data tracing, but did not configure the range. We now configure it to trace all data addresses by default, and add a trace_data_range attribute to change the range or disable data tracing. Change-Id: I9d04e3e1ea0d0b4d4d5bcb93b1b042938ad738b2 Signed-off-by: Arve Hjønnevåg --- arch/arm/include/asm/hardware/coresight.h | 10 ++- arch/arm/kernel/etm.c | 77 ++++++++++++++++++++++- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h index ad774f37c47c..782abba508ad 100644 --- a/arch/arm/include/asm/hardware/coresight.h +++ b/arch/arm/include/asm/hardware/coresight.h @@ -17,9 +17,11 @@ #define TRACER_ACCESSED_BIT 0 #define TRACER_RUNNING_BIT 1 #define TRACER_CYCLE_ACC_BIT 2 +#define TRACER_TRACE_DATA_BIT 3 #define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT) #define TRACER_RUNNING BIT(TRACER_RUNNING_BIT) #define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT) +#define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT) #define TRACER_TIMEOUT 10000 @@ -113,8 +115,14 @@ #define ETMR_TRACEENCTRL 0x24 #define ETMTE_INCLEXCL BIT(24) #define ETMR_TRACEENEVT 0x20 + +#define ETMR_VIEWDATAEVT 0x30 +#define ETMR_VIEWDATACTRL1 0x34 +#define ETMR_VIEWDATACTRL2 0x38 +#define ETMR_VIEWDATACTRL3 0x3c +#define ETMVDC3_EXCLONLY BIT(16) + #define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \ - ETMCTRL_DATA_DO_ADDR | \ ETMCTRL_BRANCH_OUTPUT | \ ETMCTRL_DO_CONTEXTID) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 416cc6bd9153..3b67d6308e2f 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -43,6 +43,8 @@ struct tracectx { int etm_portsz; unsigned long range_start; unsigned long range_end; + unsigned long data_range_start; + unsigned long data_range_end; struct device *dev; struct clk *emu_clk; struct mutex mutex; @@ -84,8 +86,15 @@ static int etm_setup_address_range(struct tracectx *t, int n, etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1)); - flags = exclude ? ETMTE_INCLEXCL : 0; - etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL); + if (data) { + flags = exclude ? ETMVDC3_EXCLONLY : 0; + if (exclude) + n += 8; + etm_writel(t, flags | BIT(n), ETMR_VIEWDATACTRL3); + } else { + flags = exclude ? ETMTE_INCLEXCL : 0; + etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL); + } return 0; } @@ -109,6 +118,9 @@ static int trace_start(struct tracectx *t) if (t->flags & TRACER_CYCLE_ACC) v |= ETMCTRL_CYCLEACCURATE; + if (t->flags & TRACER_TRACE_DATA) + v |= ETMCTRL_DATA_DO_ADDR; + etm_unlock(t); etm_writel(t, v, ETMR_CTRL); @@ -131,6 +143,17 @@ static int trace_start(struct tracectx *t) etm_writel(t, 0, ETMR_TRACESSCTRL); etm_writel(t, 0x6f, ETMR_TRACEENEVT); + etm_writel(t, 0, ETMR_VIEWDATACTRL1); + etm_writel(t, 0, ETMR_VIEWDATACTRL2); + + if (t->data_range_start || t->data_range_end) + etm_setup_address_range(t, 2, t->data_range_start, + t->data_range_end, 0, 1); + else + etm_writel(t, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3); + + etm_writel(t, 0x6f, ETMR_VIEWDATAEVT); + v &= ~ETMCTRL_PROGRAM; v |= ETMCTRL_PORTSEL; @@ -561,6 +584,48 @@ static ssize_t trace_range_store(struct kobject *kobj, static struct kobj_attribute trace_range_attr = __ATTR(trace_range, 0644, trace_range_show, trace_range_store); +static ssize_t trace_data_range_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + unsigned long range_start; + u64 range_end; + mutex_lock(&tracer.mutex); + range_start = tracer.data_range_start; + range_end = tracer.data_range_end; + if (!range_end && (tracer.flags & TRACER_TRACE_DATA)) + range_end = 0x100000000ULL; + mutex_unlock(&tracer.mutex); + return sprintf(buf, "%08lx %08llx\n", range_start, range_end); +} + +static ssize_t trace_data_range_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long range_start; + u64 range_end; + + if (sscanf(buf, "%lx %llx", &range_start, &range_end) != 2) + return -EINVAL; + + mutex_lock(&tracer.mutex); + tracer.data_range_start = range_start; + tracer.data_range_end = (unsigned long)range_end; + if (range_end) + tracer.flags |= TRACER_TRACE_DATA; + else + tracer.flags &= ~TRACER_TRACE_DATA; + mutex_unlock(&tracer.mutex); + + return n; +} + + +static struct kobj_attribute trace_data_range_attr = + __ATTR(trace_data_range, 0644, + trace_data_range_show, trace_data_range_store); + static int etm_probe(struct amba_device *dev, const struct amba_id *id) { struct tracectx *t = &tracer; @@ -586,7 +651,7 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) mutex_init(&t->mutex); t->dev = &dev->dev; - t->flags = TRACER_CYCLE_ACC; + t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA; t->etm_portsz = 1; etm_unlock(t); @@ -616,6 +681,11 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) if (ret) dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n"); + ret = sysfs_create_file(&dev->dev.kobj, &trace_data_range_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_data_range in sysfs\n"); + dev_dbg(t->dev, "ETM AMBA driver initialized.\n"); out: @@ -643,6 +713,7 @@ static int etm_remove(struct amba_device *dev) sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); sysfs_remove_file(&dev->dev.kobj, &trace_mode_attr.attr); sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr); + sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr); return 0; } From d03740e2c561846a63fb7dccb0877369fd636bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 14 Feb 2011 21:09:51 -0800 Subject: [PATCH 081/358] ARM: etm: Add some missing locks and error checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not safe to call etm_lock or etb_lock without holding the mutex since another thread may also have unlocked the registers. Also add some missing checks for valid etb_regs in the etm sysfs entries. Change-Id: I939f76a6ea7546a8fc0d4ddafa2fd2b6f38103bb Signed-off-by: Arve Hjønnevåg --- arch/arm/kernel/etm.c | 57 +++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 3b67d6308e2f..798a07152965 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -264,8 +264,13 @@ static void etm_dump(void) static void sysrq_etm_dump(int key) { + if (!mutex_trylock(&tracer.mutex)) { + printk(KERN_INFO "Tracing hardware busy\n"); + return; + } dev_dbg(tracer.dev, "Dumping ETB buffer\n"); etm_dump(); + mutex_unlock(&tracer.mutex); } static struct sysrq_key_op sysrq_etm_op = { @@ -374,6 +379,7 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id) if (ret) goto out; + mutex_lock(&t->mutex); t->etb_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); if (!t->etb_regs) { ret = -ENOMEM; @@ -382,6 +388,16 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id) amba_set_drvdata(dev, t); + etb_unlock(t); + t->etb_bufsz = etb_readl(t, ETBR_DEPTH); + dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); + + /* make sure trace capture is disabled */ + etb_writel(t, 0, ETBR_CTRL); + etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); + etb_lock(t); + mutex_unlock(&t->mutex); + etb_miscdev.parent = &dev->dev; ret = misc_register(&etb_miscdev); @@ -395,24 +411,18 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id) else clk_enable(t->emu_clk); - etb_unlock(t); - t->etb_bufsz = etb_readl(t, ETBR_DEPTH); - dev_dbg(&dev->dev, "Size: %x\n", t->etb_bufsz); - - /* make sure trace capture is disabled */ - etb_writel(t, 0, ETBR_CTRL); - etb_writel(t, 0x1000, ETBR_FORMATTERCTRL); - etb_lock(t); - dev_dbg(&dev->dev, "ETB AMBA driver initialized.\n"); out: return ret; out_unmap: + mutex_lock(&t->mutex); iounmap(t->etb_regs); + t->etb_regs = NULL; out_release: + mutex_unlock(&t->mutex); amba_release_regions(dev); return ret; @@ -472,7 +482,10 @@ static ssize_t trace_running_store(struct kobject *kobj, return -EINVAL; mutex_lock(&tracer.mutex); - ret = value ? trace_start(&tracer) : trace_stop(&tracer); + if (!tracer.etb_regs) + ret = -ENODEV; + else + ret = value ? trace_start(&tracer) : trace_stop(&tracer); mutex_unlock(&tracer.mutex); return ret ? : n; @@ -488,18 +501,25 @@ static ssize_t trace_info_show(struct kobject *kobj, u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; int datalen; - etb_unlock(&tracer); - datalen = etb_getdatalen(&tracer); - etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); - etb_ra = etb_readl(&tracer, ETBR_READADDR); - etb_st = etb_readl(&tracer, ETBR_STATUS); - etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); - etb_lock(&tracer); + mutex_lock(&tracer.mutex); + if (tracer.etb_regs) { + etb_unlock(&tracer); + datalen = etb_getdatalen(&tracer); + etb_wa = etb_readl(&tracer, ETBR_WRITEADDR); + etb_ra = etb_readl(&tracer, ETBR_READADDR); + etb_st = etb_readl(&tracer, ETBR_STATUS); + etb_fc = etb_readl(&tracer, ETBR_FORMATTERCTRL); + etb_lock(&tracer); + } else { + etb_wa = etb_ra = etb_st = etb_fc = ~0; + datalen = -1; + } etm_unlock(&tracer); etm_ctrl = etm_readl(&tracer, ETMR_CTRL); etm_st = etm_readl(&tracer, ETMR_STATUS); etm_lock(&tracer); + mutex_unlock(&tracer.mutex); return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" "ETBR_WRITEADDR:\t%08x\n" @@ -649,7 +669,6 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) amba_set_drvdata(dev, t); - mutex_init(&t->mutex); t->dev = &dev->dev; t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA; t->etm_portsz = 1; @@ -740,6 +759,8 @@ static int __init etm_init(void) { int retval; + mutex_init(&tracer.mutex); + retval = amba_driver_register(&etb_driver); if (retval) { printk(KERN_ERR "Failed to register etb\n"); From c191e3322006f811a55d772f5a08218c709c6dea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 4 Feb 2011 22:38:14 -0800 Subject: [PATCH 082/358] ARM: etm: Return the entire trace buffer if it is empty after reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some SOCs the read and write pointer are reset when the chip resets, but the trace buffer content is preserved. If the status bits indicates that the buffer is empty and we have never started tracing, assume the buffer is full instead. This can be useful if the system rebooted from a watchdog reset. Change-Id: Iaf21c2c329c6059004ee1d38e3dfff66d7d28029 Signed-off-by: Arve Hjønnevåg --- arch/arm/kernel/etm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 798a07152965..20aee26b9550 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -45,6 +45,7 @@ struct tracectx { unsigned long range_end; unsigned long data_range_start; unsigned long data_range_end; + bool dump_initial_etb; struct device *dev; struct clk *emu_clk; struct mutex mutex; @@ -106,6 +107,7 @@ static int trace_start(struct tracectx *t) etb_unlock(t); + t->dump_initial_etb = false; etb_writel(t, 0, ETBR_WRITEADDR); etb_writel(t, 0, ETBR_FORMATTERCTRL); etb_writel(t, 1, ETBR_CTRL); @@ -312,6 +314,8 @@ static ssize_t etb_read(struct file *file, char __user *data, etb_unlock(t); total = etb_getdatalen(t); + if (total == 0 && t->dump_initial_etb) + total = t->etb_bufsz; if (total == t->etb_bufsz) first = etb_readl(t, ETBR_WRITEADDR); @@ -386,6 +390,7 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id) goto out_release; } + t->dump_initial_etb = true; amba_set_drvdata(dev, t); etb_unlock(t); From 1c2178bb256e43db5f9759e94e37bd8a22a5cc4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 4 Feb 2011 22:38:14 -0800 Subject: [PATCH 083/358] ARM: etm: Support multiple ETMs/PTMs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If more than one ETM or PTM are present, configure all of them and enable the formatter in the ETB. This allows tracing on dual core systems (e.g. omap4). Change-Id: I028657d5cf2bee1b23f193d4387b607953b35888 Signed-off-by: Arve Hjønnevåg --- arch/arm/include/asm/hardware/coresight.h | 16 +- arch/arm/kernel/etm.c | 230 +++++++++++++++------- 2 files changed, 164 insertions(+), 82 deletions(-) diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h index 782abba508ad..8f8ae5833587 100644 --- a/arch/arm/include/asm/hardware/coresight.h +++ b/arch/arm/include/asm/hardware/coresight.h @@ -25,9 +25,9 @@ #define TRACER_TIMEOUT 10000 -#define etm_writel(t, v, x) \ - (writel_relaxed((v), (t)->etm_regs + (x))) -#define etm_readl(t, x) (readl_relaxed((t)->etm_regs + (x))) +#define etm_writel(t, id, v, x) \ + (writel_relaxed((v), (t)->etm_regs[(id)] + (x))) +#define etm_readl(t, id, x) (readl_relaxed((t)->etm_regs[(id)] + (x))) /* CoreSight Management Registers */ #define CSMR_LOCKACCESS 0xfb0 @@ -126,6 +126,8 @@ ETMCTRL_BRANCH_OUTPUT | \ ETMCTRL_DO_CONTEXTID) +#define ETMR_TRACEIDR 0x200 + /* ETM management registers, "ETM Architecture", 3.5.24 */ #define ETMMR_OSLAR 0x300 #define ETMMR_OSLSR 0x304 @@ -148,14 +150,16 @@ #define ETBFF_TRIGIN BIT(8) #define ETBFF_TRIGEVT BIT(9) #define ETBFF_TRIGFL BIT(10) +#define ETBFF_STOPFL BIT(12) #define etb_writel(t, v, x) \ (writel_relaxed((v), (t)->etb_regs + (x))) #define etb_readl(t, x) (readl_relaxed((t)->etb_regs + (x))) -#define etm_lock(t) do { etm_writel((t), 0, CSMR_LOCKACCESS); } while (0) -#define etm_unlock(t) \ - do { etm_writel((t), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0) +#define etm_lock(t, id) \ + do { etm_writel((t), (id), 0, CSMR_LOCKACCESS); } while (0) +#define etm_unlock(t, id) \ + do { etm_writel((t), (id), CS_LAR_KEY, CSMR_LOCKACCESS); } while (0) #define etb_lock(t) do { etb_writel((t), 0, CSMR_LOCKACCESS); } while (0) #define etb_unlock(t) \ diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 20aee26b9550..2be8f5b8c03b 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -37,10 +38,12 @@ MODULE_AUTHOR("Alexander Shishkin"); struct tracectx { unsigned int etb_bufsz; void __iomem *etb_regs; - void __iomem *etm_regs; + void __iomem **etm_regs; + int etm_regs_count; unsigned long flags; int ncmppairs; int etm_portsz; + u32 etb_fc; unsigned long range_start; unsigned long range_end; unsigned long data_range_start; @@ -61,7 +64,7 @@ static inline bool trace_isrunning(struct tracectx *t) return !!(t->flags & TRACER_RUNNING); } -static int etm_setup_address_range(struct tracectx *t, int n, +static int etm_setup_address_range(struct tracectx *t, int id, int n, unsigned long start, unsigned long end, int exclude, int data) { u32 flags = ETMAAT_ARM | ETMAAT_IGNCONTEXTID | ETMAAT_IGNSECURITY | @@ -80,41 +83,31 @@ static int etm_setup_address_range(struct tracectx *t, int n, flags |= ETMAAT_IEXEC; /* first comparator for the range */ - etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2)); - etm_writel(t, start, ETMR_COMP_VAL(n * 2)); + etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2)); + etm_writel(t, id, start, ETMR_COMP_VAL(n * 2)); /* second comparator is right next to it */ - etm_writel(t, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); - etm_writel(t, end, ETMR_COMP_VAL(n * 2 + 1)); + etm_writel(t, id, flags, ETMR_COMP_ACC_TYPE(n * 2 + 1)); + etm_writel(t, id, end, ETMR_COMP_VAL(n * 2 + 1)); if (data) { flags = exclude ? ETMVDC3_EXCLONLY : 0; if (exclude) n += 8; - etm_writel(t, flags | BIT(n), ETMR_VIEWDATACTRL3); + etm_writel(t, id, flags | BIT(n), ETMR_VIEWDATACTRL3); } else { flags = exclude ? ETMTE_INCLEXCL : 0; - etm_writel(t, flags | (1 << n), ETMR_TRACEENCTRL); + etm_writel(t, id, flags | (1 << n), ETMR_TRACEENCTRL); } return 0; } -static int trace_start(struct tracectx *t) +static int trace_start_etm(struct tracectx *t, int id) { u32 v; unsigned long timeout = TRACER_TIMEOUT; - etb_unlock(t); - - t->dump_initial_etb = false; - etb_writel(t, 0, ETBR_WRITEADDR); - etb_writel(t, 0, ETBR_FORMATTERCTRL); - etb_writel(t, 1, ETBR_CTRL); - - etb_lock(t); - - /* configure etm */ v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); if (t->flags & TRACER_CYCLE_ACC) @@ -123,79 +116,122 @@ static int trace_start(struct tracectx *t) if (t->flags & TRACER_TRACE_DATA) v |= ETMCTRL_DATA_DO_ADDR; - etm_unlock(t); + etm_unlock(t, id); - etm_writel(t, v, ETMR_CTRL); + etm_writel(t, id, v, ETMR_CTRL); - while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) + while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ; if (!timeout) { dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); - etm_lock(t); + etm_lock(t, id); return -EFAULT; } if (t->range_start || t->range_end) - etm_setup_address_range(t, 1, + etm_setup_address_range(t, id, 1, t->range_start, t->range_end, 0, 0); else - etm_writel(t, ETMTE_INCLEXCL, ETMR_TRACEENCTRL); + etm_writel(t, id, ETMTE_INCLEXCL, ETMR_TRACEENCTRL); - etm_writel(t, 0, ETMR_TRACEENCTRL2); - etm_writel(t, 0, ETMR_TRACESSCTRL); - etm_writel(t, 0x6f, ETMR_TRACEENEVT); + etm_writel(t, id, 0, ETMR_TRACEENCTRL2); + etm_writel(t, id, 0, ETMR_TRACESSCTRL); + etm_writel(t, id, 0x6f, ETMR_TRACEENEVT); - etm_writel(t, 0, ETMR_VIEWDATACTRL1); - etm_writel(t, 0, ETMR_VIEWDATACTRL2); + etm_writel(t, id, 0, ETMR_VIEWDATACTRL1); + etm_writel(t, id, 0, ETMR_VIEWDATACTRL2); if (t->data_range_start || t->data_range_end) - etm_setup_address_range(t, 2, t->data_range_start, + etm_setup_address_range(t, id, 2, t->data_range_start, t->data_range_end, 0, 1); else - etm_writel(t, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3); + etm_writel(t, id, ETMVDC3_EXCLONLY, ETMR_VIEWDATACTRL3); - etm_writel(t, 0x6f, ETMR_VIEWDATAEVT); + etm_writel(t, id, 0x6f, ETMR_VIEWDATAEVT); v &= ~ETMCTRL_PROGRAM; v |= ETMCTRL_PORTSEL; - etm_writel(t, v, ETMR_CTRL); + etm_writel(t, id, v, ETMR_CTRL); timeout = TRACER_TIMEOUT; - while (etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) + while (etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM && --timeout) ; if (!timeout) { dev_dbg(t->dev, "Waiting for progbit to deassert timed out\n"); - etm_lock(t); + etm_lock(t, id); return -EFAULT; } - etm_lock(t); + etm_lock(t, id); + return 0; +} + +static int trace_start(struct tracectx *t) +{ + int ret; + int id; + u32 etb_fc = t->etb_fc; + + etb_unlock(t); + + t->dump_initial_etb = false; + etb_writel(t, 0, ETBR_WRITEADDR); + etb_writel(t, etb_fc, ETBR_FORMATTERCTRL); + etb_writel(t, 1, ETBR_CTRL); + + etb_lock(t); + + /* configure etm(s) */ + for (id = 0; id < t->etm_regs_count; id++) { + ret = trace_start_etm(t, id); + if (ret) + return ret; + } t->flags |= TRACER_RUNNING; return 0; } -static int trace_stop(struct tracectx *t) +static int trace_stop_etm(struct tracectx *t, int id) { unsigned long timeout = TRACER_TIMEOUT; - etm_unlock(t); + etm_unlock(t, id); - etm_writel(t, 0x440, ETMR_CTRL); - while (!(etm_readl(t, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) + etm_writel(t, id, 0x440, ETMR_CTRL); + while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ; if (!timeout) { dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); - etm_lock(t); + etm_lock(t, id); return -EFAULT; } - etm_lock(t); + etm_lock(t, id); + return 0; +} + +static int trace_stop(struct tracectx *t) +{ + int id; + int ret; + unsigned long timeout = TRACER_TIMEOUT; + u32 etb_fc = t->etb_fc; + + for (id = 0; id < t->etm_regs_count; id++) { + ret = trace_stop_etm(t, id); + if (ret) + return ret; + } etb_unlock(t); - etb_writel(t, ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); + if (etb_fc) { + etb_fc |= ETBFF_STOPFL; + etb_writel(t, t->etb_fc, ETBR_FORMATTERCTRL); + } + etb_writel(t, etb_fc | ETBFF_MANUAL_FLUSH, ETBR_FORMATTERCTRL); timeout = TRACER_TIMEOUT; while (etb_readl(t, ETBR_FORMATTERCTRL) & @@ -390,6 +426,7 @@ static int etb_probe(struct amba_device *dev, const struct amba_id *id) goto out_release; } + t->dev = &dev->dev; t->dump_initial_etb = true; amba_set_drvdata(dev, t); @@ -505,6 +542,8 @@ static ssize_t trace_info_show(struct kobject *kobj, { u32 etb_wa, etb_ra, etb_st, etb_fc, etm_ctrl, etm_st; int datalen; + int id; + int ret; mutex_lock(&tracer.mutex); if (tracer.etb_regs) { @@ -520,28 +559,33 @@ static ssize_t trace_info_show(struct kobject *kobj, datalen = -1; } - etm_unlock(&tracer); - etm_ctrl = etm_readl(&tracer, ETMR_CTRL); - etm_st = etm_readl(&tracer, ETMR_STATUS); - etm_lock(&tracer); - mutex_unlock(&tracer.mutex); - - return sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" + ret = sprintf(buf, "Trace buffer len: %d\nComparator pairs: %d\n" "ETBR_WRITEADDR:\t%08x\n" "ETBR_READADDR:\t%08x\n" "ETBR_STATUS:\t%08x\n" - "ETBR_FORMATTERCTRL:\t%08x\n" - "ETMR_CTRL:\t%08x\n" - "ETMR_STATUS:\t%08x\n", + "ETBR_FORMATTERCTRL:\t%08x\n", datalen, tracer.ncmppairs, etb_wa, etb_ra, etb_st, - etb_fc, + etb_fc + ); + + for (id = 0; id < tracer.etm_regs_count; id++) { + etm_unlock(&tracer, id); + etm_ctrl = etm_readl(&tracer, id, ETMR_CTRL); + etm_st = etm_readl(&tracer, id, ETMR_STATUS); + etm_lock(&tracer, id); + ret += sprintf(buf + ret, "ETMR_CTRL:\t%08x\n" + "ETMR_STATUS:\t%08x\n", etm_ctrl, etm_st ); + } + mutex_unlock(&tracer.mutex); + + return ret; } static struct kobj_attribute trace_info_attr = @@ -655,37 +699,46 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) { struct tracectx *t = &tracer; int ret = 0; + void __iomem **new_regs; + int new_count; - if (t->etm_regs) { - dev_dbg(&dev->dev, "ETM already initialized\n"); - ret = -EBUSY; + mutex_lock(&t->mutex); + new_count = t->etm_regs_count + 1; + new_regs = krealloc(t->etm_regs, + sizeof(t->etm_regs[0]) * new_count, GFP_KERNEL); + + if (!new_regs) { + dev_dbg(&dev->dev, "Failed to allocate ETM register array\n"); + ret = -ENOMEM; goto out; } + t->etm_regs = new_regs; ret = amba_request_regions(dev, NULL); if (ret) goto out; - t->etm_regs = ioremap_nocache(dev->res.start, resource_size(&dev->res)); - if (!t->etm_regs) { + t->etm_regs[t->etm_regs_count] = + ioremap_nocache(dev->res.start, resource_size(&dev->res)); + if (!t->etm_regs[t->etm_regs_count]) { ret = -ENOMEM; goto out_release; } - amba_set_drvdata(dev, t); + amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]); - t->dev = &dev->dev; t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA; t->etm_portsz = 1; - etm_unlock(t); - (void)etm_readl(t, ETMMR_PDSR); + etm_unlock(t, t->etm_regs_count); + (void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR); /* dummy first read */ - (void)etm_readl(&tracer, ETMMR_OSSRR); + (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR); - t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf; - etm_writel(t, 0x440, ETMR_CTRL); - etm_lock(t); + t->ncmppairs = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE) & 0xf; + etm_writel(t, t->etm_regs_count, 0x440, ETMR_CTRL); + etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR); + etm_lock(t, t->etm_regs_count); ret = sysfs_create_file(&dev->dev.kobj, &trace_running_attr.attr); @@ -710,28 +763,33 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) dev_dbg(&dev->dev, "Failed to create trace_data_range in sysfs\n"); - dev_dbg(t->dev, "ETM AMBA driver initialized.\n"); + dev_dbg(&dev->dev, "ETM AMBA driver initialized.\n"); + + /* Enable formatter if there are multiple trace sources */ + if (new_count > 1) + t->etb_fc = ETBFF_ENFCONT | ETBFF_ENFTC; + + t->etm_regs_count = new_count; out: + mutex_unlock(&t->mutex); return ret; out_unmap: - iounmap(t->etm_regs); + iounmap(t->etm_regs[t->etm_regs_count]); out_release: amba_release_regions(dev); + mutex_unlock(&t->mutex); return ret; } static int etm_remove(struct amba_device *dev) { - struct tracectx *t = amba_get_drvdata(dev); - - iounmap(t->etm_regs); - t->etm_regs = NULL; - - amba_release_regions(dev); + int i; + struct tracectx *t = &tracer; + void __iomem *etm_regs = amba_get_drvdata(dev); sysfs_remove_file(&dev->dev.kobj, &trace_running_attr.attr); sysfs_remove_file(&dev->dev.kobj, &trace_info_attr.attr); @@ -739,6 +797,22 @@ static int etm_remove(struct amba_device *dev) sysfs_remove_file(&dev->dev.kobj, &trace_range_attr.attr); sysfs_remove_file(&dev->dev.kobj, &trace_data_range_attr.attr); + mutex_lock(&t->mutex); + for (i = 0; i < t->etm_regs_count; i++) + if (t->etm_regs[i] == etm_regs) + break; + for (; i < t->etm_regs_count - 1; i++) + t->etm_regs[i] = t->etm_regs[i + 1]; + t->etm_regs_count--; + if (!t->etm_regs_count) { + kfree(t->etm_regs); + t->etm_regs = NULL; + } + mutex_unlock(&t->mutex); + + iounmap(etm_regs); + amba_release_regions(dev); + return 0; } @@ -747,6 +821,10 @@ static struct amba_id etm_ids[] = { .id = 0x0003b921, .mask = 0x0007ffff, }, + { + .id = 0x0003b950, + .mask = 0x0007ffff, + }, { 0, 0 }, }; From 5ff24f6c1206fadaa1cbc65f9fc18d29241440fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 23 Feb 2011 16:51:58 -0800 Subject: [PATCH 084/358] ARM: etm: Power down etm(s) when tracing is not enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this change a saw an 18% increase in idle power consumption on one deivce when trace support is compiled into the kernel. Now I see the same increase only when tracing. Change-Id: I21bb5ecf1b7d29ce3790ceeb5323409cc22d5a3b Signed-off-by: Arve Hjønnevåg --- arch/arm/kernel/etm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 2be8f5b8c03b..4e51d123aa0b 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -200,7 +200,7 @@ static int trace_stop_etm(struct tracectx *t, int id) etm_unlock(t, id); - etm_writel(t, id, 0x440, ETMR_CTRL); + etm_writel(t, id, 0x441, ETMR_CTRL); while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ; if (!timeout) { @@ -736,7 +736,7 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR); t->ncmppairs = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE) & 0xf; - etm_writel(t, t->etm_regs_count, 0x440, ETMR_CTRL); + etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL); etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR); etm_lock(t, t->etm_regs_count); From 8cb3a372215f5106ecb0a8c57fa6b3e262cff3f1 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 3 Jun 2010 10:55:33 -0700 Subject: [PATCH 085/358] network: wireless: Add get_mac_addr functionality to platform Signed-off-by: Dmitry Shmidt --- include/linux/wlan_plat.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/wlan_plat.h b/include/linux/wlan_plat.h index 70ee63b44ad6..3b1e2e054fd5 100644 --- a/include/linux/wlan_plat.h +++ b/include/linux/wlan_plat.h @@ -20,6 +20,7 @@ struct wifi_platform_data { int (*set_reset)(int val); int (*set_carddetect)(int val); void *(*mem_prealloc)(int section, unsigned long size); + int (*get_mac_addr)(unsigned char *buf); }; #endif From c34d35c06a61fa633fa629e02bde4693ea104c2a Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 14 Feb 2011 16:58:48 -0800 Subject: [PATCH 086/358] net: wireless: Add get_country_code functionality to platform Signed-off-by: Dmitry Shmidt --- include/linux/wlan_plat.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/wlan_plat.h b/include/linux/wlan_plat.h index 3b1e2e054fd5..40ec3482d1ef 100644 --- a/include/linux/wlan_plat.h +++ b/include/linux/wlan_plat.h @@ -21,6 +21,7 @@ struct wifi_platform_data { int (*set_carddetect)(int val); void *(*mem_prealloc)(int section, unsigned long size); int (*get_mac_addr)(unsigned char *buf); + void *(*get_country_code)(char *ccode); }; #endif From e9990dac1f714e763f54ca12fabda83a23649c76 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 8 Jun 2011 15:16:52 -0700 Subject: [PATCH 087/358] net: wireless: Add CONFIG_WIFI_CONTROL_FUNC option Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 200020eb3005..eba19fe8eaf8 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -264,6 +264,11 @@ config MWL8K To compile this driver as a module, choose M here: the module will be called mwl8k. If unsure, say N. +config WIFI_CONTROL_FUNC + bool "Enable WiFi control function abstraction" + help + Enables Power/Reset/Carddetect function abstraction + source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/b43/Kconfig" source "drivers/net/wireless/b43legacy/Kconfig" From fd1ee5868c72d52c8e29812db92caa9c40e9faca Mon Sep 17 00:00:00 2001 From: "jun.ho.lee" Date: Fri, 9 Sep 2011 18:50:02 +0900 Subject: [PATCH 088/358] net: wireless: change the expire time about each entry of scan results Change-Id: I6e8d838d91bebc28f4cd09dcb8b9f1de775be13d Signed-off-by: jun.ho.lee Signed-off-by: Dmitry Shmidt --- net/wireless/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index d1ed4aebbbb7..cf26b232a2a5 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -55,7 +55,7 @@ * also linked into the probe response struct. */ -#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) +#define IEEE80211_SCAN_RESULT_EXPIRE (3 * HZ) static void bss_free(struct cfg80211_internal_bss *bss) { From 73728105d6120d94fbbf1c8dffb4631aca0823d3 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 20 Jun 2011 12:41:46 -0700 Subject: [PATCH 089/358] netfilter: add xt_qtaguid matching module This module allows tracking stats at the socket level for given UIDs. It replaces xt_owner. If the --uid-owner is not specified, it will just count stats based on who the skb belongs to. This will even happen on incoming skbs as it looks into the skb via xt_socket magic to see who owns it. If an skb is lost, it will be assigned to uid=0. To control what sockets of what UIDs are tagged by what, one uses: echo t $sock_fd $accounting_tag $the_billed_uid \ > /proc/net/xt_qtaguid/ctrl So whenever an skb belongs to a sock_fd, it will be accounted against $the_billed_uid and matching stats will show up under the uid with the given $accounting_tag. Because the number of allocations for the stats structs is not that big: ~500 apps * 32 per app we'll just do it atomic. This avoids walking lists many times, and the fancy worker thread handling. Slabs will grow when needed later. It use netdevice and inetaddr notifications instead of hooks in the core dev code to track when a device comes and goes. This removes the need for exposed iface_stat.h. Put procfs dirs in /proc/net/xt_qtaguid/ ctrl stats iface_stat//... The uid stats are obtainable in ./stats. Change-Id: I01af4fd91c8de651668d3decb76d9bdc1e343919 Signed-off-by: JP Abgrall --- include/linux/android_aid.h | 2 + include/linux/netfilter/xt_qtaguid.h | 13 + net/netfilter/Kconfig | 18 + net/netfilter/Makefile | 1 + net/netfilter/xt_qtaguid.c | 2785 ++++++++++++++++++++++++++ net/netfilter/xt_qtaguid_internal.h | 330 +++ net/netfilter/xt_qtaguid_print.c | 556 +++++ net/netfilter/xt_qtaguid_print.h | 120 ++ 8 files changed, 3825 insertions(+) create mode 100644 include/linux/netfilter/xt_qtaguid.h create mode 100644 net/netfilter/xt_qtaguid.c create mode 100644 net/netfilter/xt_qtaguid_internal.h create mode 100644 net/netfilter/xt_qtaguid_print.c create mode 100644 net/netfilter/xt_qtaguid_print.h diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h index 3d7a5ead1200..6f1fa1792dfc 100644 --- a/include/linux/android_aid.h +++ b/include/linux/android_aid.h @@ -22,5 +22,7 @@ #define AID_INET KGIDT_INIT(3003) #define AID_NET_RAW KGIDT_INIT(3004) #define AID_NET_ADMIN KGIDT_INIT(3005) +#define AID_NET_BW_STATS KGIDT_INIT(3006) /* read bandwidth statistics */ +#define AID_NET_BW_ACCT KGIDT_INIT(3007) /* change bandwidth statistics accounting */ #endif diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h new file mode 100644 index 000000000000..ca60fbdec2f3 --- /dev/null +++ b/include/linux/netfilter/xt_qtaguid.h @@ -0,0 +1,13 @@ +#ifndef _XT_QTAGUID_MATCH_H +#define _XT_QTAGUID_MATCH_H + +/* For now we just replace the xt_owner. + * FIXME: make iptables aware of qtaguid. */ +#include + +#define XT_QTAGUID_UID XT_OWNER_UID +#define XT_QTAGUID_GID XT_OWNER_GID +#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET +#define xt_qtaguid_match_info xt_owner_match_info + +#endif /* _XT_QTAGUID_MATCH_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index e9410d17619d..7a49f414c13e 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1231,6 +1231,8 @@ config NETFILTER_XT_MATCH_OWNER based on who created the socket: the user or group. It is also possible to check whether a socket actually exists. + Conflicts with '"quota, tag, uid" match' + config NETFILTER_XT_MATCH_POLICY tristate 'IPsec "policy" match support' depends on XFRM @@ -1264,6 +1266,22 @@ config NETFILTER_XT_MATCH_PKTTYPE To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_MATCH_QTAGUID + bool '"quota, tag, owner" match and stats support' + depends on NETFILTER_XT_MATCH_SOCKET + depends on NETFILTER_XT_MATCH_OWNER=n + help + This option replaces the `owner' match. In addition to matching + on uid, it keeps stats based on a tag assigned to a socket. + The full tag is comprised of a UID and an accounting tag. + The tags are assignable to sockets from user space (e.g. a download + manager can assign the socket to another UID for accounting). + Stats and control are done via /proc/net/xt_qtaguid/. + It replaces owner as it takes the same arguments, but should + really be recognized by the iptables tool. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_QUOTA tristate '"quota" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index bffdad774da7..06336758b386 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -151,6 +151,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_CGROUP) += xt_cgroup.o obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c new file mode 100644 index 000000000000..b0a221806878 --- /dev/null +++ b/net/netfilter/xt_qtaguid.c @@ -0,0 +1,2785 @@ +/* + * Kernel iptables module to track stats for packets based on user tags. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * There are run-time debug flags enabled via the debug_mask module param, or + * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h. + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "xt_qtaguid_internal.h" +#include "xt_qtaguid_print.h" + +/* + * We only use the xt_socket funcs within a similar context to avoid unexpected + * return values. + */ +#define XT_SOCKET_SUPPORTED_HOOKS \ + ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) + + +static const char *module_procdirname = "xt_qtaguid"; +static struct proc_dir_entry *xt_qtaguid_procdir; + +static unsigned int proc_iface_perms = S_IRUGO; +module_param_named(iface_perms, proc_iface_perms, uint, S_IRUGO | S_IWUSR); + +static struct proc_dir_entry *xt_qtaguid_stats_file; +static unsigned int proc_stats_perms = S_IRUGO; +module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); + +static struct proc_dir_entry *xt_qtaguid_ctrl_file; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; +#else +static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUSR; +#endif +module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); + +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +static gid_t proc_stats_readall_gid = AID_NET_BW_STATS; +static gid_t proc_ctrl_write_gid = AID_NET_BW_ACCT; +#else +/* 0 means, don't limit anybody */ +static gid_t proc_stats_readall_gid; +static gid_t proc_ctrl_write_gid; +#endif +module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, + S_IRUGO | S_IWUSR); +module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, + S_IRUGO | S_IWUSR); + +/* + * Limit the number of active tags (via socket tags) for a given UID. + * Multiple processes could share the UID. + */ +static int max_sock_tags = DEFAULT_MAX_SOCK_TAGS; +module_param(max_sock_tags, int, S_IRUGO | S_IWUSR); + +/* + * After the kernel has initiallized this module, it is still possible + * to make it passive. + * Setting passive to Y: + * - the iface stats handling will not act on notifications. + * - iptables matches will never match. + * - ctrl commands silently succeed. + * - stats are always empty. + * This is mostly usefull when a bug is suspected. + */ +static bool module_passive; +module_param_named(passive, module_passive, bool, S_IRUGO | S_IWUSR); + +/* + * Control how qtaguid data is tracked per proc/uid. + * Setting tag_tracking_passive to Y: + * - don't create proc specific structs to track tags + * - don't check that active tag stats exceed some limits. + * - don't clean up socket tags on process exits. + * This is mostly usefull when a bug is suspected. + */ +static bool qtu_proc_handling_passive; +module_param_named(tag_tracking_passive, qtu_proc_handling_passive, bool, + S_IRUGO | S_IWUSR); + +#define QTU_DEV_NAME "xt_qtaguid" + +uint qtaguid_debug_mask = DEFAULT_DEBUG_MASK; +module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); + +/*---------------------------------------------------------------------------*/ +static const char *iface_stat_procdirname = "iface_stat"; +static struct proc_dir_entry *iface_stat_procdir; +static const char *iface_stat_all_procfilename = "iface_stat_all"; +static struct proc_dir_entry *iface_stat_all_procfile; + +/* + * Ordering of locks: + * outer locks: + * iface_stat_list_lock + * sock_tag_list_lock + * inner locks: + * uid_tag_data_tree_lock + * tag_counter_set_list_lock + * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock + * is acquired. + * + * Call tree with all lock holders as of 2011-09-25: + * + * iface_stat_all_proc_read() + * iface_stat_list_lock + * (struct iface_stat) + * + * qtaguid_ctrl_proc_read() + * sock_tag_list_lock + * (sock_tag_tree) + * (struct proc_qtu_data->sock_tag_list) + * prdebug_full_state() + * sock_tag_list_lock + * (sock_tag_tree) + * uid_tag_data_tree_lock + * (uid_tag_data_tree) + * (proc_qtu_data_tree) + * iface_stat_list_lock + * + * qtaguid_stats_proc_read() + * iface_stat_list_lock + * struct iface_stat->tag_stat_list_lock + * + * qtudev_open() + * uid_tag_data_tree_lock + * + * qtudev_release() + * sock_tag_data_list_lock + * uid_tag_data_tree_lock + * prdebug_full_state() + * sock_tag_list_lock + * uid_tag_data_tree_lock + * iface_stat_list_lock + * + * iface_netdev_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inetaddr_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inet6addr_event_handler() + * iface_stat_create_ipv6() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * qtaguid_mt() + * account_for_uid() + * if_tag_stat_update() + * get_sock_stat() + * sock_tag_list_lock + * struct iface_stat->tag_stat_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * + * + * qtaguid_ctrl_parse() + * ctrl_cmd_delete() + * sock_tag_list_lock + * tag_counter_set_list_lock + * iface_stat_list_lock + * struct iface_stat->tag_stat_list_lock + * uid_tag_data_tree_lock + * ctrl_cmd_counter_set() + * tag_counter_set_list_lock + * ctrl_cmd_tag() + * sock_tag_list_lock + * (sock_tag_tree) + * get_tag_ref() + * uid_tag_data_tree_lock + * (uid_tag_data_tree) + * uid_tag_data_tree_lock + * (proc_qtu_data_tree) + * ctrl_cmd_untag() + * sock_tag_list_lock + * uid_tag_data_tree_lock + * + */ +static LIST_HEAD(iface_stat_list); +static DEFINE_SPINLOCK(iface_stat_list_lock); + +static struct rb_root sock_tag_tree = RB_ROOT; +static DEFINE_SPINLOCK(sock_tag_list_lock); + +static struct rb_root tag_counter_set_tree = RB_ROOT; +static DEFINE_SPINLOCK(tag_counter_set_list_lock); + +static struct rb_root uid_tag_data_tree = RB_ROOT; +static DEFINE_SPINLOCK(uid_tag_data_tree_lock); + +static struct rb_root proc_qtu_data_tree = RB_ROOT; +/* No proc_qtu_data_tree_lock; use uid_tag_data_tree_lock */ + +static struct qtaguid_event_counts qtu_events; +/*----------------------------------------------*/ +static bool can_manipulate_uids(void) +{ + /* root pwnd */ + return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) + || in_egroup_p(proc_ctrl_write_gid); +} + +static bool can_impersonate_uid(uid_t uid) +{ + return uid == current_fsuid() || can_manipulate_uids(); +} + +static bool can_read_other_uid_stats(uid_t uid) +{ + /* root pwnd */ + return unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_gid) + || in_egroup_p(proc_stats_readall_gid); +} + +static inline void dc_add_byte_packets(struct data_counters *counters, int set, + enum ifs_tx_rx direction, + enum ifs_proto ifs_proto, + int bytes, + int packets) +{ + counters->bpc[set][direction][ifs_proto].bytes += bytes; + counters->bpc[set][direction][ifs_proto].packets += packets; +} + +static inline uint64_t dc_sum_bytes(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].bytes + + counters->bpc[set][direction][IFS_UDP].bytes + + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; +} + +static inline uint64_t dc_sum_packets(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].packets + + counters->bpc[set][direction][IFS_UDP].packets + + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; +} + +static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct tag_node *data = rb_entry(node, struct tag_node, node); + int result; + RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " + " node=%p data=%p\n", tag, node, data); + result = tag_compare(tag, data->tag); + RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " + " data.tag=0x%llx (uid=%u) res=%d\n", + tag, data->tag, get_uid_from_tag(data->tag), result); + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct tag_node *this = rb_entry(*new, struct tag_node, + node); + int result = tag_compare(data->tag, this->tag); + RB_DEBUG("qtaguid: %s(): tag=0x%llx" + " (uid=%u)\n", __func__, + this->tag, + get_uid_from_tag(this->tag)); + parent = *new; + if (result < 0) + new = &((*new)->rb_left); + else if (result > 0) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_stat, tn.node); +} + +static void tag_counter_set_tree_insert(struct tag_counter_set *data, + struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, + tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_counter_set, tn.node); + +} + +static void tag_ref_tree_insert(struct tag_ref *data, struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_ref *tag_ref_tree_search(struct rb_root *root, tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_ref, tn.node); +} + +static struct sock_tag *sock_tag_tree_search(struct rb_root *root, + const struct sock *sk) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct sock_tag *data = rb_entry(node, struct sock_tag, + sock_node); + if (sk < data->sk) + node = node->rb_left; + else if (sk > data->sk) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct sock_tag *this = rb_entry(*new, struct sock_tag, + sock_node); + parent = *new; + if (data->sk < this->sk) + new = &((*new)->rb_left); + else if (data->sk > this->sk) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->sock_node, parent, new); + rb_insert_color(&data->sock_node, root); +} + +static void sock_tag_tree_erase(struct rb_root *st_to_free_tree) +{ + struct rb_node *node; + struct sock_tag *st_entry; + + node = rb_first(st_to_free_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, sock_node); + node = rb_next(node); + CT_DEBUG("qtaguid: %s(): " + "erase st: sk=%p tag=0x%llx (uid=%u)\n", __func__, + st_entry->sk, + st_entry->tag, + get_uid_from_tag(st_entry->tag)); + rb_erase(&st_entry->sock_node, st_to_free_tree); + sockfd_put(st_entry->socket); + kfree(st_entry); + } +} + +static struct proc_qtu_data *proc_qtu_data_tree_search(struct rb_root *root, + const pid_t pid) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct proc_qtu_data *data = rb_entry(node, + struct proc_qtu_data, + node); + if (pid < data->pid) + node = node->rb_left; + else if (pid > data->pid) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void proc_qtu_data_tree_insert(struct proc_qtu_data *data, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct proc_qtu_data *this = rb_entry(*new, + struct proc_qtu_data, + node); + parent = *new; + if (data->pid < this->pid) + new = &((*new)->rb_left); + else if (data->pid > this->pid) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static void uid_tag_data_tree_insert(struct uid_tag_data *data, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct uid_tag_data *this = rb_entry(*new, + struct uid_tag_data, + node); + parent = *new; + if (data->uid < this->uid) + new = &((*new)->rb_left); + else if (data->uid > this->uid) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static struct uid_tag_data *uid_tag_data_tree_search(struct rb_root *root, + uid_t uid) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct uid_tag_data *data = rb_entry(node, + struct uid_tag_data, + node); + if (uid < data->uid) + node = node->rb_left; + else if (uid > data->uid) + node = node->rb_right; + else + return data; + } + return NULL; +} + +/* + * Allocates a new uid_tag_data struct if needed. + * Returns a pointer to the found or allocated uid_tag_data. + * Returns a PTR_ERR on failures, and lock is not held. + * If found is not NULL: + * sets *found to true if not allocated. + * sets *found to false if allocated. + */ +struct uid_tag_data *get_uid_data(uid_t uid, bool *found_res) +{ + struct uid_tag_data *utd_entry; + + /* Look for top level uid_tag_data for the UID */ + utd_entry = uid_tag_data_tree_search(&uid_tag_data_tree, uid); + DR_DEBUG("qtaguid: get_uid_data(%u) utd=%p\n", uid, utd_entry); + + if (found_res) + *found_res = utd_entry; + if (utd_entry) + return utd_entry; + + utd_entry = kzalloc(sizeof(*utd_entry), GFP_ATOMIC); + if (!utd_entry) { + pr_err("qtaguid: get_uid_data(%u): " + "tag data alloc failed\n", uid); + return ERR_PTR(-ENOMEM); + } + + utd_entry->uid = uid; + utd_entry->tag_ref_tree = RB_ROOT; + uid_tag_data_tree_insert(utd_entry, &uid_tag_data_tree); + DR_DEBUG("qtaguid: get_uid_data(%u) new utd=%p\n", uid, utd_entry); + return utd_entry; +} + +/* Never returns NULL. Either PTR_ERR or a valid ptr. */ +static struct tag_ref *new_tag_ref(tag_t new_tag, + struct uid_tag_data *utd_entry) +{ + struct tag_ref *tr_entry; + int res; + + if (utd_entry->num_active_tags + 1 > max_sock_tags) { + pr_info("qtaguid: new_tag_ref(0x%llx): " + "tag ref alloc quota exceeded. max=%d\n", + new_tag, max_sock_tags); + res = -EMFILE; + goto err_res; + + } + + tr_entry = kzalloc(sizeof(*tr_entry), GFP_ATOMIC); + if (!tr_entry) { + pr_err("qtaguid: new_tag_ref(0x%llx): " + "tag ref alloc failed\n", + new_tag); + res = -ENOMEM; + goto err_res; + } + tr_entry->tn.tag = new_tag; + /* tr_entry->num_sock_tags handled by caller */ + utd_entry->num_active_tags++; + tag_ref_tree_insert(tr_entry, &utd_entry->tag_ref_tree); + DR_DEBUG("qtaguid: new_tag_ref(0x%llx): " + " inserted new tag ref %p\n", + new_tag, tr_entry); + return tr_entry; + +err_res: + return ERR_PTR(res); +} + +static struct tag_ref *lookup_tag_ref(tag_t full_tag, + struct uid_tag_data **utd_res) +{ + struct uid_tag_data *utd_entry; + struct tag_ref *tr_entry; + bool found_utd; + uid_t uid = get_uid_from_tag(full_tag); + + DR_DEBUG("qtaguid: lookup_tag_ref(tag=0x%llx (uid=%u))\n", + full_tag, uid); + + utd_entry = get_uid_data(uid, &found_utd); + if (IS_ERR_OR_NULL(utd_entry)) { + if (utd_res) + *utd_res = utd_entry; + return NULL; + } + + tr_entry = tag_ref_tree_search(&utd_entry->tag_ref_tree, full_tag); + if (utd_res) + *utd_res = utd_entry; + DR_DEBUG("qtaguid: lookup_tag_ref(0x%llx) utd_entry=%p tr_entry=%p\n", + full_tag, utd_entry, tr_entry); + return tr_entry; +} + +/* Never returns NULL. Either PTR_ERR or a valid ptr. */ +static struct tag_ref *get_tag_ref(tag_t full_tag, + struct uid_tag_data **utd_res) +{ + struct uid_tag_data *utd_entry; + struct tag_ref *tr_entry; + + DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", + full_tag); + spin_lock_bh(&uid_tag_data_tree_lock); + tr_entry = lookup_tag_ref(full_tag, &utd_entry); + BUG_ON(IS_ERR_OR_NULL(utd_entry)); + if (!tr_entry) + tr_entry = new_tag_ref(full_tag, utd_entry); + + spin_unlock_bh(&uid_tag_data_tree_lock); + if (utd_res) + *utd_res = utd_entry; + DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", + full_tag, utd_entry, tr_entry); + return tr_entry; +} + +/* Checks and maybe frees the UID Tag Data entry */ +static void put_utd_entry(struct uid_tag_data *utd_entry) +{ + /* Are we done with the UID tag data entry? */ + if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree) && + !utd_entry->num_pqd) { + DR_DEBUG("qtaguid: %s(): " + "erase utd_entry=%p uid=%u " + "by pid=%u tgid=%u uid=%u\n", __func__, + utd_entry, utd_entry->uid, + current->pid, current->tgid, current_fsuid()); + BUG_ON(utd_entry->num_active_tags); + rb_erase(&utd_entry->node, &uid_tag_data_tree); + kfree(utd_entry); + } else { + DR_DEBUG("qtaguid: %s(): " + "utd_entry=%p still has %d tags %d proc_qtu_data\n", + __func__, utd_entry, utd_entry->num_active_tags, + utd_entry->num_pqd); + BUG_ON(!(utd_entry->num_active_tags || + utd_entry->num_pqd)); + } +} + +/* + * If no sock_tags are using this tag_ref, + * decrements refcount of utd_entry, removes tr_entry + * from utd_entry->tag_ref_tree and frees. + */ +static void free_tag_ref_from_utd_entry(struct tag_ref *tr_entry, + struct uid_tag_data *utd_entry) +{ + DR_DEBUG("qtaguid: %s(): %p tag=0x%llx (uid=%u)\n", __func__, + tr_entry, tr_entry->tn.tag, + get_uid_from_tag(tr_entry->tn.tag)); + if (!tr_entry->num_sock_tags) { + BUG_ON(!utd_entry->num_active_tags); + utd_entry->num_active_tags--; + rb_erase(&tr_entry->tn.node, &utd_entry->tag_ref_tree); + DR_DEBUG("qtaguid: %s(): erased %p\n", __func__, tr_entry); + kfree(tr_entry); + } +} + +static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) +{ + struct rb_node *node; + struct tag_ref *tr_entry; + tag_t acct_tag; + + DR_DEBUG("qtaguid: %s(tag=0x%llx (uid=%u))\n", __func__, + full_tag, get_uid_from_tag(full_tag)); + acct_tag = get_atag_from_tag(full_tag); + node = rb_first(&utd_entry->tag_ref_tree); + while (node) { + tr_entry = rb_entry(node, struct tag_ref, tn.node); + node = rb_next(node); + if (!acct_tag || tr_entry->tn.tag == full_tag) + free_tag_ref_from_utd_entry(tr_entry, utd_entry); + } +} + +static int read_proc_u64(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + uint64_t value; + char *p = page; + uint64_t *iface_entry = data; + + if (!data) + return 0; + + value = *iface_entry; + p += sprintf(p, "%llu\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int read_proc_bool(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + bool value; + char *p = page; + bool *bool_entry = data; + + if (!data) + return 0; + + value = *bool_entry; + p += sprintf(p, "%u\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int get_active_counter_set(tag_t tag) +{ + int active_set = 0; + struct tag_counter_set *tcs; + + MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)" + " (uid=%u)\n", + tag, get_uid_from_tag(tag)); + /* For now we only handle UID tags for active sets */ + tag = get_utag_from_tag(tag); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs) + active_set = tcs->active_set; + spin_unlock_bh(&tag_counter_set_list_lock); + return active_set; +} + +/* + * Find the entry for tracking the specified interface. + * Caller must hold iface_stat_list_lock + */ +static struct iface_stat *get_iface_entry(const char *ifname) +{ + struct iface_stat *iface_entry; + + /* Find the entry for tracking the specified tag within the interface */ + if (ifname == NULL) { + pr_info("qtaguid: iface_stat: get() NULL device name\n"); + return NULL; + } + + /* Iterate over interfaces */ + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (!strcmp(ifname, iface_entry->ifname)) + goto done; + } + iface_entry = NULL; +done: + return iface_entry; +} + +static int iface_stat_all_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, + int *eof, void *data) +{ + char *outp = page; + int item_index = 0; + int len; + struct iface_stat *iface_entry; + struct rtnl_link_stats64 dev_stats, *stats; + struct rtnl_link_stats64 no_dev_stats = {0}; + + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } + + CT_DEBUG("qtaguid:proc iface_stat_all " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", page, *num_items_returned, + items_to_skip, char_count, *eof); + + if (*eof) + return 0; + + /* + * This lock will prevent iface_stat_update() from changing active, + * and in turn prevent an interface from unregistering itself. + */ + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (item_index++ < items_to_skip) + continue; + + if (iface_entry->active) { + stats = dev_get_stats(iface_entry->net_dev, + &dev_stats); + } else { + stats = &no_dev_stats; + } + len = snprintf(outp, char_count, + "%s %d " + "%llu %llu %llu %llu " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals[IFS_RX].bytes, + iface_entry->totals[IFS_RX].packets, + iface_entry->totals[IFS_TX].bytes, + iface_entry->totals[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets); + if (len >= char_count) { + spin_unlock_bh(&iface_stat_list_lock); + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + spin_unlock_bh(&iface_stat_list_lock); + + *eof = 1; + return outp - page; +} + +static void iface_create_proc_worker(struct work_struct *work) +{ + struct proc_dir_entry *proc_entry; + struct iface_stat_work *isw = container_of(work, struct iface_stat_work, + iface_work); + struct iface_stat *new_iface = isw->iface_entry; + + /* iface_entries are not deleted, so safe to manipulate. */ + proc_entry = proc_mkdir(new_iface->ifname, iface_stat_procdir); + if (IS_ERR_OR_NULL(proc_entry)) { + pr_err("qtaguid: iface_stat: create_proc(): alloc failed.\n"); + kfree(isw); + return; + } + + new_iface->proc_ptr = proc_entry; + + create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_TX].bytes); + create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_RX].bytes); + create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_TX].packets); + create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_RX].packets); + create_proc_read_entry("active", proc_iface_perms, proc_entry, + read_proc_bool, &new_iface->active); + + IF_DEBUG("qtaguid: iface_stat: create_proc(): done " + "entry=%p dev=%s\n", new_iface, new_iface->ifname); + kfree(isw); +} + +/* + * Will set the entry's active state, and + * update the net_dev accordingly also. + */ +static void _iface_stat_set_active(struct iface_stat *entry, + struct net_device *net_dev, + bool activate) +{ + if (activate) { + entry->net_dev = net_dev; + entry->active = true; + IF_DEBUG("qtaguid: %s(%s): " + "enable tracking. rfcnt=%d\n", __func__, + entry->ifname, + __this_cpu_read(*net_dev->pcpu_refcnt)); + } else { + entry->active = false; + entry->net_dev = NULL; + IF_DEBUG("qtaguid: %s(%s): " + "disable tracking. rfcnt=%d\n", __func__, + entry->ifname, + __this_cpu_read(*net_dev->pcpu_refcnt)); + + } +} + +/* Caller must hold iface_stat_list_lock */ +static struct iface_stat *iface_alloc(struct net_device *net_dev) +{ + struct iface_stat *new_iface; + struct iface_stat_work *isw; + + new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); + if (new_iface == NULL) { + pr_err("qtaguid: iface_stat: create(%s): " + "iface_stat alloc failed\n", net_dev->name); + return NULL; + } + new_iface->ifname = kstrdup(net_dev->name, GFP_ATOMIC); + if (new_iface->ifname == NULL) { + pr_err("qtaguid: iface_stat: create(%s): " + "ifname alloc failed\n", net_dev->name); + kfree(new_iface); + return NULL; + } + spin_lock_init(&new_iface->tag_stat_list_lock); + new_iface->tag_stat_tree = RB_ROOT; + _iface_stat_set_active(new_iface, net_dev, true); + + /* + * ipv6 notifier chains are atomic :( + * No create_proc_read_entry() for you! + */ + isw = kmalloc(sizeof(*isw), GFP_ATOMIC); + if (!isw) { + pr_err("qtaguid: iface_stat: create(%s): " + "work alloc failed\n", new_iface->ifname); + _iface_stat_set_active(new_iface, net_dev, false); + kfree(new_iface->ifname); + kfree(new_iface); + return NULL; + } + isw->iface_entry = new_iface; + INIT_WORK(&isw->iface_work, iface_create_proc_worker); + schedule_work(&isw->iface_work); + list_add(&new_iface->list, &iface_stat_list); + return new_iface; +} + +static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, + struct iface_stat *iface) +{ + struct rtnl_link_stats64 dev_stats, *stats; + bool stats_rewound; + + stats = dev_get_stats(net_dev, &dev_stats); + /* No empty packets */ + stats_rewound = + (stats->rx_bytes < iface->last_known[IFS_RX].bytes) + || (stats->tx_bytes < iface->last_known[IFS_TX].bytes); + + IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p " + "bytes rx/tx=%llu/%llu " + "active=%d last_known=%d " + "stats_rewound=%d\n", __func__, + net_dev ? net_dev->name : "?", + iface, net_dev, + stats->rx_bytes, stats->tx_bytes, + iface->active, iface->last_known_valid, stats_rewound); + + if (iface->active && iface->last_known_valid && stats_rewound) { + pr_warn_once("qtaguid: iface_stat: %s(%s): " + "iface reset its stats unexpectedly\n", __func__, + net_dev->name); + + iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes; + iface->totals[IFS_TX].packets += + iface->last_known[IFS_TX].packets; + iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes; + iface->totals[IFS_RX].packets += + iface->last_known[IFS_RX].packets; + iface->last_known_valid = false; + IF_DEBUG("qtaguid: %s(%s): iface=%p " + "used last known bytes rx/tx=%llu/%llu\n", __func__, + iface->ifname, iface, iface->last_known[IFS_RX].bytes, + iface->last_known[IFS_TX].bytes); + } +} + +/* + * Create a new entry for tracking the specified interface. + * Do nothing if the entry already exists. + * Called when an interface is configured with a valid IP address. + */ +static void iface_stat_create(struct net_device *net_dev, + struct in_ifaddr *ifa) +{ + struct in_device *in_dev = NULL; + const char *ifname; + struct iface_stat *entry; + __be32 ipaddr = 0; + struct iface_stat *new_iface; + + IF_DEBUG("qtaguid: iface_stat: create(%s): ifa=%p netdev=%p\n", + net_dev ? net_dev->name : "?", + ifa, net_dev); + if (!net_dev) { + pr_err("qtaguid: iface_stat: create(): no net dev\n"); + return; + } + + ifname = net_dev->name; + if (!ifa) { + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create(%s): no inet dev\n", + ifname); + return; + } + IF_DEBUG("qtaguid: iface_stat: create(%s): in_dev=%p\n", + ifname, in_dev); + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ifa=%p ifa_label=%s\n", + ifname, ifa, + ifa->ifa_label ? ifa->ifa_label : "(null)"); + if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) + break; + } + } + + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create(%s): no matching IP\n", + ifname); + goto done_put; + } + ipaddr = ifa->ifa_local; + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); + if (entry != NULL) { + bool activate = !ipv4_is_loopback(ipaddr); + IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", + ifname, entry); + iface_check_stats_reset_and_adjust(net_dev, entry); + _iface_stat_set_active(entry, net_dev, activate); + IF_DEBUG("qtaguid: %s(%s): " + "tracking now %d on ip=%pI4\n", __func__, + entry->ifname, activate, &ipaddr); + goto done_unlock_put; + } else if (ipv4_is_loopback(ipaddr)) { + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ignore loopback dev. ip=%pI4\n", ifname, &ipaddr); + goto done_unlock_put; + } + + new_iface = iface_alloc(net_dev); + IF_DEBUG("qtaguid: iface_stat: create(%s): done " + "entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr); +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + if (in_dev) + in_dev_put(in_dev); +} + +static void iface_stat_create_ipv6(struct net_device *net_dev, + struct inet6_ifaddr *ifa) +{ + struct in_device *in_dev; + const char *ifname; + struct iface_stat *entry; + struct iface_stat *new_iface; + int addr_type; + + IF_DEBUG("qtaguid: iface_stat: create6(): ifa=%p netdev=%p->name=%s\n", + ifa, net_dev, net_dev ? net_dev->name : ""); + if (!net_dev) { + pr_err("qtaguid: iface_stat: create6(): no net dev!\n"); + return; + } + ifname = net_dev->name; + + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create6(%s): no inet dev\n", + ifname); + return; + } + + IF_DEBUG("qtaguid: iface_stat: create6(%s): in_dev=%p\n", + ifname, in_dev); + + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create6(%s): no matching IP\n", + ifname); + goto done_put; + } + addr_type = ipv6_addr_type(&ifa->addr); + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); + if (entry != NULL) { + bool activate = !(addr_type & IPV6_ADDR_LOOPBACK); + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + ifname, entry); + iface_check_stats_reset_and_adjust(net_dev, entry); + _iface_stat_set_active(entry, net_dev, activate); + IF_DEBUG("qtaguid: %s(%s): " + "tracking now %d on ip=%pI6c\n", __func__, + entry->ifname, activate, &ifa->addr); + goto done_unlock_put; + } else if (addr_type & IPV6_ADDR_LOOPBACK) { + IF_DEBUG("qtaguid: %s(%s): " + "ignore loopback dev. ip=%pI6c\n", __func__, + ifname, &ifa->addr); + goto done_unlock_put; + } + + new_iface = iface_alloc(net_dev); + IF_DEBUG("qtaguid: iface_stat: create6(%s): done " + "entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr); + +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + in_dev_put(in_dev); +} + +static struct sock_tag *get_sock_stat_nl(const struct sock *sk) +{ + MT_DEBUG("qtaguid: get_sock_stat_nl(sk=%p)\n", sk); + return sock_tag_tree_search(&sock_tag_tree, sk); +} + +static struct sock_tag *get_sock_stat(const struct sock *sk) +{ + struct sock_tag *sock_tag_entry; + MT_DEBUG("qtaguid: get_sock_stat(sk=%p)\n", sk); + if (!sk) + return NULL; + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(sk); + spin_unlock_bh(&sock_tag_list_lock); + return sock_tag_entry; +} + +static void +data_counters_update(struct data_counters *dc, int set, + enum ifs_tx_rx direction, int proto, int bytes) +{ + switch (proto) { + case IPPROTO_TCP: + dc_add_byte_packets(dc, set, direction, IFS_TCP, bytes, 1); + break; + case IPPROTO_UDP: + dc_add_byte_packets(dc, set, direction, IFS_UDP, bytes, 1); + break; + case IPPROTO_IP: + default: + dc_add_byte_packets(dc, set, direction, IFS_PROTO_OTHER, bytes, + 1); + break; + } +} + +/* + * Update stats for the specified interface. Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called when an device is being unregistered. + */ +static void iface_stat_update(struct net_device *net_dev, bool stash_only) +{ + struct rtnl_link_stats64 dev_stats, *stats; + struct iface_stat *entry; + + stats = dev_get_stats(net_dev, &dev_stats); + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(net_dev->name); + if (entry == NULL) { + IF_DEBUG("qtaguid: iface_stat: update(%s): not tracked\n", + net_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + net_dev->name, entry); + if (!entry->active) { + IF_DEBUG("qtaguid: %s(%s): already disabled\n", __func__, + net_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + if (stash_only) { + entry->last_known[IFS_TX].bytes = stats->tx_bytes; + entry->last_known[IFS_TX].packets = stats->tx_packets; + entry->last_known[IFS_RX].bytes = stats->rx_bytes; + entry->last_known[IFS_RX].packets = stats->rx_packets; + entry->last_known_valid = true; + IF_DEBUG("qtaguid: %s(%s): " + "dev stats stashed rx/tx=%llu/%llu\n", __func__, + net_dev->name, stats->rx_bytes, stats->tx_bytes); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + entry->totals[IFS_TX].bytes += stats->tx_bytes; + entry->totals[IFS_TX].packets += stats->tx_packets; + entry->totals[IFS_RX].bytes += stats->rx_bytes; + entry->totals[IFS_RX].packets += stats->rx_packets; + /* We don't need the last_known[] anymore */ + entry->last_known_valid = false; + _iface_stat_set_active(entry, net_dev, false); + IF_DEBUG("qtaguid: %s(%s): " + "disable tracking. rx/tx=%llu/%llu\n", __func__, + net_dev->name, stats->rx_bytes, stats->tx_bytes); + spin_unlock_bh(&iface_stat_list_lock); +} + +static void tag_stat_update(struct tag_stat *tag_entry, + enum ifs_tx_rx direction, int proto, int bytes) +{ + int active_set; + active_set = get_active_counter_set(tag_entry->tn.tag); + MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%u) set=%d " + "dir=%d proto=%d bytes=%d)\n", + tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag), + active_set, direction, proto, bytes); + data_counters_update(&tag_entry->counters, active_set, direction, + proto, bytes); + if (tag_entry->parent_counters) + data_counters_update(tag_entry->parent_counters, active_set, + direction, proto, bytes); +} + +/* + * Create a new entry for tracking the specified {acct_tag,uid_tag} within + * the interface. + * iface_entry->tag_stat_list_lock should be held. + */ +static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, + tag_t tag) +{ + struct tag_stat *new_tag_stat_entry = NULL; + IF_DEBUG("qtaguid: iface_stat: %s(): ife=%p tag=0x%llx" + " (uid=%u)\n", __func__, + iface_entry, tag, get_uid_from_tag(tag)); + new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); + if (!new_tag_stat_entry) { + pr_err("qtaguid: iface_stat: tag stat alloc failed\n"); + goto done; + } + new_tag_stat_entry->tn.tag = tag; + tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree); +done: + return new_tag_stat_entry; +} + +static void if_tag_stat_update(const char *ifname, uid_t uid, + const struct sock *sk, enum ifs_tx_rx direction, + int proto, int bytes) +{ + struct tag_stat *tag_stat_entry; + tag_t tag, acct_tag; + tag_t uid_tag; + struct data_counters *uid_tag_counters; + struct sock_tag *sock_tag_entry; + struct iface_stat *iface_entry; + struct tag_stat *new_tag_stat; + MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " + "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", + ifname, uid, sk, direction, proto, bytes); + + + iface_entry = get_iface_entry(ifname); + if (!iface_entry) { + pr_err("qtaguid: iface_stat: stat_update() %s not found\n", + ifname); + return; + } + /* It is ok to process data when an iface_entry is inactive */ + + MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n", + ifname, iface_entry); + + /* + * Look for a tagged sock. + * It will have an acct_uid. + */ + sock_tag_entry = get_sock_stat(sk); + if (sock_tag_entry) { + tag = sock_tag_entry->tag; + acct_tag = get_atag_from_tag(tag); + uid_tag = get_utag_from_tag(tag); + } else { + acct_tag = make_atag_from_value(0); + tag = combine_atag_with_uid(acct_tag, uid); + uid_tag = make_tag_from_uid(uid); + } + MT_DEBUG("qtaguid: iface_stat: stat_update(): " + " looking for tag=0x%llx (uid=%u) in ife=%p\n", + tag, get_uid_from_tag(tag), iface_entry); + /* Loop over tag list under this interface for {acct_tag,uid_tag} */ + spin_lock_bh(&iface_entry->tag_stat_list_lock); + + tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, + tag); + if (tag_stat_entry) { + /* + * Updating the {acct_tag, uid_tag} entry handles both stats: + * {0, uid_tag} will also get updated. + */ + tag_stat_update(tag_stat_entry, direction, proto, bytes); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + return; + } + + /* Loop over tag list under this interface for {0,uid_tag} */ + tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, + uid_tag); + if (!tag_stat_entry) { + /* Here: the base uid_tag did not exist */ + /* + * No parent counters. So + * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. + */ + new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); + uid_tag_counters = &new_tag_stat->counters; + } else { + uid_tag_counters = &tag_stat_entry->counters; + } + + if (acct_tag) { + new_tag_stat = create_if_tag_stat(iface_entry, tag); + new_tag_stat->parent_counters = uid_tag_counters; + } + tag_stat_update(new_tag_stat, direction, proto, bytes); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); +} + +static int iface_netdev_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) { + struct net_device *dev = ptr; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: netdev_event(): " + "ev=0x%lx/%s netdev=%p->name=%s\n", + event, netdev_evt_str(event), dev, dev ? dev->name : ""); + + switch (event) { + case NETDEV_UP: + iface_stat_create(dev, NULL); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static int iface_inet6addr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct inet6_ifaddr *ifa = ptr; + struct net_device *dev; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): " + "ev=0x%lx/%s ifa=%p\n", + event, netdev_evt_str(event), ifa); + + switch (event) { + case NETDEV_UP: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_create_ipv6(dev, ifa); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static int iface_inetaddr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct in_ifaddr *ifa = ptr; + struct net_device *dev; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): " + "ev=0x%lx/%s ifa=%p\n", + event, netdev_evt_str(event), ifa); + + switch (event) { + case NETDEV_UP: + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_create(dev, ifa); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block iface_netdev_notifier_blk = { + .notifier_call = iface_netdev_event_handler, +}; + +static struct notifier_block iface_inetaddr_notifier_blk = { + .notifier_call = iface_inetaddr_event_handler, +}; + +static struct notifier_block iface_inet6addr_notifier_blk = { + .notifier_call = iface_inet6addr_event_handler, +}; + +static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) +{ + int err; + + iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); + if (!iface_stat_procdir) { + pr_err("qtaguid: iface_stat: init failed to create proc entry\n"); + err = -1; + goto err; + } + + iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_all_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_all proc entry\n"); + err = -1; + goto err_zap_entry; + } + iface_stat_all_procfile->read_proc = iface_stat_all_proc_read; + + + err = register_netdevice_notifier(&iface_netdev_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register dev event handler\n"); + goto err_zap_all_stats_entry; + } + err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register ipv4 dev event handler\n"); + goto err_unreg_nd; + } + + err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register ipv6 dev event handler\n"); + goto err_unreg_ip4_addr; + } + return 0; + +err_unreg_ip4_addr: + unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); +err_unreg_nd: + unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_all_stats_entry: + remove_proc_entry(iface_stat_all_procfilename, parent_procdir); +err_zap_entry: + remove_proc_entry(iface_stat_procdirname, parent_procdir); +err: + return err; +} + +static struct sock *qtaguid_find_sk(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct sock *sk; + unsigned int hook_mask = (1 << par->hooknum); + + MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, + par->hooknum, par->family); + + /* + * Let's not abuse the the xt_socket_get*_sk(), or else it will + * return garbage SKs. + */ + if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) + return NULL; + + switch (par->family) { + case NFPROTO_IPV6: + sk = xt_socket_get6_sk(skb, par); + break; + case NFPROTO_IPV4: + sk = xt_socket_get4_sk(skb, par); + break; + default: + return NULL; + } + + /* + * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. + * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 + * Not fixed in 3.0-r3 :( + */ + if (sk) { + MT_DEBUG("qtaguid: %p->sk_proto=%u " + "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); + if (sk->sk_state == TCP_TIME_WAIT) { + xt_socket_put_sk(sk); + sk = NULL; + } + } + return sk; +} + +static void account_for_uid(const struct sk_buff *skb, + const struct sock *alternate_sk, uid_t uid, + struct xt_action_param *par) +{ + const struct net_device *el_dev; + + if (!skb->dev) { + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); + } else if (unlikely(!el_dev->name)) { + pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); + } else { + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n", + par->hooknum, + el_dev->name, + el_dev->type); + + if_tag_stat_update(el_dev->name, uid, + skb->sk ? skb->sk : alternate_sk, + par->in ? IFS_RX : IFS_TX, + ip_hdr(skb)->protocol, skb->len); + } +} + +static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_qtaguid_match_info *info = par->matchinfo; + const struct file *filp; + bool got_sock = false; + struct sock *sk; + uid_t sock_uid; + bool res; + + if (unlikely(module_passive)) + return (info->match ^ info->invert) == 0; + + MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", + par->hooknum, skb, par->in, par->out, par->family); + + atomic64_inc(&qtu_events.match_calls); + if (skb == NULL) { + res = (info->match ^ info->invert) == 0; + goto ret_res; + } + + sk = skb->sk; + + if (sk == NULL) { + /* + * A missing sk->sk_socket happens when packets are in-flight + * and the matching socket is already closed and gone. + */ + sk = qtaguid_find_sk(skb, par); + /* + * If we got the socket from the find_sk(), we will need to put + * it back, as nf_tproxy_get_sock_v4() got it. + */ + got_sock = sk; + if (sk) + atomic64_inc(&qtu_events.match_found_sk_in_ct); + else + atomic64_inc(&qtu_events.match_found_no_sk_in_ct); + } else { + atomic64_inc(&qtu_events.match_found_sk); + } + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", + par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); + if (sk != NULL) { + MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", + par->hooknum, sk, sk->sk_socket, + sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); + filp = sk->sk_socket ? sk->sk_socket->file : NULL; + MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", + par->hooknum, filp ? filp->f_cred->fsuid : -1); + } + + if (sk == NULL || sk->sk_socket == NULL) { + /* + * Here, the qtaguid_find_sk() using connection tracking + * couldn't find the owner, so for now we just count them + * against the system. + */ + /* + * TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not + * requested. + */ + if (!(info->match & XT_QTAGUID_UID)) + account_for_uid(skb, sk, 0, par); + MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", + par->hooknum, + sk ? sk->sk_socket : NULL); + res = (info->match ^ info->invert) == 0; + atomic64_inc(&qtu_events.match_no_sk); + goto put_sock_ret_res; + } else if (info->match & info->invert & XT_QTAGUID_SOCKET) { + res = false; + goto put_sock_ret_res; + } + filp = sk->sk_socket->file; + if (filp == NULL) { + MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); + account_for_uid(skb, sk, 0, par); + res = ((info->match ^ info->invert) & + (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; + atomic64_inc(&qtu_events.match_no_sk_file); + goto put_sock_ret_res; + } + sock_uid = filp->f_cred->fsuid; + /* + * TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not requested + */ + if (!(info->match & XT_QTAGUID_UID)) + account_for_uid(skb, sk, sock_uid, par); + + /* + * The following two tests fail the match when: + * id not in range AND no inverted condition requested + * or id in range AND inverted condition requested + * Thus (!a && b) || (a && !b) == a ^ b + */ + if (info->match & XT_QTAGUID_UID) + if ((filp->f_cred->fsuid >= info->uid_min && + filp->f_cred->fsuid <= info->uid_max) ^ + !(info->invert & XT_QTAGUID_UID)) { + MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", + par->hooknum); + res = false; + goto put_sock_ret_res; + } + if (info->match & XT_QTAGUID_GID) + if ((filp->f_cred->fsgid >= info->gid_min && + filp->f_cred->fsgid <= info->gid_max) ^ + !(info->invert & XT_QTAGUID_GID)) { + MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", + par->hooknum); + res = false; + goto put_sock_ret_res; + } + + MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); + res = true; + +put_sock_ret_res: + if (got_sock) + xt_socket_put_sk(sk); +ret_res: + MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); + return res; +} + +#ifdef DDEBUG +/* This function is not in xt_qtaguid_print.c because of locks visibility */ +static void prdebug_full_state(int indent_level, const char *fmt, ...) +{ + va_list args; + char *fmt_buff; + char *buff; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + fmt_buff = kasprintf(GFP_ATOMIC, + "qtaguid: %s(): %s {\n", __func__, fmt); + BUG_ON(!fmt_buff); + va_start(args, fmt); + buff = kvasprintf(GFP_ATOMIC, + fmt_buff, args); + BUG_ON(!buff); + pr_debug("%s", buff); + kfree(fmt_buff); + kfree(buff); + va_end(args); + + spin_lock_bh(&sock_tag_list_lock); + prdebug_sock_tag_tree(indent_level, &sock_tag_tree); + spin_unlock_bh(&sock_tag_list_lock); + + spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); + prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); + prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); + spin_unlock_bh(&uid_tag_data_tree_lock); + spin_unlock_bh(&sock_tag_list_lock); + + spin_lock_bh(&iface_stat_list_lock); + prdebug_iface_stat_list(indent_level, &iface_stat_list); + spin_unlock_bh(&iface_stat_list_lock); + + pr_debug("qtaguid: %s(): }\n", __func__); +} +#else +static void prdebug_full_state(int indent_level, const char *fmt, ...) {} +#endif + +/* + * Procfs reader to get all active socket tags using style "1)" as described in + * fs/proc/generic.c + */ +static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, int *eof, + void *data) +{ + char *outp = page; + int len; + uid_t uid; + struct rb_node *node; + struct sock_tag *sock_tag_entry; + int item_index = 0; + int indent_level = 0; + long f_count; + + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } + + if (*eof) + return 0; + + CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", + page, items_to_skip, char_count, *eof); + + spin_lock_bh(&sock_tag_list_lock); + for (node = rb_first(&sock_tag_tree); + node; + node = rb_next(node)) { + if (item_index++ < items_to_skip) + continue; + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + uid = get_uid_from_tag(sock_tag_entry->tag); + CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " + "pid=%u\n", + sock_tag_entry->sk, + sock_tag_entry->tag, + uid, + sock_tag_entry->pid + ); + f_count = atomic_long_read( + &sock_tag_entry->socket->file->f_count); + len = snprintf(outp, char_count, + "sock=%p tag=0x%llx (uid=%u) pid=%u " + "f_count=%lu\n", + sock_tag_entry->sk, + sock_tag_entry->tag, uid, + sock_tag_entry->pid, f_count); + if (len >= char_count) { + spin_unlock_bh(&sock_tag_list_lock); + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + spin_unlock_bh(&sock_tag_list_lock); + + if (item_index++ >= items_to_skip) { + len = snprintf(outp, char_count, + "events: sockets_tagged=%llu " + "sockets_untagged=%llu " + "counter_set_changes=%llu " + "delete_cmds=%llu " + "iface_events=%llu " + "match_calls=%llu " + "match_found_sk=%llu " + "match_found_sk_in_ct=%llu " + "match_found_no_sk_in_ct=%llu " + "match_no_sk=%llu " + "match_no_sk_file=%llu\n", + atomic64_read(&qtu_events.sockets_tagged), + atomic64_read(&qtu_events.sockets_untagged), + atomic64_read(&qtu_events.counter_set_changes), + atomic64_read(&qtu_events.delete_cmds), + atomic64_read(&qtu_events.iface_events), + atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_found_sk), + atomic64_read(&qtu_events.match_found_sk_in_ct), + atomic64_read( + &qtu_events.match_found_no_sk_in_ct), + atomic64_read(&qtu_events.match_no_sk), + atomic64_read(&qtu_events.match_no_sk_file)); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + + /* Count the following as part of the last item_index */ + if (item_index > items_to_skip) { + prdebug_full_state(indent_level, "proc ctrl"); + } + + *eof = 1; + return outp - page; +} + +/* + * Delete socket tags, and stat tags associated with a given + * accouting tag and uid. + */ +static int ctrl_cmd_delete(const char *input) +{ + char cmd; + uid_t uid; + uid_t entry_uid; + tag_t acct_tag; + tag_t tag; + int res, argc; + struct iface_stat *iface_entry; + struct rb_node *node; + struct sock_tag *st_entry; + struct rb_root st_to_free_tree = RB_ROOT; + struct tag_stat *ts_entry; + struct tag_counter_set *tcs_entry; + struct tag_ref *tr_entry; + struct uid_tag_data *utd_entry; + + argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); + CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " + "user_tag=0x%llx uid=%u\n", input, argc, cmd, + acct_tag, uid); + if (argc < 2) { + res = -EINVAL; + goto err; + } + if (!valid_atag(acct_tag)) { + pr_info("qtaguid: ctrl_delete(%s): invalid tag\n", input); + res = -EINVAL; + goto err; + } + if (argc < 3) { + uid = current_fsuid(); + } else if (!can_impersonate_uid(uid)) { + pr_info("qtaguid: ctrl_delete(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err; + } + + tag = combine_atag_with_uid(acct_tag, uid); + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "looking for tag=0x%llx (uid=%u)\n", + input, tag, uid); + + /* Delete socket tags */ + spin_lock_bh(&sock_tag_list_lock); + node = rb_first(&sock_tag_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, sock_node); + entry_uid = get_uid_from_tag(st_entry->tag); + node = rb_next(node); + if (entry_uid != uid) + continue; + + CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n", + input, st_entry->tag, entry_uid); + + if (!acct_tag || st_entry->tag == tag) { + rb_erase(&st_entry->sock_node, &sock_tag_tree); + /* Can't sockfd_put() within spinlock, do it later. */ + sock_tag_tree_insert(st_entry, &st_to_free_tree); + tr_entry = lookup_tag_ref(st_entry->tag, NULL); + BUG_ON(tr_entry->num_sock_tags <= 0); + tr_entry->num_sock_tags--; + /* + * TODO: remove if, and start failing. + * This is a hack to work around the fact that in some + * places we have "if (IS_ERR_OR_NULL(pqd_entry))" + * and are trying to work around apps + * that didn't open the /dev/xt_qtaguid. + */ + if (st_entry->list.next && st_entry->list.prev) + list_del(&st_entry->list); + } + } + spin_unlock_bh(&sock_tag_list_lock); + + sock_tag_tree_erase(&st_to_free_tree); + + /* Delete tag counter-sets */ + spin_lock_bh(&tag_counter_set_list_lock); + /* Counter sets are only on the uid tag, not full tag */ + tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs_entry) { + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "erase tcs: tag=0x%llx (uid=%u) set=%d\n", + input, + tcs_entry->tn.tag, + get_uid_from_tag(tcs_entry->tn.tag), + tcs_entry->active_set); + rb_erase(&tcs_entry->tn.node, &tag_counter_set_tree); + kfree(tcs_entry); + } + spin_unlock_bh(&tag_counter_set_list_lock); + + /* + * If acct_tag is 0, then all entries belonging to uid are + * erased. + */ + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + spin_lock_bh(&iface_entry->tag_stat_list_lock); + node = rb_first(&iface_entry->tag_stat_tree); + while (node) { + ts_entry = rb_entry(node, struct tag_stat, tn.node); + entry_uid = get_uid_from_tag(ts_entry->tn.tag); + node = rb_next(node); + + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "ts tag=0x%llx (uid=%u)\n", + input, ts_entry->tn.tag, entry_uid); + + if (entry_uid != uid) + continue; + if (!acct_tag || ts_entry->tn.tag == tag) { + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "erase ts: %s 0x%llx %u\n", + input, iface_entry->ifname, + get_atag_from_tag(ts_entry->tn.tag), + entry_uid); + rb_erase(&ts_entry->tn.node, + &iface_entry->tag_stat_tree); + kfree(ts_entry); + } + } + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + } + spin_unlock_bh(&iface_stat_list_lock); + + /* Cleanup the uid_tag_data */ + spin_lock_bh(&uid_tag_data_tree_lock); + node = rb_first(&uid_tag_data_tree); + while (node) { + utd_entry = rb_entry(node, struct uid_tag_data, node); + entry_uid = utd_entry->uid; + node = rb_next(node); + + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "utd uid=%u\n", + input, entry_uid); + + if (entry_uid != uid) + continue; + /* + * Go over the tag_refs, and those that don't have + * sock_tags using them are freed. + */ + put_tag_ref_tree(tag, utd_entry); + put_utd_entry(utd_entry); + } + spin_unlock_bh(&uid_tag_data_tree_lock); + + atomic64_inc(&qtu_events.delete_cmds); + res = 0; + +err: + return res; +} + +static int ctrl_cmd_counter_set(const char *input) +{ + char cmd; + uid_t uid = 0; + tag_t tag; + int res, argc; + struct tag_counter_set *tcs; + int counter_set; + + argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); + CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " + "set=%d uid=%u\n", input, argc, cmd, + counter_set, uid); + if (argc != 3) { + res = -EINVAL; + goto err; + } + if (counter_set < 0 || counter_set >= IFS_MAX_COUNTER_SETS) { + pr_info("qtaguid: ctrl_counterset(%s): invalid counter_set range\n", + input); + res = -EINVAL; + goto err; + } + if (!can_manipulate_uids()) { + pr_info("qtaguid: ctrl_counterset(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err; + } + + tag = make_tag_from_uid(uid); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (!tcs) { + tcs = kzalloc(sizeof(*tcs), GFP_ATOMIC); + if (!tcs) { + spin_unlock_bh(&tag_counter_set_list_lock); + pr_err("qtaguid: ctrl_counterset(%s): " + "failed to alloc counter set\n", + input); + res = -ENOMEM; + goto err; + } + tcs->tn.tag = tag; + tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); + CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " + "(uid=%u) set=%d\n", + input, tag, get_uid_from_tag(tag), counter_set); + } + tcs->active_set = counter_set; + spin_unlock_bh(&tag_counter_set_list_lock); + atomic64_inc(&qtu_events.counter_set_changes); + res = 0; + +err: + return res; +} + +static int ctrl_cmd_tag(const char *input) +{ + char cmd; + int sock_fd = 0; + uid_t uid = 0; + tag_t acct_tag = make_atag_from_value(0); + tag_t full_tag; + struct socket *el_socket; + int res, argc; + struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *uid_tag_data_entry; + struct proc_qtu_data *pqd_entry; + + /* Unassigned args will get defaulted later. */ + argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); + CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " + "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, + acct_tag, uid); + if (argc < 2) { + res = -EINVAL; + goto err; + } + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ + if (!el_socket) { + pr_info("qtaguid: ctrl_tag(%s): failed to lookup" + " sock_fd=%d err=%d\n", input, sock_fd, res); + goto err; + } + CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", + input, atomic_long_read(&el_socket->file->f_count), + el_socket->sk); + if (argc < 3) { + acct_tag = make_atag_from_value(0); + } else if (!valid_atag(acct_tag)) { + pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); + res = -EINVAL; + goto err_put; + } + CT_DEBUG("qtaguid: ctrl_tag(%s): " + "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " + "in_group=%d in_egroup=%d\n", + input, current->pid, current->tgid, current_uid(), + current_euid(), current_fsuid(), + in_group_p(proc_ctrl_write_gid), + in_egroup_p(proc_ctrl_write_gid)); + if (argc < 4) { + uid = current_fsuid(); + } else if (!can_impersonate_uid(uid)) { + pr_info("qtaguid: ctrl_tag(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err_put; + } + full_tag = combine_atag_with_uid(acct_tag, uid); + + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); + if (IS_ERR(tag_ref_entry)) { + res = PTR_ERR(tag_ref_entry); + spin_unlock_bh(&sock_tag_list_lock); + goto err_put; + } + tag_ref_entry->num_sock_tags++; + if (sock_tag_entry) { + struct tag_ref *prev_tag_ref_entry; + + CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " + "st@%p ...->f_count=%ld\n", + input, el_socket->sk, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count)); + /* + * This is a re-tagging, so release the sock_fd that was + * locked at the time of the 1st tagging. + * There is still the ref from this call's sockfd_lookup() so + * it can be done within the spinlock. + */ + sockfd_put(sock_tag_entry->socket); + prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, + &uid_tag_data_entry); + BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); + BUG_ON(prev_tag_ref_entry->num_sock_tags <= 0); + prev_tag_ref_entry->num_sock_tags--; + sock_tag_entry->tag = full_tag; + } else { + CT_DEBUG("qtaguid: ctrl_tag(%s): newtag for sk=%p\n", + input, el_socket->sk); + sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), + GFP_ATOMIC); + if (!sock_tag_entry) { + pr_err("qtaguid: ctrl_tag(%s): " + "socket tag alloc failed\n", + input); + spin_unlock_bh(&sock_tag_list_lock); + res = -ENOMEM; + goto err_tag_unref_put; + } + sock_tag_entry->sk = el_socket->sk; + sock_tag_entry->socket = el_socket; + sock_tag_entry->pid = current->tgid; + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, + uid); + spin_lock_bh(&uid_tag_data_tree_lock); + pqd_entry = proc_qtu_data_tree_search( + &proc_qtu_data_tree, current->tgid); + /* + * TODO: remove if, and start failing. + * At first, we want to catch user-space code that is not + * opening the /dev/xt_qtaguid. + */ + if (IS_ERR_OR_NULL(pqd_entry)) + pr_warn_once( + "qtaguid: %s(): " + "User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", __func__, + current->pid, current->tgid, + current_fsuid()); + else + list_add(&sock_tag_entry->list, + &pqd_entry->sock_tag_list); + spin_unlock_bh(&uid_tag_data_tree_lock); + + sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); + atomic64_inc(&qtu_events.sockets_tagged); + } + spin_unlock_bh(&sock_tag_list_lock); + /* We keep the ref to the socket (file) until it is untagged */ + CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n", + input, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count)); + return 0; + +err_tag_unref_put: + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + tag_ref_entry->num_sock_tags--; + free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); +err_put: + CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n", + input, atomic_long_read(&el_socket->file->f_count) - 1); + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + return res; + +err: + CT_DEBUG("qtaguid: ctrl_tag(%s): done.\n", input); + return res; +} + +static int ctrl_cmd_untag(const char *input) +{ + char cmd; + int sock_fd = 0; + struct socket *el_socket; + int res, argc; + struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + + argc = sscanf(input, "%c %d", &cmd, &sock_fd); + CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", + input, argc, cmd, sock_fd); + if (argc < 2) { + res = -EINVAL; + goto err; + } + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ + if (!el_socket) { + pr_info("qtaguid: ctrl_untag(%s): failed to lookup" + " sock_fd=%d err=%d\n", input, sock_fd, res); + goto err; + } + CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", + input, atomic_long_read(&el_socket->file->f_count), + el_socket->sk); + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + if (!sock_tag_entry) { + spin_unlock_bh(&sock_tag_list_lock); + res = -EINVAL; + goto err_put; + } + /* + * The socket already belongs to the current process + * so it can do whatever it wants to it. + */ + rb_erase(&sock_tag_entry->sock_node, &sock_tag_tree); + + tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &utd_entry); + BUG_ON(!tag_ref_entry); + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + spin_lock_bh(&uid_tag_data_tree_lock); + pqd_entry = proc_qtu_data_tree_search( + &proc_qtu_data_tree, current->tgid); + /* + * TODO: remove if, and start failing. + * At first, we want to catch user-space code that is not + * opening the /dev/xt_qtaguid. + */ + if (IS_ERR_OR_NULL(pqd_entry)) + pr_warn_once("qtaguid: %s(): " + "User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", __func__, + current->pid, current->tgid, current_fsuid()); + else + list_del(&sock_tag_entry->list); + spin_unlock_bh(&uid_tag_data_tree_lock); + /* + * We don't free tag_ref from the utd_entry here, + * only during a cmd_delete(). + */ + tag_ref_entry->num_sock_tags--; + spin_unlock_bh(&sock_tag_list_lock); + /* + * Release the sock_fd that was grabbed at tag time, + * and once more for the sockfd_lookup() here. + */ + sockfd_put(sock_tag_entry->socket); + CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n", + input, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count) - 1); + sockfd_put(el_socket); + + kfree(sock_tag_entry); + atomic64_inc(&qtu_events.sockets_untagged); + + return 0; + +err_put: + CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n", + input, atomic_long_read(&el_socket->file->f_count) - 1); + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + return res; + +err: + CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input); + return res; +} + +static int qtaguid_ctrl_parse(const char *input, int count) +{ + char cmd; + int res; + + cmd = input[0]; + /* Collect params for commands */ + switch (cmd) { + case 'd': + res = ctrl_cmd_delete(input); + break; + + case 's': + res = ctrl_cmd_counter_set(input); + break; + + case 't': + res = ctrl_cmd_tag(input); + break; + + case 'u': + res = ctrl_cmd_untag(input); + break; + + default: + res = -EINVAL; + goto err; + } + if (!res) + res = count; +err: + CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res); + return res; +} + +#define MAX_QTAGUID_CTRL_INPUT_LEN 255 +static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; + + if (unlikely(module_passive)) + return count; + + if (count >= MAX_QTAGUID_CTRL_INPUT_LEN) + return -EINVAL; + + if (copy_from_user(input_buf, buffer, count)) + return -EFAULT; + + input_buf[count] = '\0'; + return qtaguid_ctrl_parse(input_buf, count); +} + +struct proc_print_info { + char *outp; + char **num_items_returned; + struct iface_stat *iface_entry; + struct tag_stat *ts_entry; + int item_index; + int items_to_skip; + int char_count; +}; + +static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) +{ + int len; + struct data_counters *cnts; + + if (!ppi->item_index) { + if (ppi->item_index++ < ppi->items_to_skip) + return 0; + len = snprintf(ppi->outp, ppi->char_count, + "idx iface acct_tag_hex uid_tag_int cnt_set " + "rx_bytes rx_packets " + "tx_bytes tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n"); + } else { + tag_t tag = ppi->ts_entry->tn.tag; + uid_t stat_uid = get_uid_from_tag(tag); + + if (!can_read_other_uid_stats(stat_uid)) { + CT_DEBUG("qtaguid: stats line: " + "%s 0x%llx %u: insufficient priv " + "from pid=%u tgid=%u uid=%u\n", + ppi->iface_entry->ifname, + get_atag_from_tag(tag), stat_uid, + current->pid, current->tgid, current_fsuid()); + return 0; + } + if (ppi->item_index++ < ppi->items_to_skip) + return 0; + cnts = &ppi->ts_entry->counters; + len = snprintf( + ppi->outp, ppi->char_count, + "%d %s 0x%llx %u %u " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu\n", + ppi->item_index, + ppi->iface_entry->ifname, + get_atag_from_tag(tag), + stat_uid, + cnt_set, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + } + return len; +} + +static bool pp_sets(struct proc_print_info *ppi) +{ + int len; + int counter_set; + for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; + counter_set++) { + len = pp_stats_line(ppi, counter_set); + if (len >= ppi->char_count) { + *ppi->outp = '\0'; + return false; + } + if (len) { + ppi->outp += len; + ppi->char_count -= len; + (*ppi->num_items_returned)++; + } + } + return true; +} + +/* + * Procfs reader to get all tag stats using style "1)" as described in + * fs/proc/generic.c + * Groups all protocols tx/rx bytes. + */ +static int qtaguid_stats_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, int *eof, + void *data) +{ + struct proc_print_info ppi; + int len; + + ppi.outp = page; + ppi.item_index = 0; + ppi.char_count = char_count; + ppi.num_items_returned = num_items_returned; + ppi.items_to_skip = items_to_skip; + + if (unlikely(module_passive)) { + len = pp_stats_line(&ppi, 0); + /* The header should always be shorter than the buffer. */ + BUG_ON(len >= ppi.char_count); + (*num_items_returned)++; + *eof = 1; + return len; + } + + CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", page, *num_items_returned, + items_to_skip, char_count, *eof); + + if (*eof) + return 0; + + /* The idx is there to help debug when things go belly up. */ + len = pp_stats_line(&ppi, 0); + /* Don't advance the outp unless the whole line was printed */ + if (len >= ppi.char_count) { + *ppi.outp = '\0'; + return ppi.outp - page; + } + if (len) { + ppi.outp += len; + ppi.char_count -= len; + (*num_items_returned)++; + } + + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) { + struct rb_node *node; + spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock); + for (node = rb_first(&ppi.iface_entry->tag_stat_tree); + node; + node = rb_next(node)) { + ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); + if (!pp_sets(&ppi)) { + spin_unlock_bh( + &ppi.iface_entry->tag_stat_list_lock); + spin_unlock_bh(&iface_stat_list_lock); + return ppi.outp - page; + } + } + spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock); + } + spin_unlock_bh(&iface_stat_list_lock); + + *eof = 1; + return ppi.outp - page; +} + +/*------------------------------------------*/ +static int qtudev_open(struct inode *inode, struct file *file) +{ + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + struct proc_qtu_data *new_pqd_entry; + int res; + bool utd_entry_found; + + if (unlikely(qtu_proc_handling_passive)) + return 0; + + DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); + + spin_lock_bh(&uid_tag_data_tree_lock); + + /* Look for existing uid data, or alloc one. */ + utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); + if (IS_ERR_OR_NULL(utd_entry)) { + res = PTR_ERR(utd_entry); + goto err; + } + + /* Look for existing PID based proc_data */ + pqd_entry = proc_qtu_data_tree_search(&proc_qtu_data_tree, + current->tgid); + if (pqd_entry) { + pr_err("qtaguid: qtudev_open(): %u/%u %u " + "%s already opened\n", + current->pid, current->tgid, current_fsuid(), + QTU_DEV_NAME); + res = -EBUSY; + goto err_unlock_free_utd; + } + + new_pqd_entry = kzalloc(sizeof(*new_pqd_entry), GFP_ATOMIC); + if (!new_pqd_entry) { + pr_err("qtaguid: qtudev_open(): %u/%u %u: " + "proc data alloc failed\n", + current->pid, current->tgid, current_fsuid()); + res = -ENOMEM; + goto err_unlock_free_utd; + } + new_pqd_entry->pid = current->tgid; + INIT_LIST_HEAD(&new_pqd_entry->sock_tag_list); + new_pqd_entry->parent_tag_data = utd_entry; + utd_entry->num_pqd++; + + proc_qtu_data_tree_insert(new_pqd_entry, + &proc_qtu_data_tree); + + spin_unlock_bh(&uid_tag_data_tree_lock); + DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n", + current_fsuid(), new_pqd_entry); + file->private_data = new_pqd_entry; + return 0; + +err_unlock_free_utd: + if (!utd_entry_found) { + rb_erase(&utd_entry->node, &uid_tag_data_tree); + kfree(utd_entry); + } + spin_unlock_bh(&uid_tag_data_tree_lock); +err: + return res; +} + +static int qtudev_release(struct inode *inode, struct file *file) +{ + struct proc_qtu_data *pqd_entry = file->private_data; + struct uid_tag_data *utd_entry = pqd_entry->parent_tag_data; + struct sock_tag *st_entry; + struct rb_root st_to_free_tree = RB_ROOT; + struct list_head *entry, *next; + struct tag_ref *tr; + + if (unlikely(qtu_proc_handling_passive)) + return 0; + + /* + * Do not trust the current->pid, it might just be a kworker cleaning + * up after a dead proc. + */ + DR_DEBUG("qtaguid: qtudev_release(): " + "pid=%u tgid=%u uid=%u " + "pqd_entry=%p->pid=%u utd_entry=%p->active_tags=%d\n", + current->pid, current->tgid, pqd_entry->parent_tag_data->uid, + pqd_entry, pqd_entry->pid, utd_entry, + utd_entry->num_active_tags); + + spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); + + list_for_each_safe(entry, next, &pqd_entry->sock_tag_list) { + st_entry = list_entry(entry, struct sock_tag, list); + DR_DEBUG("qtaguid: %s(): " + "erase sock_tag=%p->sk=%p pid=%u tgid=%u uid=%u\n", + __func__, + st_entry, st_entry->sk, + current->pid, current->tgid, + pqd_entry->parent_tag_data->uid); + + utd_entry = uid_tag_data_tree_search( + &uid_tag_data_tree, + get_uid_from_tag(st_entry->tag)); + BUG_ON(IS_ERR_OR_NULL(utd_entry)); + DR_DEBUG("qtaguid: %s(): " + "looking for tag=0x%llx in utd_entry=%p\n", __func__, + st_entry->tag, utd_entry); + tr = tag_ref_tree_search(&utd_entry->tag_ref_tree, + st_entry->tag); + BUG_ON(!tr); + BUG_ON(tr->num_sock_tags <= 0); + tr->num_sock_tags--; + free_tag_ref_from_utd_entry(tr, utd_entry); + + rb_erase(&st_entry->sock_node, &sock_tag_tree); + list_del(&st_entry->list); + /* Can't sockfd_put() within spinlock, do it later. */ + sock_tag_tree_insert(st_entry, &st_to_free_tree); + + /* + * Try to free the utd_entry if no other proc_qtu_data is + * using it (num_pqd is 0) and it doesn't have active tags + * (num_active_tags is 0). + */ + put_utd_entry(utd_entry); + } + + rb_erase(&pqd_entry->node, &proc_qtu_data_tree); + BUG_ON(pqd_entry->parent_tag_data->num_pqd < 1); + pqd_entry->parent_tag_data->num_pqd--; + put_utd_entry(pqd_entry->parent_tag_data); + kfree(pqd_entry); + file->private_data = NULL; + + spin_unlock_bh(&uid_tag_data_tree_lock); + spin_unlock_bh(&sock_tag_list_lock); + + + sock_tag_tree_erase(&st_to_free_tree); + + prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, + current->pid, current->tgid); + return 0; +} + +/*------------------------------------------*/ +static const struct file_operations qtudev_fops = { + .owner = THIS_MODULE, + .open = qtudev_open, + .release = qtudev_release, +}; + +static struct miscdevice qtu_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = QTU_DEV_NAME, + .fops = &qtudev_fops, + /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */ +}; + +/*------------------------------------------*/ +static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) +{ + int ret; + *res_procdir = proc_mkdir(module_procdirname, init_net.proc_net); + if (!*res_procdir) { + pr_err("qtaguid: failed to create proc/.../xt_qtaguid\n"); + ret = -ENOMEM; + goto no_dir; + } + + xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, + *res_procdir); + if (!xt_qtaguid_ctrl_file) { + pr_err("qtaguid: failed to create xt_qtaguid/ctrl " + " file\n"); + ret = -ENOMEM; + goto no_ctrl_entry; + } + xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read; + xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write; + + xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, + *res_procdir); + if (!xt_qtaguid_stats_file) { + pr_err("qtaguid: failed to create xt_qtaguid/stats " + "file\n"); + ret = -ENOMEM; + goto no_stats_entry; + } + xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read; + /* + * TODO: add support counter hacking + * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write; + */ + return 0; + +no_stats_entry: + remove_proc_entry("ctrl", *res_procdir); +no_ctrl_entry: + remove_proc_entry("xt_qtaguid", NULL); +no_dir: + return ret; +} + +static struct xt_match qtaguid_mt_reg __read_mostly = { + /* + * This module masquerades as the "owner" module so that iptables + * tools can deal with it. + */ + .name = "owner", + .revision = 1, + .family = NFPROTO_UNSPEC, + .match = qtaguid_mt, + .matchsize = sizeof(struct xt_qtaguid_match_info), + .me = THIS_MODULE, +}; + +static int __init qtaguid_mt_init(void) +{ + if (qtaguid_proc_register(&xt_qtaguid_procdir) + || iface_stat_init(xt_qtaguid_procdir) + || xt_register_match(&qtaguid_mt_reg) + || misc_register(&qtu_device)) + return -1; + return 0; +} + +/* + * TODO: allow unloading of the module. + * For now stats are permanent. + * Kconfig forces'y/n' and never an 'm'. + */ + +module_init(qtaguid_mt_init); +MODULE_AUTHOR("jpa "); +MODULE_DESCRIPTION("Xtables: socket owner+tag matching and associated stats"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_owner"); +MODULE_ALIAS("ip6t_owner"); +MODULE_ALIAS("ipt_qtaguid"); +MODULE_ALIAS("ip6t_qtaguid"); diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h new file mode 100644 index 000000000000..02479d6d317d --- /dev/null +++ b/net/netfilter/xt_qtaguid_internal.h @@ -0,0 +1,330 @@ +/* + * Kernel iptables module to track stats for packets based on user tags. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __XT_QTAGUID_INTERNAL_H__ +#define __XT_QTAGUID_INTERNAL_H__ + +#include +#include +#include +#include + +/* Iface handling */ +#define IDEBUG_MASK (1<<0) +/* Iptable Matching. Per packet. */ +#define MDEBUG_MASK (1<<1) +/* Red-black tree handling. Per packet. */ +#define RDEBUG_MASK (1<<2) +/* procfs ctrl/stats handling */ +#define CDEBUG_MASK (1<<3) +/* dev and resource tracking */ +#define DDEBUG_MASK (1<<4) + +/* E.g (IDEBUG_MASK | CDEBUG_MASK | DDEBUG_MASK) */ +#define DEFAULT_DEBUG_MASK 0 + +/* + * (Un)Define these *DEBUG to compile out/in the pr_debug calls. + * All undef: text size ~ 0x3030; all def: ~ 0x4404. + */ +#define IDEBUG +#define MDEBUG +#define RDEBUG +#define CDEBUG +#define DDEBUG + +#define MSK_DEBUG(mask, ...) do { \ + if (unlikely(qtaguid_debug_mask & (mask))) \ + pr_debug(__VA_ARGS__); \ + } while (0) +#ifdef IDEBUG +#define IF_DEBUG(...) MSK_DEBUG(IDEBUG_MASK, __VA_ARGS__) +#else +#define IF_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef MDEBUG +#define MT_DEBUG(...) MSK_DEBUG(MDEBUG_MASK, __VA_ARGS__) +#else +#define MT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef RDEBUG +#define RB_DEBUG(...) MSK_DEBUG(RDEBUG_MASK, __VA_ARGS__) +#else +#define RB_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef CDEBUG +#define CT_DEBUG(...) MSK_DEBUG(CDEBUG_MASK, __VA_ARGS__) +#else +#define CT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef DDEBUG +#define DR_DEBUG(...) MSK_DEBUG(DDEBUG_MASK, __VA_ARGS__) +#else +#define DR_DEBUG(...) no_printk(__VA_ARGS__) +#endif + +extern uint qtaguid_debug_mask; + +/*---------------------------------------------------------------------------*/ +/* + * Tags: + * + * They represent what the data usage counters will be tracked against. + * By default a tag is just based on the UID. + * The UID is used as the base for policing, and can not be ignored. + * So a tag will always at least represent a UID (uid_tag). + * + * A tag can be augmented with an "accounting tag" which is associated + * with a UID. + * User space can set the acct_tag portion of the tag which is then used + * with sockets: all data belonging to that socket will be counted against the + * tag. The policing is then based on the tag's uid_tag portion, + * and stats are collected for the acct_tag portion separately. + * + * There could be + * a: {acct_tag=1, uid_tag=10003} + * b: {acct_tag=2, uid_tag=10003} + * c: {acct_tag=3, uid_tag=10003} + * d: {acct_tag=0, uid_tag=10003} + * a, b, and c represent tags associated with specific sockets. + * d is for the totals for that uid, including all untagged traffic. + * Typically d is used with policing/quota rules. + * + * We want tag_t big enough to distinguish uid_t and acct_tag. + * It might become a struct if needed. + * Nothing should be using it as an int. + */ +typedef uint64_t tag_t; /* Only used via accessors */ + +#define TAG_UID_MASK 0xFFFFFFFFULL +#define TAG_ACCT_MASK (~0xFFFFFFFFULL) + +static inline int tag_compare(tag_t t1, tag_t t2) +{ + return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; +} + +static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) +{ + return acct_tag | uid; +} +static inline tag_t make_tag_from_uid(uid_t uid) +{ + return uid; +} +static inline uid_t get_uid_from_tag(tag_t tag) +{ + return tag & TAG_UID_MASK; +} +static inline tag_t get_utag_from_tag(tag_t tag) +{ + return tag & TAG_UID_MASK; +} +static inline tag_t get_atag_from_tag(tag_t tag) +{ + return tag & TAG_ACCT_MASK; +} + +static inline bool valid_atag(tag_t tag) +{ + return !(tag & TAG_UID_MASK); +} +static inline tag_t make_atag_from_value(uint32_t value) +{ + return (uint64_t)value << 32; +} +/*---------------------------------------------------------------------------*/ + +/* + * Maximum number of socket tags that a UID is allowed to have active. + * Multiple processes belonging to the same UID contribute towards this limit. + * Special UIDs that can impersonate a UID also contribute (e.g. download + * manager, ...) + */ +#define DEFAULT_MAX_SOCK_TAGS 1024 + +/* + * For now we only track 2 sets of counters. + * The default set is 0. + * Userspace can activate another set for a given uid being tracked. + */ +#define IFS_MAX_COUNTER_SETS 2 + +enum ifs_tx_rx { + IFS_TX, + IFS_RX, + IFS_MAX_DIRECTIONS +}; + +/* For now, TCP, UDP, the rest */ +enum ifs_proto { + IFS_TCP, + IFS_UDP, + IFS_PROTO_OTHER, + IFS_MAX_PROTOS +}; + +struct byte_packet_counters { + uint64_t bytes; + uint64_t packets; +}; + +struct data_counters { + struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; +}; + +/* Generic X based nodes used as a base for rb_tree ops */ +struct tag_node { + struct rb_node node; + tag_t tag; +}; + +struct tag_stat { + struct tag_node tn; + struct data_counters counters; + /* + * If this tag is acct_tag based, we need to count against the + * matching parent uid_tag. + */ + struct data_counters *parent_counters; +}; + +struct iface_stat { + struct list_head list; /* in iface_stat_list */ + char *ifname; + bool active; + /* net_dev is only valid for active iface_stat */ + struct net_device *net_dev; + + struct byte_packet_counters totals[IFS_MAX_DIRECTIONS]; + /* + * We keep the last_known, because some devices reset their counters + * just before NETDEV_UP, while some will reset just before + * NETDEV_REGISTER (which is more normal). + * So now, if the device didn't do a NETDEV_UNREGISTER and we see + * its current dev stats smaller that what was previously known, we + * assume an UNREGISTER and just use the last_known. + */ + struct byte_packet_counters last_known[IFS_MAX_DIRECTIONS]; + /* last_known is usable when last_known_valid is true */ + bool last_known_valid; + + struct proc_dir_entry *proc_ptr; + + struct rb_root tag_stat_tree; + spinlock_t tag_stat_list_lock; +}; + +/* This is needed to create proc_dir_entries from atomic context. */ +struct iface_stat_work { + struct work_struct iface_work; + struct iface_stat *iface_entry; +}; + +/* + * Track tag that this socket is transferring data for, and not necessarily + * the uid that owns the socket. + * This is the tag against which tag_stat.counters will be billed. + * These structs need to be looked up by sock and pid. + */ +struct sock_tag { + struct rb_node sock_node; + struct sock *sk; /* Only used as a number, never dereferenced */ + /* The socket is needed for sockfd_put() */ + struct socket *socket; + /* Used to associate with a given pid */ + struct list_head list; /* in proc_qtu_data.sock_tag_list */ + pid_t pid; + + tag_t tag; +}; + +struct qtaguid_event_counts { + /* Various successful events */ + atomic64_t sockets_tagged; + atomic64_t sockets_untagged; + atomic64_t counter_set_changes; + atomic64_t delete_cmds; + atomic64_t iface_events; /* Number of NETDEV_* events handled */ + + atomic64_t match_calls; /* Number of times iptables called mt */ + /* + * match_found_sk_*: numbers related to the netfilter matching + * function finding a sock for the sk_buff. + * Total skbs processed is sum(match_found*). + */ + atomic64_t match_found_sk; /* An sk was already in the sk_buff. */ + /* The connection tracker had or didn't have the sk. */ + atomic64_t match_found_sk_in_ct; + atomic64_t match_found_no_sk_in_ct; + /* + * No sk could be found. No apparent owner. Could happen with + * unsolicited traffic. + */ + atomic64_t match_no_sk; + /* + * The file ptr in the sk_socket wasn't there. + * This might happen for traffic while the socket is being closed. + */ + atomic64_t match_no_sk_file; +}; + +/* Track the set active_set for the given tag. */ +struct tag_counter_set { + struct tag_node tn; + int active_set; +}; + +/*----------------------------------------------*/ +/* + * The qtu uid data is used to track resources that are created directly or + * indirectly by processes (uid tracked). + * It is shared by the processes with the same uid. + * Some of the resource will be counted to prevent further rogue allocations, + * some will need freeing once the owner process (uid) exits. + */ +struct uid_tag_data { + struct rb_node node; + uid_t uid; + + /* + * For the uid, how many accounting tags have been set. + */ + int num_active_tags; + /* Track the number of proc_qtu_data that reference it */ + int num_pqd; + struct rb_root tag_ref_tree; + /* No tag_node_tree_lock; use uid_tag_data_tree_lock */ +}; + +struct tag_ref { + struct tag_node tn; + + /* + * This tracks the number of active sockets that have a tag on them + * which matches this tag_ref.tn.tag. + * A tag ref can live on after the sockets are untagged. + * A tag ref can only be removed during a tag delete command. + */ + int num_sock_tags; +}; + +struct proc_qtu_data { + struct rb_node node; + pid_t pid; + + struct uid_tag_data *parent_tag_data; + + /* Tracks the sock_tags that need freeing upon this proc's death */ + struct list_head sock_tag_list; + /* No spinlock_t sock_tag_list_lock; use the global one. */ +}; + +/*----------------------------------------------*/ +#endif /* ifndef __XT_QTAGUID_INTERNAL_H__ */ diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c new file mode 100644 index 000000000000..39176785c91f --- /dev/null +++ b/net/netfilter/xt_qtaguid_print.c @@ -0,0 +1,556 @@ +/* + * Pretty printing Support for iptables xt_qtaguid module. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Most of the functions in this file just waste time if DEBUG is not defined. + * The matching xt_qtaguid_print.h will static inline empty funcs if the needed + * debug flags ore not defined. + * Those funcs that fail to allocate memory will panic as there is no need to + * hobble allong just pretending to do the requested work. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include + + +#include "xt_qtaguid_internal.h" +#include "xt_qtaguid_print.h" + +#ifdef DDEBUG + +static void _bug_on_err_or_null(void *ptr) +{ + if (IS_ERR_OR_NULL(ptr)) { + pr_err("qtaguid: kmalloc failed\n"); + BUG(); + } +} + +char *pp_tag_t(tag_t *tag) +{ + char *res; + + if (!tag) + res = kasprintf(GFP_ATOMIC, "tag_t@null{}"); + else + res = kasprintf(GFP_ATOMIC, + "tag_t@%p{tag=0x%llx, uid=%u}", + tag, *tag, get_uid_from_tag(*tag)); + _bug_on_err_or_null(res); + return res; +} + +char *pp_data_counters(struct data_counters *dc, bool showValues) +{ + char *res; + + if (!dc) + res = kasprintf(GFP_ATOMIC, "data_counters@null{}"); + else if (showValues) + res = kasprintf( + GFP_ATOMIC, "data_counters@%p{" + "set0{" + "rx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}, " + "tx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}}, " + "set1{" + "rx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}, " + "tx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}}}", + dc, + dc->bpc[0][IFS_RX][IFS_TCP].bytes, + dc->bpc[0][IFS_RX][IFS_TCP].packets, + dc->bpc[0][IFS_RX][IFS_UDP].bytes, + dc->bpc[0][IFS_RX][IFS_UDP].packets, + dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].bytes, + dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].packets, + dc->bpc[0][IFS_TX][IFS_TCP].bytes, + dc->bpc[0][IFS_TX][IFS_TCP].packets, + dc->bpc[0][IFS_TX][IFS_UDP].bytes, + dc->bpc[0][IFS_TX][IFS_UDP].packets, + dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].bytes, + dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].packets, + dc->bpc[1][IFS_RX][IFS_TCP].bytes, + dc->bpc[1][IFS_RX][IFS_TCP].packets, + dc->bpc[1][IFS_RX][IFS_UDP].bytes, + dc->bpc[1][IFS_RX][IFS_UDP].packets, + dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].bytes, + dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].packets, + dc->bpc[1][IFS_TX][IFS_TCP].bytes, + dc->bpc[1][IFS_TX][IFS_TCP].packets, + dc->bpc[1][IFS_TX][IFS_UDP].bytes, + dc->bpc[1][IFS_TX][IFS_UDP].packets, + dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].bytes, + dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].packets); + else + res = kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc); + _bug_on_err_or_null(res); + return res; +} + +char *pp_tag_node(struct tag_node *tn) +{ + char *tag_str; + char *res; + + if (!tn) { + res = kasprintf(GFP_ATOMIC, "tag_node@null{}"); + _bug_on_err_or_null(res); + return res; + } + tag_str = pp_tag_t(&tn->tag); + res = kasprintf(GFP_ATOMIC, + "tag_node@%p{tag=%s}", + tn, tag_str); + _bug_on_err_or_null(res); + kfree(tag_str); + return res; +} + +char *pp_tag_ref(struct tag_ref *tr) +{ + char *tn_str; + char *res; + + if (!tr) { + res = kasprintf(GFP_ATOMIC, "tag_ref@null{}"); + _bug_on_err_or_null(res); + return res; + } + tn_str = pp_tag_node(&tr->tn); + res = kasprintf(GFP_ATOMIC, + "tag_ref@%p{%s, num_sock_tags=%d}", + tr, tn_str, tr->num_sock_tags); + _bug_on_err_or_null(res); + kfree(tn_str); + return res; +} + +char *pp_tag_stat(struct tag_stat *ts) +{ + char *tn_str; + char *counters_str; + char *parent_counters_str; + char *res; + + if (!ts) { + res = kasprintf(GFP_ATOMIC, "tag_stat@null{}"); + _bug_on_err_or_null(res); + return res; + } + tn_str = pp_tag_node(&ts->tn); + counters_str = pp_data_counters(&ts->counters, true); + parent_counters_str = pp_data_counters(ts->parent_counters, false); + res = kasprintf(GFP_ATOMIC, + "tag_stat@%p{%s, counters=%s, parent_counters=%s}", + ts, tn_str, counters_str, parent_counters_str); + _bug_on_err_or_null(res); + kfree(tn_str); + kfree(counters_str); + kfree(parent_counters_str); + return res; +} + +char *pp_iface_stat(struct iface_stat *is) +{ + char *res; + if (!is) + res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); + else + res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" + "list=list_head{...}, " + "ifname=%s, " + "total={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "last_known_valid=%d, " + "last_known={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "active=%d, " + "net_dev=%p, " + "proc_ptr=%p, " + "tag_stat_tree=rb_root{...}}", + is, + is->ifname, + is->totals[IFS_RX].bytes, + is->totals[IFS_RX].packets, + is->totals[IFS_TX].bytes, + is->totals[IFS_TX].packets, + is->last_known_valid, + is->last_known[IFS_RX].bytes, + is->last_known[IFS_RX].packets, + is->last_known[IFS_TX].bytes, + is->last_known[IFS_TX].packets, + is->active, + is->net_dev, + is->proc_ptr); + _bug_on_err_or_null(res); + return res; +} + +char *pp_sock_tag(struct sock_tag *st) +{ + char *tag_str; + char *res; + + if (!st) { + res = kasprintf(GFP_ATOMIC, "sock_tag@null{}"); + _bug_on_err_or_null(res); + return res; + } + tag_str = pp_tag_t(&st->tag); + res = kasprintf(GFP_ATOMIC, "sock_tag@%p{" + "sock_node=rb_node{...}, " + "sk=%p socket=%p (f_count=%lu), list=list_head{...}, " + "pid=%u, tag=%s}", + st, st->sk, st->socket, atomic_long_read( + &st->socket->file->f_count), + st->pid, tag_str); + _bug_on_err_or_null(res); + kfree(tag_str); + return res; +} + +char *pp_uid_tag_data(struct uid_tag_data *utd) +{ + char *res; + + if (!utd) + res = kasprintf(GFP_ATOMIC, "uid_tag_data@null{}"); + else + res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{" + "uid=%u, num_active_acct_tags=%d, " + "num_pqd=%d, " + "tag_node_tree=rb_root{...}, " + "proc_qtu_data_tree=rb_root{...}}", + utd, utd->uid, + utd->num_active_tags, utd->num_pqd); + _bug_on_err_or_null(res); + return res; +} + +char *pp_proc_qtu_data(struct proc_qtu_data *pqd) +{ + char *parent_tag_data_str; + char *res; + + if (!pqd) { + res = kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}"); + _bug_on_err_or_null(res); + return res; + } + parent_tag_data_str = pp_uid_tag_data(pqd->parent_tag_data); + res = kasprintf(GFP_ATOMIC, "proc_qtu_data@%p{" + "node=rb_node{...}, pid=%u, " + "parent_tag_data=%s, " + "sock_tag_list=list_head{...}}", + pqd, pqd->pid, parent_tag_data_str + ); + _bug_on_err_or_null(res); + kfree(parent_tag_data_str); + return res; +} + +/*------------------------------------------*/ +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree) +{ + struct rb_node *node; + struct sock_tag *sock_tag_entry; + char *str; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(sock_tag_tree)) { + str = "sock_tag_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "sock_tag_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(sock_tag_tree); + node; + node = rb_next(node)) { + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + str = pp_sock_tag(sock_tag_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list) +{ + struct sock_tag *sock_tag_entry; + char *str; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (list_empty(sock_tag_list)) { + str = "sock_tag_list=list_head{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "sock_tag_list=list_head{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + list_for_each_entry(sock_tag_entry, sock_tag_list, list) { + str = pp_sock_tag(sock_tag_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree) +{ + char *str; + struct rb_node *node; + struct proc_qtu_data *proc_qtu_data_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(proc_qtu_data_tree)) { + str = "proc_qtu_data_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "proc_qtu_data_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(proc_qtu_data_tree); + node; + node = rb_next(node)) { + proc_qtu_data_entry = rb_entry(node, + struct proc_qtu_data, + node); + str = pp_proc_qtu_data(proc_qtu_data_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, + str); + kfree(str); + indent_level++; + prdebug_sock_tag_list(indent_level, + &proc_qtu_data_entry->sock_tag_list); + indent_level--; + + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) +{ + char *str; + struct rb_node *node; + struct tag_ref *tag_ref_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(tag_ref_tree)) { + str = "tag_ref_tree{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "tag_ref_tree{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(tag_ref_tree); + node; + node = rb_next(node)) { + tag_ref_entry = rb_entry(node, + struct tag_ref, + tn.node); + str = pp_tag_ref(tag_ref_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, + str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree) +{ + char *str; + struct rb_node *node; + struct uid_tag_data *uid_tag_data_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(uid_tag_data_tree)) { + str = "uid_tag_data_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "uid_tag_data_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(uid_tag_data_tree); + node; + node = rb_next(node)) { + uid_tag_data_entry = rb_entry(node, struct uid_tag_data, + node); + str = pp_uid_tag_data(uid_tag_data_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + if (!RB_EMPTY_ROOT(&uid_tag_data_entry->tag_ref_tree)) { + indent_level++; + prdebug_tag_ref_tree(indent_level, + &uid_tag_data_entry->tag_ref_tree); + indent_level--; + } + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree) +{ + char *str; + struct rb_node *node; + struct tag_stat *ts_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(tag_stat_tree)) { + str = "tag_stat_tree{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "tag_stat_tree{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(tag_stat_tree); + node; + node = rb_next(node)) { + ts_entry = rb_entry(node, struct tag_stat, tn.node); + str = pp_tag_stat(ts_entry); + pr_debug("%*d: %s\n", indent_level*2, indent_level, + str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list) +{ + char *str; + struct iface_stat *iface_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (list_empty(iface_stat_list)) { + str = "iface_stat_list=list_head{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "iface_stat_list=list_head{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + list_for_each_entry(iface_entry, iface_stat_list, list) { + str = pp_iface_stat(iface_entry); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + kfree(str); + + spin_lock_bh(&iface_entry->tag_stat_list_lock); + if (!RB_EMPTY_ROOT(&iface_entry->tag_stat_tree)) { + indent_level++; + prdebug_tag_stat_tree(indent_level, + &iface_entry->tag_stat_tree); + indent_level--; + } + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +#endif /* ifdef DDEBUG */ +/*------------------------------------------*/ +static const char * const netdev_event_strings[] = { + "netdev_unknown", + "NETDEV_UP", + "NETDEV_DOWN", + "NETDEV_REBOOT", + "NETDEV_CHANGE", + "NETDEV_REGISTER", + "NETDEV_UNREGISTER", + "NETDEV_CHANGEMTU", + "NETDEV_CHANGEADDR", + "NETDEV_GOING_DOWN", + "NETDEV_CHANGENAME", + "NETDEV_FEAT_CHANGE", + "NETDEV_BONDING_FAILOVER", + "NETDEV_PRE_UP", + "NETDEV_PRE_TYPE_CHANGE", + "NETDEV_POST_TYPE_CHANGE", + "NETDEV_POST_INIT", + "NETDEV_UNREGISTER_BATCH", + "NETDEV_RELEASE", + "NETDEV_NOTIFY_PEERS", + "NETDEV_JOIN", +}; + +const char *netdev_evt_str(int netdev_event) +{ + if (netdev_event < 0 + || netdev_event >= ARRAY_SIZE(netdev_event_strings)) + return "bad event num"; + return netdev_event_strings[netdev_event]; +} diff --git a/net/netfilter/xt_qtaguid_print.h b/net/netfilter/xt_qtaguid_print.h new file mode 100644 index 000000000000..b63871a0be5a --- /dev/null +++ b/net/netfilter/xt_qtaguid_print.h @@ -0,0 +1,120 @@ +/* + * Pretty printing Support for iptables xt_qtaguid module. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __XT_QTAGUID_PRINT_H__ +#define __XT_QTAGUID_PRINT_H__ + +#include "xt_qtaguid_internal.h" + +#ifdef DDEBUG + +char *pp_tag_t(tag_t *tag); +char *pp_data_counters(struct data_counters *dc, bool showValues); +char *pp_tag_node(struct tag_node *tn); +char *pp_tag_ref(struct tag_ref *tr); +char *pp_tag_stat(struct tag_stat *ts); +char *pp_iface_stat(struct iface_stat *is); +char *pp_sock_tag(struct sock_tag *st); +char *pp_uid_tag_data(struct uid_tag_data *qtd); +char *pp_proc_qtu_data(struct proc_qtu_data *pqd); + +/*------------------------------------------*/ +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list); +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree); +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree); +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree); +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree); +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree); +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list); + +#else + +/*------------------------------------------*/ +static inline char *pp_tag_t(tag_t *tag) +{ + return NULL; +} +static inline char *pp_data_counters(struct data_counters *dc, bool showValues) +{ + return NULL; +} +static inline char *pp_tag_node(struct tag_node *tn) +{ + return NULL; +} +static inline char *pp_tag_ref(struct tag_ref *tr) +{ + return NULL; +} +static inline char *pp_tag_stat(struct tag_stat *ts) +{ + return NULL; +} +static inline char *pp_iface_stat(struct iface_stat *is) +{ + return NULL; +} +static inline char *pp_sock_tag(struct sock_tag *st) +{ + return NULL; +} +static inline char *pp_uid_tag_data(struct uid_tag_data *qtd) +{ + return NULL; +} +static inline char *pp_proc_qtu_data(struct proc_qtu_data *pqd) +{ + return NULL; +} + +/*------------------------------------------*/ +static inline +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list) +{ +} +static inline +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree) +{ +} +static inline +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree) +{ +} +static inline +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) +{ +} +static inline +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree) +{ +} +static inline +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree) +{ +} +static inline +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list) +{ +} +#endif +/*------------------------------------------*/ +const char *netdev_evt_str(int netdev_event); +#endif /* ifndef __XT_QTAGUID_PRINT_H__ */ From 7715a481af4c7b5557567d5222ccbcbebde1ee56 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 1 Jul 2011 17:19:56 -0700 Subject: [PATCH 090/358] USB: OTG: Take wakelock when VBUS present Enabled by default, can disable with: echo N > /sys/module/otg_wakelock/parameters/enabled Change-Id: I34974624c52ae23490852b44c270d2f326cf6116 Signed-off-by: Todd Poynor --- drivers/usb/phy/Kconfig | 8 ++ drivers/usb/phy/Makefile | 1 + drivers/usb/phy/otg-wakelock.c | 190 +++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 drivers/usb/phy/otg-wakelock.c diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 7d1451d5bbea..231fa35b421d 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -15,6 +15,14 @@ config USB_OTG_FSM Implements OTG Final State Machine as specified in On-The-Go and Embedded Host Supplement to the USB Revision 2.0 Specification. +config USB_OTG_WAKELOCK + bool "Hold a wakelock when USB connected" + depends on WAKELOCK + select USB_OTG_UTILS + help + Select this to automatically hold a wakelock when USB is + connected, preventing suspend. + # # USB Transceiver Drivers # diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index be58adae3496..145b6a09f1b6 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_USB_PHY) += phy.o obj-$(CONFIG_OF) += of.o obj-$(CONFIG_USB_OTG_FSM) += phy-fsm-usb.o +obj-$(CONFIG_USB_OTG_WAKELOCK) += otg-wakelock.o # transceiver drivers, keep the list sorted diff --git a/drivers/usb/phy/otg-wakelock.c b/drivers/usb/phy/otg-wakelock.c new file mode 100644 index 000000000000..993162674836 --- /dev/null +++ b/drivers/usb/phy/otg-wakelock.c @@ -0,0 +1,190 @@ +/* + * otg-wakelock.c + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +static bool enabled = true; +static struct otg_transceiver *otgwl_xceiv; +static struct notifier_block otgwl_nb; + +/* + * otgwl_spinlock is held while the VBUS lock is grabbed or dropped and the + * locked field is updated to match. + */ + +static DEFINE_SPINLOCK(otgwl_spinlock); + +/* + * Only one lock, but since these 3 fields are associated with each other... + */ + +struct otgwl_lock { + char name[40]; + struct wake_lock wakelock; + bool locked; +}; + +/* + * VBUS present lock. + */ + +static struct otgwl_lock vbus_lock; + +static void otgwl_grab(struct otgwl_lock *lock) +{ + if (!lock->locked) { + wake_lock(&lock->wakelock); + lock->locked = true; + } +} + +static void otgwl_drop(struct otgwl_lock *lock) +{ + if (lock->locked) { + wake_unlock(&lock->wakelock); + lock->locked = false; + } +} + +static int otgwl_otg_notifications(struct notifier_block *nb, + unsigned long event, void *unused) +{ + unsigned long irqflags; + + if (!enabled) + return NOTIFY_OK; + + spin_lock_irqsave(&otgwl_spinlock, irqflags); + + switch (event) { + case USB_EVENT_VBUS: + case USB_EVENT_ENUMERATED: + otgwl_grab(&vbus_lock); + break; + + case USB_EVENT_NONE: + case USB_EVENT_ID: + case USB_EVENT_CHARGER: + otgwl_drop(&vbus_lock); + break; + + default: + break; + } + + spin_unlock_irqrestore(&otgwl_spinlock, irqflags); + return NOTIFY_OK; +} + +static void sync_with_xceiv_state(void) +{ + if ((otgwl_xceiv->last_event == USB_EVENT_VBUS) || + (otgwl_xceiv->last_event == USB_EVENT_ENUMERATED)) + otgwl_grab(&vbus_lock); + else + otgwl_drop(&vbus_lock); +} + +static int init_for_xceiv(void) +{ + int rv; + + if (!otgwl_xceiv) { + otgwl_xceiv = otg_get_transceiver(); + + if (!otgwl_xceiv) { + pr_err("%s: No OTG transceiver found\n", __func__); + return -ENODEV; + } + + snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s", + dev_name(otgwl_xceiv->dev)); + wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND, + vbus_lock.name); + + rv = otg_register_notifier(otgwl_xceiv, &otgwl_nb); + + if (rv) { + pr_err("%s: otg_register_notifier on transceiver %s" + " failed\n", __func__, + dev_name(otgwl_xceiv->dev)); + otgwl_xceiv = NULL; + wake_lock_destroy(&vbus_lock.wakelock); + return rv; + } + } + + return 0; +} + +static int set_enabled(const char *val, const struct kernel_param *kp) +{ + unsigned long irqflags; + int rv = param_set_bool(val, kp); + + if (rv) + return rv; + + rv = init_for_xceiv(); + + if (rv) + return rv; + + spin_lock_irqsave(&otgwl_spinlock, irqflags); + + if (enabled) + sync_with_xceiv_state(); + else + otgwl_drop(&vbus_lock); + + spin_unlock_irqrestore(&otgwl_spinlock, irqflags); + return 0; +} + +static struct kernel_param_ops enabled_param_ops = { + .set = set_enabled, + .get = param_get_bool, +}; + +module_param_cb(enabled, &enabled_param_ops, &enabled, 0644); +MODULE_PARM_DESC(enabled, "enable wakelock when VBUS present"); + +static int __init otg_wakelock_init(void) +{ + unsigned long irqflags; + + otgwl_nb.notifier_call = otgwl_otg_notifications; + + if (!init_for_xceiv()) { + spin_lock_irqsave(&otgwl_spinlock, irqflags); + + if (enabled) + sync_with_xceiv_state(); + + spin_unlock_irqrestore(&otgwl_spinlock, irqflags); + } else { + enabled = false; + } + + return 0; +} + +late_initcall(otg_wakelock_init); From d386703772d4e2e42939215a77fc71e685575048 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 21 Jun 2011 11:14:49 -0700 Subject: [PATCH 091/358] netfilter: adding the original quota2 from xtables-addons The original xt_quota in the kernel is plain broken: - counts quota at a per CPU level (was written back when ubiquitous SMP was just a dream) - provides no way to count across IPV4/IPV6. This patch is the original unaltered code from: http://sourceforge.net/projects/xtables-addons at commit e84391ce665cef046967f796dd91026851d6bbf3 Change-Id: I19d49858840effee9ecf6cff03c23b45a97efdeb Signed-off-by: JP Abgrall --- include/linux/netfilter/xt_quota2.h | 25 +++ net/netfilter/xt_quota2.c | 274 ++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 include/linux/netfilter/xt_quota2.h create mode 100644 net/netfilter/xt_quota2.c diff --git a/include/linux/netfilter/xt_quota2.h b/include/linux/netfilter/xt_quota2.h new file mode 100644 index 000000000000..eadc6903314e --- /dev/null +++ b/include/linux/netfilter/xt_quota2.h @@ -0,0 +1,25 @@ +#ifndef _XT_QUOTA_H +#define _XT_QUOTA_H + +enum xt_quota_flags { + XT_QUOTA_INVERT = 1 << 0, + XT_QUOTA_GROW = 1 << 1, + XT_QUOTA_PACKET = 1 << 2, + XT_QUOTA_NO_CHANGE = 1 << 3, + XT_QUOTA_MASK = 0x0F, +}; + +struct xt_quota_counter; + +struct xt_quota_mtinfo2 { + char name[15]; + u_int8_t flags; + + /* Comparison-invariant */ + aligned_u64 quota; + + /* Used internally by the kernel */ + struct xt_quota_counter *master __attribute__((aligned(8))); +}; + +#endif /* _XT_QUOTA_H */ diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c new file mode 100644 index 000000000000..4857008f1eb0 --- /dev/null +++ b/net/netfilter/xt_quota2.c @@ -0,0 +1,274 @@ +/* + * xt_quota2 - enhanced xt_quota that can count upwards and in packets + * as a minimal accounting match. + * by Jan Engelhardt , 2008 + * + * Originally based on xt_quota.c: + * netfilter module to enforce network quotas + * Sam Johnston + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; either + * version 2 of the License, as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include +#include "xt_quota2.h" +#include "compat_xtables.h" + +/** + * @lock: lock to protect quota writers from each other + */ +struct xt_quota_counter { + u_int64_t quota; + spinlock_t lock; + struct list_head list; + atomic_t ref; + char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)]; + struct proc_dir_entry *procfs_entry; +}; + +static LIST_HEAD(counter_list); +static DEFINE_SPINLOCK(counter_list_lock); + +static struct proc_dir_entry *proc_xt_quota; +static unsigned int quota_list_perms = S_IRUGO | S_IWUSR; +static unsigned int quota_list_uid = 0; +static unsigned int quota_list_gid = 0; +module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); +module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); +module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); + +static int quota_proc_read(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + struct xt_quota_counter *e = data; + int ret; + + spin_lock_bh(&e->lock); + ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); + spin_unlock_bh(&e->lock); + return ret; +} + +static int quota_proc_write(struct file *file, const char __user *input, + unsigned long size, void *data) +{ + struct xt_quota_counter *e = data; + char buf[sizeof("18446744073709551616")]; + + if (size > sizeof(buf)) + size = sizeof(buf); + if (copy_from_user(buf, input, size) != 0) + return -EFAULT; + buf[sizeof(buf)-1] = '\0'; + + spin_lock_bh(&e->lock); + e->quota = simple_strtoull(buf, NULL, 0); + spin_unlock_bh(&e->lock); + return size; +} + +static struct xt_quota_counter * +q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) +{ + struct xt_quota_counter *e; + unsigned int size; + + /* Do not need all the procfs things for anonymous counters. */ + size = anon ? offsetof(typeof(*e), list) : sizeof(*e); + e = kmalloc(size, GFP_KERNEL); + if (e == NULL) + return NULL; + + e->quota = q->quota; + spin_lock_init(&e->lock); + if (!anon) { + INIT_LIST_HEAD(&e->list); + atomic_set(&e->ref, 1); + strncpy(e->name, q->name, sizeof(e->name)); + } + return e; +} + +/** + * q2_get_counter - get ref to counter or create new + * @name: name of counter + */ +static struct xt_quota_counter * +q2_get_counter(const struct xt_quota_mtinfo2 *q) +{ + struct proc_dir_entry *p; + struct xt_quota_counter *e; + + if (*q->name == '\0') + return q2_new_counter(q, true); + + spin_lock_bh(&counter_list_lock); + list_for_each_entry(e, &counter_list, list) + if (strcmp(e->name, q->name) == 0) { + atomic_inc(&e->ref); + spin_unlock_bh(&counter_list_lock); + return e; + } + + e = q2_new_counter(q, false); + if (e == NULL) + goto out; + + p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, + proc_xt_quota); + if (p == NULL || IS_ERR(p)) + goto out; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + p->owner = THIS_MODULE; +#endif + p->data = e; + p->read_proc = quota_proc_read; + p->write_proc = quota_proc_write; + p->uid = quota_list_uid; + p->gid = quota_list_gid; + list_add_tail(&e->list, &counter_list); + spin_unlock_bh(&counter_list_lock); + return e; + + out: + spin_unlock_bh(&counter_list_lock); + kfree(e); + return NULL; +} + +static int quota_mt2_check(const struct xt_mtchk_param *par) +{ + struct xt_quota_mtinfo2 *q = par->matchinfo; + + if (q->flags & ~XT_QUOTA_MASK) + return -EINVAL; + + q->name[sizeof(q->name)-1] = '\0'; + if (*q->name == '.' || strchr(q->name, '/') != NULL) { + printk(KERN_ERR "xt_quota.3: illegal name\n"); + return -EINVAL; + } + + q->master = q2_get_counter(q); + if (q->master == NULL) { + printk(KERN_ERR "xt_quota.3: memory alloc failure\n"); + return -ENOMEM; + } + + return 0; +} + +static void quota_mt2_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_quota_mtinfo2 *q = par->matchinfo; + struct xt_quota_counter *e = q->master; + + if (*q->name == '\0') { + kfree(e); + return; + } + + spin_lock_bh(&counter_list_lock); + if (!atomic_dec_and_test(&e->ref)) { + spin_unlock_bh(&counter_list_lock); + return; + } + + list_del(&e->list); + remove_proc_entry(e->name, proc_xt_quota); + spin_unlock_bh(&counter_list_lock); + kfree(e); +} + +static bool +quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) +{ + struct xt_quota_mtinfo2 *q = (void *)par->matchinfo; + struct xt_quota_counter *e = q->master; + bool ret = q->flags & XT_QUOTA_INVERT; + + spin_lock_bh(&e->lock); + if (q->flags & XT_QUOTA_GROW) { + /* + * While no_change is pointless in "grow" mode, we will + * implement it here simply to have a consistent behavior. + */ + if (!(q->flags & XT_QUOTA_NO_CHANGE)) { + e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; + q->quota = e->quota; + } + ret = true; + } else { + if (e->quota >= skb->len) { + if (!(q->flags & XT_QUOTA_NO_CHANGE)) + e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; + ret = !ret; + } else { + /* we do not allow even small packets from now on */ + e->quota = 0; + } + q->quota = e->quota; + } + spin_unlock_bh(&e->lock); + return ret; +} + +static struct xt_match quota_mt2_reg[] __read_mostly = { + { + .name = "quota2", + .revision = 3, + .family = NFPROTO_IPV4, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, + { + .name = "quota2", + .revision = 3, + .family = NFPROTO_IPV6, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, +}; + +static int __init quota_mt2_init(void) +{ + int ret; + + proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); + if (proc_xt_quota == NULL) + return -EACCES; + + ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + if (ret < 0) + remove_proc_entry("xt_quota", init_net__proc_net); + return ret; +} + +static void __exit quota_mt2_exit(void) +{ + xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + remove_proc_entry("xt_quota", init_net__proc_net); +} + +module_init(quota_mt2_init); +module_exit(quota_mt2_exit); +MODULE_DESCRIPTION("Xtables: countdown quota match; up counter"); +MODULE_AUTHOR("Sam Johnston "); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_quota2"); +MODULE_ALIAS("ip6t_quota2"); From 46e03c5aa8999cfc35b50d5d33165777f2479f42 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 12 Jul 2011 12:02:59 -0700 Subject: [PATCH 092/358] netfilter: fixup the quota2, and enable. The xt_quota2 came from http://sourceforge.net/projects/xtables-addons/develop It needed tweaking for it to compile within the kernel tree. Fixed kmalloc() and create_proc_entry() invocations within a non-interruptible context. Removed useless copying of current quota back to the iptable's struct matchinfo: - those are per CPU: they will change randomly based on which cpu gets to update the value. - they prevent matching a rule: e.g. -A chain -m quota2 --name q1 --quota 123 can't be followed by -D chain -m quota2 --name q1 --quota 123 as the 123 will be compared to the struct matchinfo's quota member. Use the NETLINK NETLINK_NFLOG family to log a single message when the quota limit is reached. It uses the same packet type as ipt_ULOG, but - never copies skb data, - uses 112 as the event number (ULOG's +1) It doesn't log if the module param "event_num" is 0. Change-Id: I021d3b743db3b22158cc49acb5c94d905b501492 Signed-off-by: JP Abgrall --- net/netfilter/Kconfig | 24 ++++++ net/netfilter/Makefile | 1 + net/netfilter/xt_quota2.c | 154 ++++++++++++++++++++++++++++++++------ 3 files changed, 156 insertions(+), 23 deletions(-) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 7a49f414c13e..6b04938a7179 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1292,6 +1292,30 @@ config NETFILTER_XT_MATCH_QUOTA If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_QUOTA2 + tristate '"quota2" match support' + depends on NETFILTER_ADVANCED + help + This option adds a `quota2' match, which allows to match on a + byte counter correctly and not per CPU. + It allows naming the quotas. + This is based on http://xtables-addons.git.sourceforge.net + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +config NETFILTER_XT_MATCH_QUOTA2_LOG + bool '"quota2" Netfilter LOG support' + depends on NETFILTER_XT_MATCH_QUOTA2 + depends on IP_NF_TARGET_ULOG=n # not yes, not module, just no + default n + help + This option allows `quota2' to log ONCE when a quota limit + is passed. It logs via NETLINK using the NETLINK_NFLOG family. + It logs similarly to how ipt_ULOG would without data. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_RATEEST tristate '"rateest" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 06336758b386..cbf190adb062 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -153,6 +153,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 4857008f1eb0..aace72928530 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -12,14 +12,18 @@ * version 2 of the License, as published by the Free Software Foundation. */ #include +#include #include #include #include #include +#include #include -#include "xt_quota2.h" -#include "compat_xtables.h" +#include +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +#include +#endif /** * @lock: lock to protect quota writers from each other @@ -33,6 +37,16 @@ struct xt_quota_counter { struct proc_dir_entry *procfs_entry; }; +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +/* Harald's favorite number +1 :D From ipt_ULOG.C */ +static int qlog_nl_event = 112; +module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(event_num, + "Event number for NETLINK_NFLOG message. 0 disables log." + "111 is what ipt_ULOG uses."); +static struct sock *nflognl; +#endif + static LIST_HEAD(counter_list); static DEFINE_SPINLOCK(counter_list_lock); @@ -44,6 +58,70 @@ module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); + +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ + ulog_packet_msg_t *pm; + struct sk_buff *log_skb; + size_t size; + struct nlmsghdr *nlh; + + if (!qlog_nl_event) + return; + + size = NLMSG_SPACE(sizeof(*pm)); + size = max(size, (size_t)NLMSG_GOODSIZE); + log_skb = alloc_skb(size, GFP_ATOMIC); + if (!log_skb) { + pr_err("xt_quota2: cannot alloc skb for logging\n"); + return; + } + + nlh = nlmsg_put(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event, + sizeof(*pm), 0); + if (!nlh) { + pr_err("xt_quota2: nlmsg_put failed\n"); + kfree_skb(log_skb); + return; + } + pm = nlmsg_data(nlh); + if (skb->tstamp.tv64 == 0) + __net_timestamp((struct sk_buff *)skb); + pm->data_len = 0; + pm->hook = hooknum; + if (prefix != NULL) + strlcpy(pm->prefix, prefix, sizeof(pm->prefix)); + else + *(pm->prefix) = '\0'; + if (in) + strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name)); + else + pm->indev_name[0] = '\0'; + + if (out) + strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name)); + else + pm->outdev_name[0] = '\0'; + + NETLINK_CB(log_skb).dst_group = 1; + pr_debug("throwing 1 packets to netlink group 1\n"); + netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC); +} +#else +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ +} +#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ + static int quota_proc_read(char *page, char **start, off_t offset, int count, int *eof, void *data) { @@ -91,7 +169,7 @@ q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) if (!anon) { INIT_LIST_HEAD(&e->list); atomic_set(&e->ref, 1); - strncpy(e->name, q->name, sizeof(e->name)); + strlcpy(e->name, q->name, sizeof(e->name)); } return e; } @@ -104,42 +182,56 @@ static struct xt_quota_counter * q2_get_counter(const struct xt_quota_mtinfo2 *q) { struct proc_dir_entry *p; - struct xt_quota_counter *e; + struct xt_quota_counter *e = NULL; + struct xt_quota_counter *new_e; if (*q->name == '\0') return q2_new_counter(q, true); + /* No need to hold a lock while getting a new counter */ + new_e = q2_new_counter(q, false); + if (new_e == NULL) + goto out; + spin_lock_bh(&counter_list_lock); list_for_each_entry(e, &counter_list, list) if (strcmp(e->name, q->name) == 0) { atomic_inc(&e->ref); spin_unlock_bh(&counter_list_lock); + kfree(new_e); + pr_debug("xt_quota2: old counter name=%s", e->name); return e; } + e = new_e; + pr_debug("xt_quota2: new_counter name=%s", e->name); + list_add_tail(&e->list, &counter_list); + /* The entry having a refcount of 1 is not directly destructible. + * This func has not yet returned the new entry, thus iptables + * has not references for destroying this entry. + * For another rule to try to destroy it, it would 1st need for this + * func* to be re-invoked, acquire a new ref for the same named quota. + * Nobody will access the e->procfs_entry either. + * So release the lock. */ + spin_unlock_bh(&counter_list_lock); - e = q2_new_counter(q, false); - if (e == NULL) - goto out; - + /* create_proc_entry() is not spin_lock happy */ p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, proc_xt_quota); - if (p == NULL || IS_ERR(p)) - goto out; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) - p->owner = THIS_MODULE; -#endif + if (IS_ERR_OR_NULL(p)) { + spin_lock_bh(&counter_list_lock); + list_del(&e->list); + spin_unlock_bh(&counter_list_lock); + goto out; + } p->data = e; p->read_proc = quota_proc_read; p->write_proc = quota_proc_write; p->uid = quota_list_uid; p->gid = quota_list_gid; - list_add_tail(&e->list, &counter_list); - spin_unlock_bh(&counter_list_lock); return e; out: - spin_unlock_bh(&counter_list_lock); kfree(e); return NULL; } @@ -148,6 +240,8 @@ static int quota_mt2_check(const struct xt_mtchk_param *par) { struct xt_quota_mtinfo2 *q = par->matchinfo; + pr_debug("xt_quota2: check() flags=0x%04x", q->flags); + if (q->flags & ~XT_QUOTA_MASK) return -EINVAL; @@ -203,7 +297,6 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) */ if (!(q->flags & XT_QUOTA_NO_CHANGE)) { e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; - q->quota = e->quota; } ret = true; } else { @@ -212,10 +305,17 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; ret = !ret; } else { + /* We are transitioning, log that fact. */ + if (e->quota) { + quota2_log(par->hooknum, + skb, + par->in, + par->out, + q->name); + } /* we do not allow even small packets from now on */ e->quota = 0; } - q->quota = e->quota; } spin_unlock_bh(&e->lock); return ret; @@ -228,7 +328,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { .family = NFPROTO_IPV4, .checkentry = quota_mt2_check, .match = quota_mt2, - .destroy = quota_mt2_destroy, + .destroy = quota_mt2_destroy, .matchsize = sizeof(struct xt_quota_mtinfo2), .me = THIS_MODULE, }, @@ -238,7 +338,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { .family = NFPROTO_IPV6, .checkentry = quota_mt2_check, .match = quota_mt2, - .destroy = quota_mt2_destroy, + .destroy = quota_mt2_destroy, .matchsize = sizeof(struct xt_quota_mtinfo2), .me = THIS_MODULE, }, @@ -247,21 +347,29 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { static int __init quota_mt2_init(void) { int ret; + pr_debug("xt_quota2: init()"); - proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG + nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, NULL); + if (!nflognl) + return -ENOMEM; +#endif + + proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net); if (proc_xt_quota == NULL) return -EACCES; ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); if (ret < 0) - remove_proc_entry("xt_quota", init_net__proc_net); + remove_proc_entry("xt_quota", init_net.proc_net); + pr_debug("xt_quota2: init() %d", ret); return ret; } static void __exit quota_mt2_exit(void) { xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); - remove_proc_entry("xt_quota", init_net__proc_net); + remove_proc_entry("xt_quota", init_net.proc_net); } module_init(quota_mt2_init); From afa678641f078d95a73192ba61a7bdc91aa7e3a5 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 29 Sep 2011 15:36:49 -0700 Subject: [PATCH 093/358] netfilter: ipv6: fix crash caused by ipv6_find_hdr() When calling: ipv6_find_hdr(skb, &thoff, -1, NULL) on a fragmented packet, thoff would be left with a random value causing callers to read random memory offsets with: skb_header_pointer(skb, thoff, ...) Now we force ipv6_find_hdr() to return a failure in this case. Calling: ipv6_find_hdr(skb, &thoff, -1, &fragoff) will set fragoff as expected, and not return a failure. Change-Id: Ib474e8a4267dd2b300feca325811330329684a88 Signed-off-by: JP Abgrall --- net/ipv6/exthdrs_core.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index 8af3eb57f438..958a187a3cc4 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c @@ -166,15 +166,15 @@ EXPORT_SYMBOL_GPL(ipv6_find_tlv); * to explore inner IPv6 header, eg. ICMPv6 error messages. * * If target header is found, its offset is set in *offset and return protocol - * number. Otherwise, return -1. + * number. Otherwise, return -ENOENT or -EBADMSG. * * If the first fragment doesn't contain the final protocol header or * NEXTHDR_NONE it is considered invalid. * * Note that non-1st fragment is special case that "the protocol number * of last header" is "next header" field in Fragment header. In this case, - * *offset is meaningless and fragment offset is stored in *fragoff if fragoff - * isn't NULL. + * *offset is meaningless. If fragoff is not NULL, the fragment offset is + * stored in *fragoff; if it is NULL, return -EINVAL. * * if flags is not NULL and it's a fragment, then the frag flag * IP6_FH_F_FRAG will be set. If it's an AH header, the @@ -253,9 +253,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, if (target < 0 && ((!ipv6_ext_hdr(hp->nexthdr)) || hp->nexthdr == NEXTHDR_NONE)) { - if (fragoff) + if (fragoff) { *fragoff = _frag_off; - return hp->nexthdr; + return hp->nexthdr; + } else { + return -EINVAL; + } } return -ENOENT; } From 40ad8e5a3c56321451476906d4a437076a189294 Mon Sep 17 00:00:00 2001 From: Ashish Sharma Date: Fri, 7 Oct 2011 17:54:16 -0700 Subject: [PATCH 094/358] bridge: Have tx_bytes count headers like rx_bytes. Since rx_bytes accounting does not include Ethernet Headers in br_input.c, excluding ETH_HLEN on the transmit path for consistent measurement of packet length on both the Tx and Rx chains. The clean way would be for Rx to include the eth header, but the skb len has already been adjusted by the time the br code sees the skb. This is only a temporary workaround until we can completely ignore or cleanly fix the skb->len handling. Change-Id: I910de95a4686b2119da7f1f326e2154ef31f9972 Signed-off-by: Ashish Sharma --- net/bridge/br_device.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 63f0455c0bc3..e668ef8f50b4 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -44,11 +44,6 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) } #endif - u64_stats_update_begin(&brstats->syncp); - brstats->tx_packets++; - brstats->tx_bytes += skb->len; - u64_stats_update_end(&brstats->syncp); - if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid)) goto out; @@ -57,6 +52,12 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); + u64_stats_update_begin(&brstats->syncp); + brstats->tx_packets++; + /* Exclude ETH_HLEN from byte stats for consistency with Rx chain */ + brstats->tx_bytes += skb->len; + u64_stats_update_end(&brstats->syncp); + if (is_broadcast_ether_addr(dest)) br_flood_deliver(br, skb, false); else if (is_multicast_ether_addr(dest)) { From c001b9448b3f5dc0fcec1aaa545524057afcaa1e Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 15 Jul 2011 15:32:57 -0700 Subject: [PATCH 095/358] net: Only NET_ADMIN is allowed to fully control TUN interfaces. Signed-off-by: Chia-chi Yeh --- drivers/net/tun.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 26f8635b027d..ce399514e0b1 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1889,6 +1889,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, unsigned int ifindex; int ret; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK + if (cmd != TUNGETIFF && !capable(CAP_NET_ADMIN)) { + return -EPERM; + } +#endif + if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == 0x89) { if (copy_from_user(&ifr, argp, ifreq_len)) return -EFAULT; From a3d5f34b620b583d47fd012dc76ba40575f480cd Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Sun, 24 Jul 2011 14:31:14 -0700 Subject: [PATCH 096/358] hid-multitouch: Filter collections by application usage. This change fixes two problems. First, it ensures that the hid-multitouch driver does not incorrectly map GenericDesktop usages that are intended for other applications, such as a Mouse. Second, it sets the appropriate input properties so that user-space can distinguish TouchScreen devices (INPUT_PROP_DIRECT) from TouchPad devices (INPUT_PROP_POINTER) and configure them accordingly. Change-Id: I8c2d947929186ffe7cf04b37c76e29b9abecf8cb Signed-off-by: jeffbrown@android.com --- drivers/hid/hid-multitouch.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 221d503f1c24..69eae679780b 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -418,6 +418,16 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) td->mt_flags |= INPUT_MT_POINTER; + /* Only map fields from TouchScreen or TouchPad collections. + * We need to ignore fields that belong to other collections + * such as Mouse that might have the same GenericDesktop usages. */ + if (field->application == HID_DG_TOUCHSCREEN) + set_bit(INPUT_PROP_DIRECT, hi->input->propbit); + else if (field->application == HID_DG_TOUCHPAD) + set_bit(INPUT_PROP_POINTER, hi->input->propbit); + else + return 0; + if (usage->usage_index) prev_usage = &field->usage[usage->usage_index - 1]; From f65c462320a39645c83b8aec2e08bfc34bb0fbe9 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 24 Aug 2011 15:01:30 -0700 Subject: [PATCH 097/358] fuse: Freeze client on suspend when request sent to userspace Suspend attempts can abort when the FUSE daemon is already frozen and a client is waiting uninterruptibly for a response, causing freezing of tasks to fail. Use the freeze-friendly wait API, but disregard other signals. Change-Id: Icefb7e4bbc718ccb76bf3c04daaa5eeea7e0e63c Signed-off-by: Todd Poynor --- fs/fuse/dev.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 0a648bb455ae..bea90c6b2beb 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -20,6 +20,7 @@ #include #include #include +#include MODULE_ALIAS_MISCDEV(FUSE_MINOR); MODULE_ALIAS("devname:fuse"); @@ -464,7 +465,10 @@ __acquires(fc->lock) * Wait it out. */ spin_unlock(&fc->lock); - wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); + + while (req->state != FUSE_REQ_FINISHED) + wait_event_freezable(req->waitq, + req->state == FUSE_REQ_FINISHED); spin_lock(&fc->lock); if (!req->aborted) From a585d41f79f13a4c81c98e16480a56af9068bb69 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 20 Sep 2011 16:40:43 -0700 Subject: [PATCH 098/358] HID: Add input_register callback. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add input_register callback which gets called after hid_configure_usage is called for all the reports and before the input device is registered. This allows individual drivers to do extra work like input mapping just before device registration. Based on discussions with David Herrmann Change-Id: Idab6fb4f7b1e5e569bd0410967288717e9d34c98 Signed-off-by: Jaikumar Ganesh Changed to add return code to input_configured instead of adding input_register Signed-off-by: Arve Hjønnevåg --- drivers/hid/hid-input.c | 10 ++++++---- drivers/hid/hid-multitouch.c | 14 ++++++++++---- include/linux/hid.h | 4 ++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index a713e6211419..811947d73703 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1410,8 +1410,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) * UGCI) cram a lot of unrelated inputs into the * same interface. */ hidinput->report = report; - if (drv->input_configured) - drv->input_configured(hid, hidinput); + if (drv->input_configured && + drv->input_configured(hid, hidinput)) + goto out_cleanup; if (input_register_device(hidinput->input)) goto out_cleanup; hidinput = NULL; @@ -1432,8 +1433,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) } if (hidinput) { - if (drv->input_configured) - drv->input_configured(hid, hidinput); + if (drv->input_configured && + drv->input_configured(hid, hidinput)) + goto out_cleanup; if (input_register_device(hidinput->input)) goto out_cleanup; } diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 69eae679780b..37aa2f9d711b 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -757,12 +757,13 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) mt_sync_frame(td, report->field[0]->hidinput->input); } -static void mt_touch_input_configured(struct hid_device *hdev, +static int mt_touch_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); struct mt_class *cls = &td->mtclass; struct input_dev *input = hi->input; + int ret; if (!td->maxcontacts) td->maxcontacts = MT_DEFAULT_MAXCONTACT; @@ -777,9 +778,12 @@ static void mt_touch_input_configured(struct hid_device *hdev, if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) td->mt_flags |= INPUT_MT_DROP_UNUSED; - input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + ret = input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + if (ret) + return ret; td->mt_flags = 0; + return 0; } static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -912,14 +916,15 @@ static void mt_post_parse(struct mt_device *td) cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } -static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) +static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); char *name; const char *suffix = NULL; + int ret = 0; if (hi->report->id == td->mt_report_id) - mt_touch_input_configured(hdev, hi); + ret = mt_touch_input_configured(hdev, hi); if (hi->report->field[0]->physical == HID_DG_STYLUS) { suffix = "Pen"; @@ -935,6 +940,7 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) hi->input->name = name; } } + return ret; } static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) diff --git a/include/linux/hid.h b/include/linux/hid.h index 31b9d299ef6c..c6dadc733748 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -655,8 +655,8 @@ struct hid_driver { int (*input_mapped)(struct hid_device *hdev, struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max); - void (*input_configured)(struct hid_device *hdev, - struct hid_input *hidinput); + int (*input_configured)(struct hid_device *hdev, + struct hid_input *hidinput); void (*feature_mapping)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage); From bd803539154a22bbc1e1863d3b24dcbdc74e7fc4 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 26 Sep 2011 20:35:30 -0700 Subject: [PATCH 099/358] usb: otg: Temporarily grab wakelock on charger and disconnect events Change-Id: If995d4af4adcb08e8369009483f2956ad9627267 Signed-off-by: Todd Poynor --- drivers/usb/phy/otg-wakelock.c | 141 ++++++++++++++------------------- 1 file changed, 60 insertions(+), 81 deletions(-) diff --git a/drivers/usb/phy/otg-wakelock.c b/drivers/usb/phy/otg-wakelock.c index 993162674836..2f11472dd2b3 100644 --- a/drivers/usb/phy/otg-wakelock.c +++ b/drivers/usb/phy/otg-wakelock.c @@ -21,13 +21,15 @@ #include #include +#define TEMPORARY_HOLD_TIME 2000 + static bool enabled = true; static struct otg_transceiver *otgwl_xceiv; static struct notifier_block otgwl_nb; /* * otgwl_spinlock is held while the VBUS lock is grabbed or dropped and the - * locked field is updated to match. + * held field is updated to match. */ static DEFINE_SPINLOCK(otgwl_spinlock); @@ -39,51 +41,62 @@ static DEFINE_SPINLOCK(otgwl_spinlock); struct otgwl_lock { char name[40]; struct wake_lock wakelock; - bool locked; + bool held; }; /* - * VBUS present lock. + * VBUS present lock. Also used as a timed lock on charger + * connect/disconnect and USB host disconnect, to allow the system + * to react to the change in power. */ static struct otgwl_lock vbus_lock; -static void otgwl_grab(struct otgwl_lock *lock) +static void otgwl_hold(struct otgwl_lock *lock) { - if (!lock->locked) { + if (!lock->held) { wake_lock(&lock->wakelock); - lock->locked = true; + lock->held = true; } } +static void otgwl_temporary_hold(struct otgwl_lock *lock) +{ + wake_lock_timeout(&lock->wakelock, + msecs_to_jiffies(TEMPORARY_HOLD_TIME)); + lock->held = false; +} + static void otgwl_drop(struct otgwl_lock *lock) { - if (lock->locked) { + if (lock->held) { wake_unlock(&lock->wakelock); - lock->locked = false; + lock->held = false; } } -static int otgwl_otg_notifications(struct notifier_block *nb, - unsigned long event, void *unused) +static void otgwl_handle_event(unsigned long event) { unsigned long irqflags; - if (!enabled) - return NOTIFY_OK; - spin_lock_irqsave(&otgwl_spinlock, irqflags); + if (!enabled) { + otgwl_drop(&vbus_lock); + spin_unlock_irqrestore(&otgwl_spinlock, irqflags); + return; + } + switch (event) { case USB_EVENT_VBUS: case USB_EVENT_ENUMERATED: - otgwl_grab(&vbus_lock); + otgwl_hold(&vbus_lock); break; case USB_EVENT_NONE: case USB_EVENT_ID: case USB_EVENT_CHARGER: - otgwl_drop(&vbus_lock); + otgwl_temporary_hold(&vbus_lock); break; default: @@ -91,71 +104,25 @@ static int otgwl_otg_notifications(struct notifier_block *nb, } spin_unlock_irqrestore(&otgwl_spinlock, irqflags); +} + +static int otgwl_otg_notifications(struct notifier_block *nb, + unsigned long event, void *unused) +{ + otgwl_handle_event(event); return NOTIFY_OK; } -static void sync_with_xceiv_state(void) -{ - if ((otgwl_xceiv->last_event == USB_EVENT_VBUS) || - (otgwl_xceiv->last_event == USB_EVENT_ENUMERATED)) - otgwl_grab(&vbus_lock); - else - otgwl_drop(&vbus_lock); -} - -static int init_for_xceiv(void) -{ - int rv; - - if (!otgwl_xceiv) { - otgwl_xceiv = otg_get_transceiver(); - - if (!otgwl_xceiv) { - pr_err("%s: No OTG transceiver found\n", __func__); - return -ENODEV; - } - - snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s", - dev_name(otgwl_xceiv->dev)); - wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND, - vbus_lock.name); - - rv = otg_register_notifier(otgwl_xceiv, &otgwl_nb); - - if (rv) { - pr_err("%s: otg_register_notifier on transceiver %s" - " failed\n", __func__, - dev_name(otgwl_xceiv->dev)); - otgwl_xceiv = NULL; - wake_lock_destroy(&vbus_lock.wakelock); - return rv; - } - } - - return 0; -} - static int set_enabled(const char *val, const struct kernel_param *kp) { - unsigned long irqflags; int rv = param_set_bool(val, kp); if (rv) return rv; - rv = init_for_xceiv(); + if (otgwl_xceiv) + otgwl_handle_event(otgwl_xceiv->last_event); - if (rv) - return rv; - - spin_lock_irqsave(&otgwl_spinlock, irqflags); - - if (enabled) - sync_with_xceiv_state(); - else - otgwl_drop(&vbus_lock); - - spin_unlock_irqrestore(&otgwl_spinlock, irqflags); return 0; } @@ -169,22 +136,34 @@ MODULE_PARM_DESC(enabled, "enable wakelock when VBUS present"); static int __init otg_wakelock_init(void) { - unsigned long irqflags; + int ret; - otgwl_nb.notifier_call = otgwl_otg_notifications; + otgwl_xceiv = otg_get_transceiver(); - if (!init_for_xceiv()) { - spin_lock_irqsave(&otgwl_spinlock, irqflags); - - if (enabled) - sync_with_xceiv_state(); - - spin_unlock_irqrestore(&otgwl_spinlock, irqflags); - } else { - enabled = false; + if (!otgwl_xceiv) { + pr_err("%s: No OTG transceiver found\n", __func__); + return -ENODEV; } - return 0; + snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s", + dev_name(otgwl_xceiv->dev)); + wake_lock_init(&vbus_lock.wakelock, WAKE_LOCK_SUSPEND, + vbus_lock.name); + + otgwl_nb.notifier_call = otgwl_otg_notifications; + ret = otg_register_notifier(otgwl_xceiv, &otgwl_nb); + + if (ret) { + pr_err("%s: otg_register_notifier on transceiver %s" + " failed\n", __func__, + dev_name(otgwl_xceiv->dev)); + otgwl_xceiv = NULL; + wake_lock_destroy(&vbus_lock.wakelock); + return ret; + } + + otgwl_handle_event(otgwl_xceiv->last_event); + return ret; } late_initcall(otg_wakelock_init); From d1f6877b1c87dce0479a642557dfbad679514965 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 1 Feb 2012 14:23:15 -0800 Subject: [PATCH 100/358] usb: otg: otg-wakelock: fix build for 3.3 Add missing module.h include Change-Id: Ib0538ca569c9e0713ceefcd1f91c6bc089d2f2ba Signed-off-by: Colin Cross --- drivers/usb/phy/otg-wakelock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/phy/otg-wakelock.c b/drivers/usb/phy/otg-wakelock.c index 2f11472dd2b3..ffd8d8aa5dc8 100644 --- a/drivers/usb/phy/otg-wakelock.c +++ b/drivers/usb/phy/otg-wakelock.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include From 53c7b4cc024a9bf38fd617cbc324c7715a148333 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 12 Dec 2011 13:01:23 -0800 Subject: [PATCH 101/358] usb: gadget: Add Android Composite Gadget driver The Android Gadget driver is a composite driver that allows userspace to change at runtime the list of functions enabled in its configuration and to configure these functions. It supports multiple functions: acm, adb, rndis, mtp/ptp, mass storage and accessory. It is usually controlled by a daemon that changes the configuration based on user settings. For example, rndis is enabled when the user enables sharing the phone data connection and adb (Android Debug Bridge) is only enabled when the user wants to debug applications for security reasons. As an example on how to use it, the following shell commands will make the gadget disconnect from the host and make it be re-enumerated as a composite with 1 rndis and 2 acm interfaces, and a different product id: echo 0 > /sys/class/android_usb/android0/enable echo rndis,acm > /sys/class/android_usb/android0/functions echo 2 > /sys/class/android_usb/android0/f_acm/instances echo 2d01 > /sys/class/android_usb/android0/idProduct echo 1 > /sys/class/android_usb/android0/enable The driver requires a gadget controller that supports software control of the D+ pullup and the controller driver must support disabling the pullup during composite_bind. Signed-off-by: Mike Lockwood Signed-off-by: Benoit Goby --- drivers/usb/gadget/Kconfig | 9 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/android.c | 1076 ++++++++++++++++++++++++++++++++++ 3 files changed, 1087 insertions(+) create mode 100644 drivers/usb/gadget/android.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 8154165aa601..b7f516f68716 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -1019,6 +1019,15 @@ config USB_G_PRINTER For more information, see Documentation/usb/gadget_printer.txt which includes sample code for accessing the device file. +config USB_G_ANDROID + boolean "Android Composite Gadget" + help + The Android Composite Gadget supports multiple USB + functions: adb, acm, mass storage, mtp, accessory + and rndis. + Each function can be configured and enabled/disabled + dynamically from userspace through a sysfs interface. + if TTY config USB_CDC_COMPOSITE diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5f150bc1b4bc..98af79e679e8 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -84,6 +84,7 @@ g_nokia-y := nokia.o g_webcam-y := webcam.o g_ncm-y := ncm.o g_acm_ms-y := acm_ms.o +g_android-y := android.o g_tcm_usb_gadget-y := tcm_usb_gadget.o obj-$(CONFIG_USB_ZERO) += g_zero.o @@ -103,4 +104,5 @@ obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o obj-$(CONFIG_USB_G_NCM) += g_ncm.o obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o +obj-$(CONFIG_USB_G_ANDROID) += g_android.o obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c new file mode 100644 index 000000000000..6d67d6d80579 --- /dev/null +++ b/drivers/usb/gadget/android.c @@ -0,0 +1,1076 @@ +/* + * Gadget Driver for Android + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * Benoit Goby + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gadget_chips.h" + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" +#include "composite.c" + +#include "f_mass_storage.c" +#include "u_serial.c" +#include "f_acm.c" +#define USB_ETH_RNDIS y +#include "f_rndis.c" +#include "rndis.c" +#include "u_ether.c" + +MODULE_AUTHOR("Mike Lockwood"); +MODULE_DESCRIPTION("Android Composite USB Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +static const char longname[] = "Gadget Android"; + +/* Default vendor and product IDs, overridden by userspace */ +#define VENDOR_ID 0x18D1 +#define PRODUCT_ID 0x0001 + +struct android_usb_function { + char *name; + void *config; + + struct device *dev; + char *dev_name; + struct device_attribute **attributes; + + /* for android_dev.enabled_functions */ + struct list_head enabled_list; + + /* Optional: initialization during gadget bind */ + int (*init)(struct android_usb_function *, struct usb_composite_dev *); + /* Optional: cleanup during gadget unbind */ + void (*cleanup)(struct android_usb_function *); + + int (*bind_config)(struct android_usb_function *, + struct usb_configuration *); + + /* Optional: called when the configuration is removed */ + void (*unbind_config)(struct android_usb_function *, + struct usb_configuration *); + /* Optional: handle ctrl requests before the device is configured */ + int (*ctrlrequest)(struct android_usb_function *, + struct usb_composite_dev *, + const struct usb_ctrlrequest *); +}; + +struct android_dev { + struct android_usb_function **functions; + struct list_head enabled_functions; + struct usb_composite_dev *cdev; + struct device *dev; + + bool enabled; + struct mutex mutex; + bool connected; + bool sw_connected; + struct work_struct work; +}; + +static struct class *android_class; +static struct android_dev *_android_dev; +static int android_bind_config(struct usb_configuration *c); +static void android_unbind_config(struct usb_configuration *c); + +/* string IDs are assigned dynamically */ +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 + +static char manufacturer_string[256]; +static char product_string[256]; +static char serial_string[256]; + +/* String Table */ +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer_string, + [STRING_PRODUCT_IDX].s = product_string, + [STRING_SERIAL_IDX].s = serial_string, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof(device_desc), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .idVendor = __constant_cpu_to_le16(VENDOR_ID), + .idProduct = __constant_cpu_to_le16(PRODUCT_ID), + .bcdDevice = __constant_cpu_to_le16(0xffff), + .bNumConfigurations = 1, +}; + +static struct usb_configuration android_config_driver = { + .label = "android", + .unbind = android_unbind_config, + .bConfigurationValue = 1, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 0xFA, /* 500ma */ +}; + +static void android_work(struct work_struct *data) +{ + struct android_dev *dev = container_of(data, struct android_dev, work); + struct usb_composite_dev *cdev = dev->cdev; + char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; + char *connected[2] = { "USB_STATE=CONNECTED", NULL }; + char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; + char **uevent_envp = NULL; + unsigned long flags; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + uevent_envp = configured; + else if (dev->connected != dev->sw_connected) + uevent_envp = dev->connected ? connected : disconnected; + dev->sw_connected = dev->connected; + spin_unlock_irqrestore(&cdev->lock, flags); + + if (uevent_envp) { + kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, uevent_envp); + pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]); + } else { + pr_info("%s: did not send uevent (%d %d %p)\n", __func__, + dev->connected, dev->sw_connected, cdev->config); + } +} + + +/*-------------------------------------------------------------------------*/ +/* Supported functions initialization */ + +#define MAX_ACM_INSTANCES 4 +struct acm_function_config { + int instances; +}; + +static int +acm_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + f->config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + + return gserial_setup(cdev->gadget, MAX_ACM_INSTANCES); +} + +static void acm_function_cleanup(struct android_usb_function *f) +{ + gserial_cleanup(); + kfree(f->config); + f->config = NULL; +} + +static int +acm_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + int i; + int ret = 0; + struct acm_function_config *config = f->config; + + for (i = 0; i < config->instances; i++) { + ret = acm_bind_config(c, i); + if (ret) { + pr_err("Could not bind acm%u config\n", i); + break; + } + } + + return ret; +} + +static ssize_t acm_instances_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct acm_function_config *config = f->config; + return sprintf(buf, "%d\n", config->instances); +} + +static ssize_t acm_instances_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct acm_function_config *config = f->config; + int value; + + sscanf(buf, "%d", &value); + if (value > MAX_ACM_INSTANCES) + value = MAX_ACM_INSTANCES; + config->instances = value; + return size; +} + +static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, acm_instances_show, + acm_instances_store); +static struct device_attribute *acm_function_attributes[] = { + &dev_attr_instances, + NULL +}; + +static struct android_usb_function acm_function = { + .name = "acm", + .init = acm_function_init, + .cleanup = acm_function_cleanup, + .bind_config = acm_function_bind_config, + .attributes = acm_function_attributes, +}; + + +struct rndis_function_config { + u8 ethaddr[ETH_ALEN]; + u32 vendorID; + char manufacturer[256]; + /* "Wireless" RNDIS; auto-detected by Windows */ + bool wceis; +}; + +static int +rndis_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + f->config = kzalloc(sizeof(struct rndis_function_config), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + return 0; +} + +static void rndis_function_cleanup(struct android_usb_function *f) +{ + kfree(f->config); + f->config = NULL; +} + +static int +rndis_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + int ret; + struct rndis_function_config *rndis = f->config; + + if (!rndis) { + pr_err("%s: rndis_pdata\n", __func__); + return -1; + } + + pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__, + rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], + rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); + + ret = gether_setup_name(c->cdev->gadget, rndis->ethaddr, "rndis"); + if (ret) { + pr_err("%s: gether_setup failed\n", __func__); + return ret; + } + + if (rndis->wceis) { + /* "Wireless" RNDIS; auto-detected by Windows */ + rndis_iad_descriptor.bFunctionClass = + USB_CLASS_WIRELESS_CONTROLLER; + rndis_iad_descriptor.bFunctionSubClass = 0x01; + rndis_iad_descriptor.bFunctionProtocol = 0x03; + rndis_control_intf.bInterfaceClass = + USB_CLASS_WIRELESS_CONTROLLER; + rndis_control_intf.bInterfaceSubClass = 0x01; + rndis_control_intf.bInterfaceProtocol = 0x03; + } + + return rndis_bind_config_vendor(c, rndis->ethaddr, rndis->vendorID, + rndis->manufacturer); +} + +static void rndis_function_unbind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + gether_cleanup(); +} + +static ssize_t rndis_manufacturer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + return sprintf(buf, "%s\n", config->manufacturer); +} + +static ssize_t rndis_manufacturer_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + + if (size >= sizeof(config->manufacturer)) + return -EINVAL; + if (sscanf(buf, "%s", config->manufacturer) == 1) + return size; + return -1; +} + +static DEVICE_ATTR(manufacturer, S_IRUGO | S_IWUSR, rndis_manufacturer_show, + rndis_manufacturer_store); + +static ssize_t rndis_wceis_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + return sprintf(buf, "%d\n", config->wceis); +} + +static ssize_t rndis_wceis_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + int value; + + if (sscanf(buf, "%d", &value) == 1) { + config->wceis = value; + return size; + } + return -EINVAL; +} + +static DEVICE_ATTR(wceis, S_IRUGO | S_IWUSR, rndis_wceis_show, + rndis_wceis_store); + +static ssize_t rndis_ethaddr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *rndis = f->config; + return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", + rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], + rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); +} + +static ssize_t rndis_ethaddr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *rndis = f->config; + + if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", + (int *)&rndis->ethaddr[0], (int *)&rndis->ethaddr[1], + (int *)&rndis->ethaddr[2], (int *)&rndis->ethaddr[3], + (int *)&rndis->ethaddr[4], (int *)&rndis->ethaddr[5]) == 6) + return size; + return -EINVAL; +} + +static DEVICE_ATTR(ethaddr, S_IRUGO | S_IWUSR, rndis_ethaddr_show, + rndis_ethaddr_store); + +static ssize_t rndis_vendorID_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + return sprintf(buf, "%04x\n", config->vendorID); +} + +static ssize_t rndis_vendorID_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct rndis_function_config *config = f->config; + int value; + + if (sscanf(buf, "%04x", &value) == 1) { + config->vendorID = value; + return size; + } + return -EINVAL; +} + +static DEVICE_ATTR(vendorID, S_IRUGO | S_IWUSR, rndis_vendorID_show, + rndis_vendorID_store); + +static struct device_attribute *rndis_function_attributes[] = { + &dev_attr_manufacturer, + &dev_attr_wceis, + &dev_attr_ethaddr, + &dev_attr_vendorID, + NULL +}; + +static struct android_usb_function rndis_function = { + .name = "rndis", + .init = rndis_function_init, + .cleanup = rndis_function_cleanup, + .bind_config = rndis_function_bind_config, + .unbind_config = rndis_function_unbind_config, + .attributes = rndis_function_attributes, +}; + + +struct mass_storage_function_config { + struct fsg_config fsg; + struct fsg_common *common; +}; + +static int mass_storage_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + struct mass_storage_function_config *config; + struct fsg_common *common; + int err; + + config = kzalloc(sizeof(struct mass_storage_function_config), + GFP_KERNEL); + if (!config) + return -ENOMEM; + + config->fsg.nluns = 1; + config->fsg.luns[0].removable = 1; + + common = fsg_common_init(NULL, cdev, &config->fsg); + if (IS_ERR(common)) { + kfree(config); + return PTR_ERR(common); + } + + err = sysfs_create_link(&f->dev->kobj, + &common->luns[0].dev.kobj, + "lun"); + if (err) { + kfree(config); + return err; + } + + config->common = common; + f->config = config; + return 0; +} + +static void mass_storage_function_cleanup(struct android_usb_function *f) +{ + kfree(f->config); + f->config = NULL; +} + +static int mass_storage_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct mass_storage_function_config *config = f->config; + return fsg_bind_config(c->cdev, c, config->common); +} + +static ssize_t mass_storage_inquiry_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct mass_storage_function_config *config = f->config; + return sprintf(buf, "%s\n", config->common->inquiry_string); +} + +static ssize_t mass_storage_inquiry_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct mass_storage_function_config *config = f->config; + if (size >= sizeof(config->common->inquiry_string)) + return -EINVAL; + if (sscanf(buf, "%s", config->common->inquiry_string) != 1) + return -EINVAL; + return size; +} + +static DEVICE_ATTR(inquiry_string, S_IRUGO | S_IWUSR, + mass_storage_inquiry_show, + mass_storage_inquiry_store); + +static struct device_attribute *mass_storage_function_attributes[] = { + &dev_attr_inquiry_string, + NULL +}; + +static struct android_usb_function mass_storage_function = { + .name = "mass_storage", + .init = mass_storage_function_init, + .cleanup = mass_storage_function_cleanup, + .bind_config = mass_storage_function_bind_config, + .attributes = mass_storage_function_attributes, +}; + + +static struct android_usb_function *supported_functions[] = { + &acm_function, + &rndis_function, + &mass_storage_function, + NULL +}; + + +static int android_init_functions(struct android_usb_function **functions, + struct usb_composite_dev *cdev) +{ + struct android_dev *dev = _android_dev; + struct android_usb_function *f; + struct device_attribute **attrs; + struct device_attribute *attr; + int err; + int index = 0; + + for (; (f = *functions++); index++) { + f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name); + f->dev = device_create(android_class, dev->dev, + MKDEV(0, index), f, f->dev_name); + if (IS_ERR(f->dev)) { + pr_err("%s: Failed to create dev %s", __func__, + f->dev_name); + err = PTR_ERR(f->dev); + goto err_create; + } + + if (f->init) { + err = f->init(f, cdev); + if (err) { + pr_err("%s: Failed to init %s", __func__, + f->name); + goto err_out; + } + } + + attrs = f->attributes; + if (attrs) { + while ((attr = *attrs++) && !err) + err = device_create_file(f->dev, attr); + } + if (err) { + pr_err("%s: Failed to create function %s attributes", + __func__, f->name); + goto err_out; + } + } + return 0; + +err_out: + device_destroy(android_class, f->dev->devt); +err_create: + kfree(f->dev_name); + return err; +} + +static void android_cleanup_functions(struct android_usb_function **functions) +{ + struct android_usb_function *f; + + while (*functions) { + f = *functions++; + + if (f->dev) { + device_destroy(android_class, f->dev->devt); + kfree(f->dev_name); + } + + if (f->cleanup) + f->cleanup(f); + } +} + +static int +android_bind_enabled_functions(struct android_dev *dev, + struct usb_configuration *c) +{ + struct android_usb_function *f; + int ret; + + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + ret = f->bind_config(f, c); + if (ret) { + pr_err("%s: %s failed", __func__, f->name); + return ret; + } + } + return 0; +} + +static void +android_unbind_enabled_functions(struct android_dev *dev, + struct usb_configuration *c) +{ + struct android_usb_function *f; + + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->unbind_config) + f->unbind_config(f, c); + } +} + +static int android_enable_function(struct android_dev *dev, char *name) +{ + struct android_usb_function **functions = dev->functions; + struct android_usb_function *f; + while ((f = *functions++)) { + if (!strcmp(name, f->name)) { + list_add_tail(&f->enabled_list, + &dev->enabled_functions); + return 0; + } + } + return -EINVAL; +} + +/*-------------------------------------------------------------------------*/ +/* /sys/class/android_usb/android%d/ interface */ + +static ssize_t +functions_show(struct device *pdev, struct device_attribute *attr, char *buf) +{ + struct android_dev *dev = dev_get_drvdata(pdev); + struct android_usb_function *f; + char *buff = buf; + + mutex_lock(&dev->mutex); + + list_for_each_entry(f, &dev->enabled_functions, enabled_list) + buff += sprintf(buff, "%s,", f->name); + + mutex_unlock(&dev->mutex); + + if (buff != buf) + *(buff-1) = '\n'; + return buff - buf; +} + +static ssize_t +functions_store(struct device *pdev, struct device_attribute *attr, + const char *buff, size_t size) +{ + struct android_dev *dev = dev_get_drvdata(pdev); + char *name; + char buf[256], *b; + int err; + + mutex_lock(&dev->mutex); + + if (dev->enabled) { + mutex_unlock(&dev->mutex); + return -EBUSY; + } + + INIT_LIST_HEAD(&dev->enabled_functions); + + strncpy(buf, buff, sizeof(buf)); + b = strim(buf); + + while (b) { + name = strsep(&b, ","); + if (name) { + err = android_enable_function(dev, name); + if (err) + pr_err("android_usb: Cannot enable '%s'", name); + } + } + + mutex_unlock(&dev->mutex); + + return size; +} + +static ssize_t enable_show(struct device *pdev, struct device_attribute *attr, + char *buf) +{ + struct android_dev *dev = dev_get_drvdata(pdev); + return sprintf(buf, "%d\n", dev->enabled); +} + +static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, + const char *buff, size_t size) +{ + struct android_dev *dev = dev_get_drvdata(pdev); + struct usb_composite_dev *cdev = dev->cdev; + int enabled = 0; + + mutex_lock(&dev->mutex); + + sscanf(buff, "%d", &enabled); + if (enabled && !dev->enabled) { + cdev->next_string_id = 0; + /* + * Update values in composite driver's copy of + * device descriptor. + */ + cdev->desc.idVendor = device_desc.idVendor; + cdev->desc.idProduct = device_desc.idProduct; + cdev->desc.bcdDevice = device_desc.bcdDevice; + cdev->desc.bDeviceClass = device_desc.bDeviceClass; + cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass; + cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol; + + usb_add_config(cdev, &android_config_driver, + android_bind_config); + usb_gadget_connect(cdev->gadget); + dev->enabled = true; + } else if (!enabled && dev->enabled) { + usb_gadget_disconnect(cdev->gadget); + /* Cancel pending control requests */ + usb_ep_dequeue(cdev->gadget->ep0, cdev->req); + usb_remove_config(cdev, &android_config_driver); + dev->enabled = false; + } else { + pr_err("android_usb: already %s\n", + dev->enabled ? "enabled" : "disabled"); + } + + mutex_unlock(&dev->mutex); + return size; +} + +static ssize_t state_show(struct device *pdev, struct device_attribute *attr, + char *buf) +{ + struct android_dev *dev = dev_get_drvdata(pdev); + struct usb_composite_dev *cdev = dev->cdev; + char *state = "DISCONNECTED"; + unsigned long flags; + + if (!cdev) + goto out; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + state = "CONFIGURED"; + else if (dev->connected) + state = "CONNECTED"; + spin_unlock_irqrestore(&cdev->lock, flags); +out: + return sprintf(buf, "%s\n", state); +} + +#define DESCRIPTOR_ATTR(field, format_string) \ +static ssize_t \ +field ## _show(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return sprintf(buf, format_string, device_desc.field); \ +} \ +static ssize_t \ +field ## _store(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + int value; \ + if (sscanf(buf, format_string, &value) == 1) { \ + device_desc.field = value; \ + return size; \ + } \ + return -1; \ +} \ +static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); + +#define DESCRIPTOR_STRING_ATTR(field, buffer) \ +static ssize_t \ +field ## _show(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return sprintf(buf, "%s", buffer); \ +} \ +static ssize_t \ +field ## _store(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + if (size >= sizeof(buffer)) \ + return -EINVAL; \ + if (sscanf(buf, "%s", buffer) == 1) { \ + return size; \ + } \ + return -1; \ +} \ +static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); + + +DESCRIPTOR_ATTR(idVendor, "%04x\n") +DESCRIPTOR_ATTR(idProduct, "%04x\n") +DESCRIPTOR_ATTR(bcdDevice, "%04x\n") +DESCRIPTOR_ATTR(bDeviceClass, "%d\n") +DESCRIPTOR_ATTR(bDeviceSubClass, "%d\n") +DESCRIPTOR_ATTR(bDeviceProtocol, "%d\n") +DESCRIPTOR_STRING_ATTR(iManufacturer, manufacturer_string) +DESCRIPTOR_STRING_ATTR(iProduct, product_string) +DESCRIPTOR_STRING_ATTR(iSerial, serial_string) + +static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show, + functions_store); +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); +static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); + +static struct device_attribute *android_usb_attributes[] = { + &dev_attr_idVendor, + &dev_attr_idProduct, + &dev_attr_bcdDevice, + &dev_attr_bDeviceClass, + &dev_attr_bDeviceSubClass, + &dev_attr_bDeviceProtocol, + &dev_attr_iManufacturer, + &dev_attr_iProduct, + &dev_attr_iSerial, + &dev_attr_functions, + &dev_attr_enable, + &dev_attr_state, + NULL +}; + +/*-------------------------------------------------------------------------*/ +/* Composite driver */ + +static int android_bind_config(struct usb_configuration *c) +{ + struct android_dev *dev = _android_dev; + int ret = 0; + + ret = android_bind_enabled_functions(dev, c); + if (ret) + return ret; + + return 0; +} + +static void android_unbind_config(struct usb_configuration *c) +{ + struct android_dev *dev = _android_dev; + + android_unbind_enabled_functions(dev, c); +} + +static int android_bind(struct usb_composite_dev *cdev) +{ + struct android_dev *dev = _android_dev; + struct usb_gadget *gadget = cdev->gadget; + int gcnum, id, ret; + + /* + * Start disconnected. Userspace will connect the gadget once + * it is done configuring the functions. + */ + usb_gadget_disconnect(gadget); + + ret = android_init_functions(dev->functions, cdev); + if (ret) + return ret; + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_MANUFACTURER_IDX].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_PRODUCT_IDX].id = id; + device_desc.iProduct = id; + + /* Default strings - should be updated by userspace */ + strncpy(manufacturer_string, "Android", sizeof(manufacturer_string)-1); + strncpy(product_string, "Android", sizeof(product_string) - 1); + strncpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1); + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_SERIAL_IDX].id = id; + device_desc.iSerialNumber = id; + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); + else { + pr_warning("%s: controller '%s' not recognized\n", + longname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + usb_gadget_set_selfpowered(gadget); + dev->cdev = cdev; + + return 0; +} + +static int android_usb_unbind(struct usb_composite_dev *cdev) +{ + struct android_dev *dev = _android_dev; + + cancel_work_sync(&dev->work); + android_cleanup_functions(dev->functions); + return 0; +} + +static struct usb_composite_driver android_usb_driver = { + .name = "android_usb", + .dev = &device_desc, + .strings = dev_strings, + .unbind = android_usb_unbind, + .max_speed = USB_SPEED_HIGH, +}; + +static int +android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) +{ + struct android_dev *dev = _android_dev; + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + struct android_usb_function *f; + int value = -EOPNOTSUPP; + unsigned long flags; + + req->zero = 0; + req->complete = composite_setup_complete; + req->length = 0; + gadget->ep0->driver_data = cdev; + + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->ctrlrequest) { + value = f->ctrlrequest(f, cdev, c); + if (value >= 0) + break; + } + } + + if (value < 0) + value = composite_setup(gadget, c); + + spin_lock_irqsave(&cdev->lock, flags); + if (!dev->connected) { + dev->connected = 1; + schedule_work(&dev->work); + } else if (c->bRequest == USB_REQ_SET_CONFIGURATION && + cdev->config) { + schedule_work(&dev->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + + return value; +} + +static void android_disconnect(struct usb_gadget *gadget) +{ + struct android_dev *dev = _android_dev; + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + + composite_disconnect(gadget); + + spin_lock_irqsave(&cdev->lock, flags); + dev->connected = 0; + schedule_work(&dev->work); + spin_unlock_irqrestore(&cdev->lock, flags); +} + +static int android_create_device(struct android_dev *dev) +{ + struct device_attribute **attrs = android_usb_attributes; + struct device_attribute *attr; + int err; + + dev->dev = device_create(android_class, NULL, + MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(dev->dev)) + return PTR_ERR(dev->dev); + + dev_set_drvdata(dev->dev, dev); + + while ((attr = *attrs++)) { + err = device_create_file(dev->dev, attr); + if (err) { + device_destroy(android_class, dev->dev->devt); + return err; + } + } + return 0; +} + + +static int __init init(void) +{ + struct android_dev *dev; + int err; + + android_class = class_create(THIS_MODULE, "android_usb"); + if (IS_ERR(android_class)) + return PTR_ERR(android_class); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->functions = supported_functions; + INIT_LIST_HEAD(&dev->enabled_functions); + INIT_WORK(&dev->work, android_work); + mutex_init(&dev->mutex); + + err = android_create_device(dev); + if (err) { + class_destroy(android_class); + kfree(dev); + return err; + } + + _android_dev = dev; + + /* Override composite driver functions */ + composite_driver.setup = android_setup; + composite_driver.disconnect = android_disconnect; + + return usb_composite_probe(&android_usb_driver, android_bind); +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&android_usb_driver); + class_destroy(android_class); + kfree(_android_dev); + _android_dev = NULL; +} +module_exit(cleanup); From 7e37a164a2b61867820928cd350ee4653c86edce Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 19 Dec 2011 14:37:50 -0800 Subject: [PATCH 102/358] usb: gadget: mtp: Add MTP/PTP function USB gadget function driver used by the Android framework to implement the MTP and PTP protocols. It creates a character device that provides an interface for fast transfer of files and supports transferring files greater than 4GB. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/android.c | 66 ++ drivers/usb/gadget/f_mtp.c | 1283 ++++++++++++++++++++++++++++++++++ include/linux/usb/f_mtp.h | 75 ++ 3 files changed, 1424 insertions(+) create mode 100644 drivers/usb/gadget/f_mtp.c create mode 100644 include/linux/usb/f_mtp.h diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 6d67d6d80579..fe688442337d 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -45,6 +45,7 @@ #include "f_mass_storage.c" #include "u_serial.c" #include "f_acm.c" +#include "f_mtp.c" #define USB_ETH_RNDIS y #include "f_rndis.c" #include "rndis.c" @@ -264,6 +265,69 @@ static struct android_usb_function acm_function = { }; +static int +mtp_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + return mtp_setup(); +} + +static void mtp_function_cleanup(struct android_usb_function *f) +{ + mtp_cleanup(); +} + +static int +mtp_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + return mtp_bind_config(c, false); +} + +static int +ptp_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + /* nothing to do - initialization is handled by mtp_function_init */ + return 0; +} + +static void ptp_function_cleanup(struct android_usb_function *f) +{ + /* nothing to do - cleanup is handled by mtp_function_cleanup */ +} + +static int +ptp_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + return mtp_bind_config(c, true); +} + +static int mtp_function_ctrlrequest(struct android_usb_function *f, + struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *c) +{ + return mtp_ctrlrequest(cdev, c); +} + +static struct android_usb_function mtp_function = { + .name = "mtp", + .init = mtp_function_init, + .cleanup = mtp_function_cleanup, + .bind_config = mtp_function_bind_config, + .ctrlrequest = mtp_function_ctrlrequest, +}; + +/* PTP function is same as MTP with slightly different interface descriptor */ +static struct android_usb_function ptp_function = { + .name = "ptp", + .init = ptp_function_init, + .cleanup = ptp_function_cleanup, + .bind_config = ptp_function_bind_config, +}; + + struct rndis_function_config { u8 ethaddr[ETH_ALEN]; u32 vendorID; @@ -543,6 +607,8 @@ static struct android_usb_function mass_storage_function = { static struct android_usb_function *supported_functions[] = { &acm_function, + &mtp_function, + &ptp_function, &rndis_function, &mass_storage_function, NULL diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c new file mode 100644 index 000000000000..1638977a5410 --- /dev/null +++ b/drivers/usb/gadget/f_mtp.c @@ -0,0 +1,1283 @@ +/* + * Gadget Function Driver for MTP + * + * Copyright (C) 2010 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MTP_BULK_BUFFER_SIZE 16384 +#define INTR_BUFFER_SIZE 28 + +/* String IDs */ +#define INTERFACE_STRING_INDEX 0 + +/* values for mtp_dev.state */ +#define STATE_OFFLINE 0 /* initial state, disconnected */ +#define STATE_READY 1 /* ready for userspace calls */ +#define STATE_BUSY 2 /* processing userspace calls */ +#define STATE_CANCELED 3 /* transaction canceled by host */ +#define STATE_ERROR 4 /* error from completion routine */ + +/* number of tx and rx requests to allocate */ +#define TX_REQ_MAX 4 +#define RX_REQ_MAX 2 +#define INTR_REQ_MAX 5 + +/* ID for Microsoft MTP OS String */ +#define MTP_OS_STRING_ID 0xEE + +/* MTP class reqeusts */ +#define MTP_REQ_CANCEL 0x64 +#define MTP_REQ_GET_EXT_EVENT_DATA 0x65 +#define MTP_REQ_RESET 0x66 +#define MTP_REQ_GET_DEVICE_STATUS 0x67 + +/* constants for device status */ +#define MTP_RESPONSE_OK 0x2001 +#define MTP_RESPONSE_DEVICE_BUSY 0x2019 + +static const char mtp_shortname[] = "mtp_usb"; + +struct mtp_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + struct usb_ep *ep_intr; + + int state; + + /* synchronize access to our device file */ + atomic_t open_excl; + /* to enforce only one ioctl at a time */ + atomic_t ioctl_excl; + + struct list_head tx_idle; + struct list_head intr_idle; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + wait_queue_head_t intr_wq; + struct usb_request *rx_req[RX_REQ_MAX]; + int rx_done; + + /* for processing MTP_SEND_FILE, MTP_RECEIVE_FILE and + * MTP_SEND_FILE_WITH_HEADER ioctls on a work queue + */ + struct workqueue_struct *wq; + struct work_struct send_file_work; + struct work_struct receive_file_work; + struct file *xfer_file; + loff_t xfer_file_offset; + int64_t xfer_file_length; + unsigned xfer_send_header; + uint16_t xfer_command; + uint32_t xfer_transaction_id; + int xfer_result; +}; + +static struct usb_interface_descriptor mtp_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, +}; + +static struct usb_interface_descriptor ptp_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_STILL_IMAGE, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1, +}; + +static struct usb_endpoint_descriptor mtp_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor mtp_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor mtp_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor mtp_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor mtp_intr_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(INTR_BUFFER_SIZE), + .bInterval = 6, +}; + +static struct usb_descriptor_header *fs_mtp_descs[] = { + (struct usb_descriptor_header *) &mtp_interface_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_mtp_descs[] = { + (struct usb_descriptor_header *) &mtp_interface_desc, + (struct usb_descriptor_header *) &mtp_highspeed_in_desc, + (struct usb_descriptor_header *) &mtp_highspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_descriptor_header *fs_ptp_descs[] = { + (struct usb_descriptor_header *) &ptp_interface_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_in_desc, + (struct usb_descriptor_header *) &mtp_fullspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_ptp_descs[] = { + (struct usb_descriptor_header *) &ptp_interface_desc, + (struct usb_descriptor_header *) &mtp_highspeed_in_desc, + (struct usb_descriptor_header *) &mtp_highspeed_out_desc, + (struct usb_descriptor_header *) &mtp_intr_desc, + NULL, +}; + +static struct usb_string mtp_string_defs[] = { + /* Naming interface "MTP" so libmtp will recognize us */ + [INTERFACE_STRING_INDEX].s = "MTP", + { }, /* end of list */ +}; + +static struct usb_gadget_strings mtp_string_table = { + .language = 0x0409, /* en-US */ + .strings = mtp_string_defs, +}; + +static struct usb_gadget_strings *mtp_strings[] = { + &mtp_string_table, + NULL, +}; + +/* Microsoft MTP OS String */ +static u8 mtp_os_string[] = { + 18, /* sizeof(mtp_os_string) */ + USB_DT_STRING, + /* Signature field: "MSFT100" */ + 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, + /* vendor code */ + 1, + /* padding */ + 0 +}; + +/* Microsoft Extended Configuration Descriptor Header Section */ +struct mtp_ext_config_desc_header { + __le32 dwLength; + __u16 bcdVersion; + __le16 wIndex; + __u8 bCount; + __u8 reserved[7]; +}; + +/* Microsoft Extended Configuration Descriptor Function Section */ +struct mtp_ext_config_desc_function { + __u8 bFirstInterfaceNumber; + __u8 bInterfaceCount; + __u8 compatibleID[8]; + __u8 subCompatibleID[8]; + __u8 reserved[6]; +}; + +/* MTP Extended Configuration Descriptor */ +struct { + struct mtp_ext_config_desc_header header; + struct mtp_ext_config_desc_function function; +} mtp_ext_config_desc = { + .header = { + .dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)), + .bcdVersion = __constant_cpu_to_le16(0x0100), + .wIndex = __constant_cpu_to_le16(4), + .bCount = __constant_cpu_to_le16(1), + }, + .function = { + .bFirstInterfaceNumber = 0, + .bInterfaceCount = 1, + .compatibleID = { 'M', 'T', 'P' }, + }, +}; + +struct mtp_device_status { + __le16 wLength; + __le16 wCode; +}; + +/* temporary variable used between mtp_open() and mtp_gadget_bind() */ +static struct mtp_dev *_mtp_dev; + +static inline struct mtp_dev *func_to_mtp(struct usb_function *f) +{ + return container_of(f, struct mtp_dev, function); +} + +static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void mtp_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static inline int mtp_lock(atomic_t *excl) +{ + if (atomic_inc_return(excl) == 1) { + return 0; + } else { + atomic_dec(excl); + return -1; + } +} + +static inline void mtp_unlock(atomic_t *excl) +{ + atomic_dec(excl); +} + +/* add a request to the tail of a list */ +static void mtp_req_put(struct mtp_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +static struct usb_request +*mtp_req_get(struct mtp_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void mtp_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct mtp_dev *dev = _mtp_dev; + + if (req->status != 0) + dev->state = STATE_ERROR; + + mtp_req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct mtp_dev *dev = _mtp_dev; + + dev->rx_done = 1; + if (req->status != 0) + dev->state = STATE_ERROR; + + wake_up(&dev->read_wq); +} + +static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req) +{ + struct mtp_dev *dev = _mtp_dev; + + if (req->status != 0) + dev->state = STATE_ERROR; + + mtp_req_put(dev, &dev->intr_idle, req); + + wake_up(&dev->intr_wq); +} + +static int mtp_create_bulk_endpoints(struct mtp_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc, + struct usb_endpoint_descriptor *intr_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + ep = usb_ep_autoconfig(cdev->gadget, intr_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for mtp ep_intr got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_intr = ep; + + /* now allocate requests for our endpoints */ + for (i = 0; i < TX_REQ_MAX; i++) { + req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = mtp_complete_in; + mtp_req_put(dev, &dev->tx_idle, req); + } + for (i = 0; i < RX_REQ_MAX; i++) { + req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = mtp_complete_out; + dev->rx_req[i] = req; + } + for (i = 0; i < INTR_REQ_MAX; i++) { + req = mtp_request_new(dev->ep_intr, INTR_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = mtp_complete_intr; + mtp_req_put(dev, &dev->intr_idle, req); + } + + return 0; + +fail: + printk(KERN_ERR "mtp_bind() could not allocate requests\n"); + return -1; +} + +static ssize_t mtp_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct mtp_dev *dev = fp->private_data; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + int r = count, xfer; + int ret = 0; + + DBG(cdev, "mtp_read(%d)\n", count); + + if (count > MTP_BULK_BUFFER_SIZE) + return -EINVAL; + + /* we will block until we're online */ + DBG(cdev, "mtp_read: waiting for online state\n"); + ret = wait_event_interruptible(dev->read_wq, + dev->state != STATE_OFFLINE); + if (ret < 0) { + r = ret; + goto done; + } + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) { + /* report cancelation to userspace */ + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + return -ECANCELED; + } + dev->state = STATE_BUSY; + spin_unlock_irq(&dev->lock); + +requeue_req: + /* queue a request */ + req = dev->rx_req[0]; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + goto done; + } else { + DBG(cdev, "rx %p queue\n", req); + } + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + r = ret; + usb_ep_dequeue(dev->ep_out, req); + goto done; + } + if (dev->state == STATE_BUSY) { + /* If we got a 0-len packet, throw it back and try again. */ + if (req->actual == 0) + goto requeue_req; + + DBG(cdev, "rx %p %d\n", req, req->actual); + xfer = (req->actual < count) ? req->actual : count; + r = xfer; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + } else + r = -EIO; + +done: + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) + r = -ECANCELED; + else if (dev->state != STATE_OFFLINE) + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + + DBG(cdev, "mtp_read returning %d\n", r); + return r; +} + +static ssize_t mtp_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct mtp_dev *dev = fp->private_data; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = 0; + int r = count, xfer; + int sendZLP = 0; + int ret; + + DBG(cdev, "mtp_write(%d)\n", count); + + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) { + /* report cancelation to userspace */ + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + return -ECANCELED; + } + if (dev->state == STATE_OFFLINE) { + spin_unlock_irq(&dev->lock); + return -ENODEV; + } + dev->state = STATE_BUSY; + spin_unlock_irq(&dev->lock); + + /* we need to send a zero length packet to signal the end of transfer + * if the transfer size is aligned to a packet boundary. + */ + if ((count & (dev->ep_in->maxpacket - 1)) == 0) + sendZLP = 1; + + while (count > 0 || sendZLP) { + /* so we exit after sending ZLP */ + if (count == 0) + sendZLP = 0; + + if (dev->state != STATE_BUSY) { + DBG(cdev, "mtp_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + ((req = mtp_req_get(dev, &dev->tx_idle)) + || dev->state != STATE_BUSY)); + if (!req) { + r = ret; + break; + } + + if (count > MTP_BULK_BUFFER_SIZE) + xfer = MTP_BULK_BUFFER_SIZE; + else + xfer = count; + if (xfer && copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + DBG(cdev, "mtp_write: xfer error %d\n", ret); + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + mtp_req_put(dev, &dev->tx_idle, req); + + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) + r = -ECANCELED; + else if (dev->state != STATE_OFFLINE) + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + + DBG(cdev, "mtp_write returning %d\n", r); + return r; +} + +/* read from a local file and write to USB */ +static void send_file_work(struct work_struct *data) +{ + struct mtp_dev *dev = container_of(data, struct mtp_dev, + send_file_work); + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = 0; + struct mtp_data_header *header; + struct file *filp; + loff_t offset; + int64_t count; + int xfer, ret, hdr_size; + int r = 0; + int sendZLP = 0; + + /* read our parameters */ + smp_rmb(); + filp = dev->xfer_file; + offset = dev->xfer_file_offset; + count = dev->xfer_file_length; + + DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); + + if (dev->xfer_send_header) { + hdr_size = sizeof(struct mtp_data_header); + count += hdr_size; + } else { + hdr_size = 0; + } + + /* we need to send a zero length packet to signal the end of transfer + * if the transfer size is aligned to a packet boundary. + */ + if ((count & (dev->ep_in->maxpacket - 1)) == 0) + sendZLP = 1; + + while (count > 0 || sendZLP) { + /* so we exit after sending ZLP */ + if (count == 0) + sendZLP = 0; + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + (req = mtp_req_get(dev, &dev->tx_idle)) + || dev->state != STATE_BUSY); + if (dev->state == STATE_CANCELED) { + r = -ECANCELED; + break; + } + if (!req) { + r = ret; + break; + } + + if (count > MTP_BULK_BUFFER_SIZE) + xfer = MTP_BULK_BUFFER_SIZE; + else + xfer = count; + + if (hdr_size) { + /* prepend MTP data header */ + header = (struct mtp_data_header *)req->buf; + header->length = __cpu_to_le32(count); + header->type = __cpu_to_le16(2); /* data packet */ + header->command = __cpu_to_le16(dev->xfer_command); + header->transaction_id = + __cpu_to_le32(dev->xfer_transaction_id); + } + + ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size, + &offset); + if (ret < 0) { + r = ret; + break; + } + xfer = ret + hdr_size; + hdr_size = 0; + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + DBG(cdev, "send_file_work: xfer error %d\n", ret); + dev->state = STATE_ERROR; + r = -EIO; + break; + } + + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + mtp_req_put(dev, &dev->tx_idle, req); + + DBG(cdev, "send_file_work returning %d\n", r); + /* write the result */ + dev->xfer_result = r; + smp_wmb(); +} + +/* read from USB and write to a local file */ +static void receive_file_work(struct work_struct *data) +{ + struct mtp_dev *dev = container_of(data, struct mtp_dev, + receive_file_work); + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *read_req = NULL, *write_req = NULL; + struct file *filp; + loff_t offset; + int64_t count; + int ret, cur_buf = 0; + int r = 0; + + /* read our parameters */ + smp_rmb(); + filp = dev->xfer_file; + offset = dev->xfer_file_offset; + count = dev->xfer_file_length; + + DBG(cdev, "receive_file_work(%lld)\n", count); + + while (count > 0 || write_req) { + if (count > 0) { + /* queue a request */ + read_req = dev->rx_req[cur_buf]; + cur_buf = (cur_buf + 1) % RX_REQ_MAX; + + read_req->length = (count > MTP_BULK_BUFFER_SIZE + ? MTP_BULK_BUFFER_SIZE : count); + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + dev->state = STATE_ERROR; + break; + } + } + + if (write_req) { + DBG(cdev, "rx %p %d\n", write_req, write_req->actual); + ret = vfs_write(filp, write_req->buf, write_req->actual, + &offset); + DBG(cdev, "vfs_write %d\n", ret); + if (ret != write_req->actual) { + r = -EIO; + dev->state = STATE_ERROR; + break; + } + write_req = NULL; + } + + if (read_req) { + /* wait for our last read to complete */ + ret = wait_event_interruptible(dev->read_wq, + dev->rx_done || dev->state != STATE_BUSY); + if (dev->state == STATE_CANCELED) { + r = -ECANCELED; + if (!dev->rx_done) + usb_ep_dequeue(dev->ep_out, read_req); + break; + } + /* if xfer_file_length is 0xFFFFFFFF, then we read until + * we get a zero length packet + */ + if (count != 0xFFFFFFFF) + count -= read_req->actual; + if (read_req->actual < read_req->length) { + /* + * short packet is used to signal EOF for + * sizes > 4 gig + */ + DBG(cdev, "got short packet\n"); + count = 0; + } + + write_req = read_req; + read_req = NULL; + } + } + + DBG(cdev, "receive_file_work returning %d\n", r); + /* write the result */ + dev->xfer_result = r; + smp_wmb(); +} + +static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) +{ + struct usb_request *req = NULL; + int ret; + int length = event->length; + + DBG(dev->cdev, "mtp_send_event(%d)\n", event->length); + + if (length < 0 || length > INTR_BUFFER_SIZE) + return -EINVAL; + if (dev->state == STATE_OFFLINE) + return -ENODEV; + + ret = wait_event_interruptible_timeout(dev->intr_wq, + (req = mtp_req_get(dev, &dev->intr_idle)), + msecs_to_jiffies(1000)); + if (!req) + return -ETIME; + + if (copy_from_user(req->buf, (void __user *)event->data, length)) { + mtp_req_put(dev, &dev->intr_idle, req); + return -EFAULT; + } + req->length = length; + ret = usb_ep_queue(dev->ep_intr, req, GFP_KERNEL); + if (ret) + mtp_req_put(dev, &dev->intr_idle, req); + + return ret; +} + +static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) +{ + struct mtp_dev *dev = fp->private_data; + struct file *filp = NULL; + int ret = -EINVAL; + + if (mtp_lock(&dev->ioctl_excl)) + return -EBUSY; + + switch (code) { + case MTP_SEND_FILE: + case MTP_RECEIVE_FILE: + case MTP_SEND_FILE_WITH_HEADER: + { + struct mtp_file_range mfr; + struct work_struct *work; + + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) { + /* report cancelation to userspace */ + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); + ret = -ECANCELED; + goto out; + } + if (dev->state == STATE_OFFLINE) { + spin_unlock_irq(&dev->lock); + ret = -ENODEV; + goto out; + } + dev->state = STATE_BUSY; + spin_unlock_irq(&dev->lock); + + if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) { + ret = -EFAULT; + goto fail; + } + /* hold a reference to the file while we are working with it */ + filp = fget(mfr.fd); + if (!filp) { + ret = -EBADF; + goto fail; + } + + /* write the parameters */ + dev->xfer_file = filp; + dev->xfer_file_offset = mfr.offset; + dev->xfer_file_length = mfr.length; + smp_wmb(); + + if (code == MTP_SEND_FILE_WITH_HEADER) { + work = &dev->send_file_work; + dev->xfer_send_header = 1; + dev->xfer_command = mfr.command; + dev->xfer_transaction_id = mfr.transaction_id; + } else if (code == MTP_SEND_FILE) { + work = &dev->send_file_work; + dev->xfer_send_header = 0; + } else { + work = &dev->receive_file_work; + } + + /* We do the file transfer on a work queue so it will run + * in kernel context, which is necessary for vfs_read and + * vfs_write to use our buffers in the kernel address space. + */ + queue_work(dev->wq, work); + /* wait for operation to complete */ + flush_workqueue(dev->wq); + fput(filp); + + /* read the result */ + smp_rmb(); + ret = dev->xfer_result; + break; + } + case MTP_SEND_EVENT: + { + struct mtp_event event; + /* return here so we don't change dev->state below, + * which would interfere with bulk transfer state. + */ + if (copy_from_user(&event, (void __user *)value, sizeof(event))) + ret = -EFAULT; + else + ret = mtp_send_event(dev, &event); + goto out; + } + } + +fail: + spin_lock_irq(&dev->lock); + if (dev->state == STATE_CANCELED) + ret = -ECANCELED; + else if (dev->state != STATE_OFFLINE) + dev->state = STATE_READY; + spin_unlock_irq(&dev->lock); +out: + mtp_unlock(&dev->ioctl_excl); + DBG(dev->cdev, "ioctl returning %d\n", ret); + return ret; +} + +static int mtp_open(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "mtp_open\n"); + if (mtp_lock(&_mtp_dev->open_excl)) + return -EBUSY; + + /* clear any error condition */ + if (_mtp_dev->state != STATE_OFFLINE) + _mtp_dev->state = STATE_READY; + + fp->private_data = _mtp_dev; + return 0; +} + +static int mtp_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "mtp_release\n"); + + mtp_unlock(&_mtp_dev->open_excl); + return 0; +} + +/* file operations for /dev/mtp_usb */ +static const struct file_operations mtp_fops = { + .owner = THIS_MODULE, + .read = mtp_read, + .write = mtp_write, + .unlocked_ioctl = mtp_ioctl, + .open = mtp_open, + .release = mtp_release, +}; + +static struct miscdevice mtp_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = mtp_shortname, + .fops = &mtp_fops, +}; + +static int mtp_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl) +{ + struct mtp_dev *dev = _mtp_dev; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + unsigned long flags; + + VDBG(cdev, "mtp_ctrlrequest " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* Handle MTP OS string */ + if (ctrl->bRequestType == + (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) + && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR + && (w_value >> 8) == USB_DT_STRING + && (w_value & 0xFF) == MTP_OS_STRING_ID) { + value = (w_length < sizeof(mtp_os_string) + ? w_length : sizeof(mtp_os_string)); + memcpy(cdev->req->buf, mtp_os_string, value); + } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { + /* Handle MTP OS descriptor */ + DBG(cdev, "vendor request: %d index: %d value: %d length: %d\n", + ctrl->bRequest, w_index, w_value, w_length); + + if (ctrl->bRequest == 1 + && (ctrl->bRequestType & USB_DIR_IN) + && (w_index == 4 || w_index == 5)) { + value = (w_length < sizeof(mtp_ext_config_desc) ? + w_length : sizeof(mtp_ext_config_desc)); + memcpy(cdev->req->buf, &mtp_ext_config_desc, value); + } + } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { + DBG(cdev, "class request: %d index: %d value: %d length: %d\n", + ctrl->bRequest, w_index, w_value, w_length); + + if (ctrl->bRequest == MTP_REQ_CANCEL && w_index == 0 + && w_value == 0) { + DBG(cdev, "MTP_REQ_CANCEL\n"); + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state == STATE_BUSY) { + dev->state = STATE_CANCELED; + wake_up(&dev->read_wq); + wake_up(&dev->write_wq); + } + spin_unlock_irqrestore(&dev->lock, flags); + + /* We need to queue a request to read the remaining + * bytes, but we don't actually need to look at + * the contents. + */ + value = w_length; + } else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS + && w_index == 0 && w_value == 0) { + struct mtp_device_status *status = cdev->req->buf; + status->wLength = + __constant_cpu_to_le16(sizeof(*status)); + + DBG(cdev, "MTP_REQ_GET_DEVICE_STATUS\n"); + spin_lock_irqsave(&dev->lock, flags); + /* device status is "busy" until we report + * the cancelation to userspace + */ + if (dev->state == STATE_CANCELED) + status->wCode = + __cpu_to_le16(MTP_RESPONSE_DEVICE_BUSY); + else + status->wCode = + __cpu_to_le16(MTP_RESPONSE_OK); + spin_unlock_irqrestore(&dev->lock, flags); + value = sizeof(*status); + } + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + int rc; + cdev->req->zero = value < w_length; + cdev->req->length = value; + rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); + if (rc < 0) + ERROR(cdev, "%s: response queue error\n", __func__); + } + return value; +} + +static int +mtp_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct mtp_dev *dev = func_to_mtp(f); + int id; + int ret; + + dev->cdev = cdev; + DBG(cdev, "mtp_function_bind dev: %p\n", dev); + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + mtp_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc, + &mtp_fullspeed_out_desc, &mtp_intr_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + mtp_highspeed_in_desc.bEndpointAddress = + mtp_fullspeed_in_desc.bEndpointAddress; + mtp_highspeed_out_desc.bEndpointAddress = + mtp_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct mtp_dev *dev = func_to_mtp(f); + struct usb_request *req; + int i; + + while ((req = mtp_req_get(dev, &dev->tx_idle))) + mtp_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + mtp_request_free(dev->rx_req[i], dev->ep_out); + while ((req = mtp_req_get(dev, &dev->intr_idle))) + mtp_request_free(req, dev->ep_intr); + dev->state = STATE_OFFLINE; +} + +static int mtp_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct mtp_dev *dev = func_to_mtp(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "mtp_function_set_alt intf: %d alt: %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_in); + if (ret) + return ret; + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_out); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_intr); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_intr); + if (ret) { + usb_ep_disable(dev->ep_out); + usb_ep_disable(dev->ep_in); + return ret; + } + dev->state = STATE_READY; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void mtp_function_disable(struct usb_function *f) +{ + struct mtp_dev *dev = func_to_mtp(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "mtp_function_disable\n"); + dev->state = STATE_OFFLINE; + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + usb_ep_disable(dev->ep_intr); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) +{ + struct mtp_dev *dev = _mtp_dev; + int ret = 0; + + printk(KERN_INFO "mtp_bind_config\n"); + + /* allocate a string ID for our interface */ + if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + mtp_string_defs[INTERFACE_STRING_INDEX].id = ret; + mtp_interface_desc.iInterface = ret; + } + + dev->cdev = c->cdev; + dev->function.name = "mtp"; + dev->function.strings = mtp_strings; + if (ptp_config) { + dev->function.descriptors = fs_ptp_descs; + dev->function.hs_descriptors = hs_ptp_descs; + } else { + dev->function.descriptors = fs_mtp_descs; + dev->function.hs_descriptors = hs_mtp_descs; + } + dev->function.bind = mtp_function_bind; + dev->function.unbind = mtp_function_unbind; + dev->function.set_alt = mtp_function_set_alt; + dev->function.disable = mtp_function_disable; + + return usb_add_function(c, &dev->function); +} + +static int mtp_setup(void) +{ + struct mtp_dev *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->lock); + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + init_waitqueue_head(&dev->intr_wq); + atomic_set(&dev->open_excl, 0); + atomic_set(&dev->ioctl_excl, 0); + INIT_LIST_HEAD(&dev->tx_idle); + INIT_LIST_HEAD(&dev->intr_idle); + + dev->wq = create_singlethread_workqueue("f_mtp"); + if (!dev->wq) { + ret = -ENOMEM; + goto err1; + } + INIT_WORK(&dev->send_file_work, send_file_work); + INIT_WORK(&dev->receive_file_work, receive_file_work); + + _mtp_dev = dev; + + ret = misc_register(&mtp_device); + if (ret) + goto err2; + + return 0; + +err2: + destroy_workqueue(dev->wq); +err1: + _mtp_dev = NULL; + kfree(dev); + printk(KERN_ERR "mtp gadget driver failed to initialize\n"); + return ret; +} + +static void mtp_cleanup(void) +{ + struct mtp_dev *dev = _mtp_dev; + + if (!dev) + return; + + misc_deregister(&mtp_device); + destroy_workqueue(dev->wq); + _mtp_dev = NULL; + kfree(dev); +} diff --git a/include/linux/usb/f_mtp.h b/include/linux/usb/f_mtp.h new file mode 100644 index 000000000000..72a432e2fcdd --- /dev/null +++ b/include/linux/usb/f_mtp.h @@ -0,0 +1,75 @@ +/* + * Gadget Function Driver for MTP + * + * Copyright (C) 2010 Google, Inc. + * Author: Mike Lockwood + * + * 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_USB_F_MTP_H +#define __LINUX_USB_F_MTP_H + +#include + +#ifdef __KERNEL__ + +struct mtp_data_header { + /* length of packet, including this header */ + uint32_t length; + /* container type (2 for data packet) */ + uint16_t type; + /* MTP command code */ + uint16_t command; + /* MTP transaction ID */ + uint32_t transaction_id; +}; + +#endif /* __KERNEL__ */ + +struct mtp_file_range { + /* file descriptor for file to transfer */ + int fd; + /* offset in file for start of transfer */ + loff_t offset; + /* number of bytes to transfer */ + int64_t length; + /* MTP command ID for data header, + * used only for MTP_SEND_FILE_WITH_HEADER + */ + uint16_t command; + /* MTP transaction ID for data header, + * used only for MTP_SEND_FILE_WITH_HEADER + */ + uint32_t transaction_id; +}; + +struct mtp_event { + /* size of the event */ + size_t length; + /* event data to send */ + void *data; +}; + +/* Sends the specified file range to the host */ +#define MTP_SEND_FILE _IOW('M', 0, struct mtp_file_range) +/* Receives data from the host and writes it to a file. + * The file is created if it does not exist. + */ +#define MTP_RECEIVE_FILE _IOW('M', 1, struct mtp_file_range) +/* Sends an event to the host via the interrupt endpoint */ +#define MTP_SEND_EVENT _IOW('M', 3, struct mtp_event) +/* Sends the specified file range to the host, + * with a 12 byte MTP data packet header at the beginning. + */ +#define MTP_SEND_FILE_WITH_HEADER _IOW('M', 4, struct mtp_file_range) + +#endif /* __LINUX_USB_F_MTP_H */ From 796cdfa202cda0c18da419c02cb10a3062d5009b Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 19 Dec 2011 14:38:41 -0800 Subject: [PATCH 103/358] usb: gadget: adb: Add ADB function Android Debug Bridge (adb) is a command line tool that lets users communicate with a Android-powered device. It is used mainly to debug applications and tranfer files. f_adb implements the transport layer between the ADB Server (on the host) and the ADBD daemon (on the device). Signed-off-by: Mike Lockwood --- drivers/usb/gadget/android.c | 29 ++ drivers/usb/gadget/f_adb.c | 611 +++++++++++++++++++++++++++++++++++ 2 files changed, 640 insertions(+) create mode 100644 drivers/usb/gadget/f_adb.c diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index fe688442337d..b9cb65d2be12 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -45,6 +45,7 @@ #include "f_mass_storage.c" #include "u_serial.c" #include "f_acm.c" +#include "f_adb.c" #include "f_mtp.c" #define USB_ETH_RNDIS y #include "f_rndis.c" @@ -185,6 +186,33 @@ static void android_work(struct work_struct *data) /*-------------------------------------------------------------------------*/ /* Supported functions initialization */ +static int +adb_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + return adb_setup(); +} + +static void adb_function_cleanup(struct android_usb_function *f) +{ + adb_cleanup(); +} + +static int +adb_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + return adb_bind_config(c); +} + +static struct android_usb_function adb_function = { + .name = "adb", + .init = adb_function_init, + .cleanup = adb_function_cleanup, + .bind_config = adb_function_bind_config, +}; + + #define MAX_ACM_INSTANCES 4 struct acm_function_config { int instances; @@ -606,6 +634,7 @@ static struct android_usb_function mass_storage_function = { static struct android_usb_function *supported_functions[] = { + &adb_function, &acm_function, &mtp_function, &ptp_function, diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c new file mode 100644 index 000000000000..5415353ab2c1 --- /dev/null +++ b/drivers/usb/gadget/f_adb.c @@ -0,0 +1,611 @@ +/* + * Gadget Driver for Android ADB + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADB_BULK_BUFFER_SIZE 4096 + +/* number of tx requests to allocate */ +#define TX_REQ_MAX 4 + +static const char adb_shortname[] = "android_adb"; + +struct adb_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + + int online; + int error; + + atomic_t read_excl; + atomic_t write_excl; + atomic_t open_excl; + + struct list_head tx_idle; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req; + int rx_done; +}; + +static struct usb_interface_descriptor adb_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0xFF, + .bInterfaceSubClass = 0x42, + .bInterfaceProtocol = 1, +}; + +static struct usb_endpoint_descriptor adb_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor adb_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor adb_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor adb_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_adb_descs[] = { + (struct usb_descriptor_header *) &adb_interface_desc, + (struct usb_descriptor_header *) &adb_fullspeed_in_desc, + (struct usb_descriptor_header *) &adb_fullspeed_out_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_adb_descs[] = { + (struct usb_descriptor_header *) &adb_interface_desc, + (struct usb_descriptor_header *) &adb_highspeed_in_desc, + (struct usb_descriptor_header *) &adb_highspeed_out_desc, + NULL, +}; + + +/* temporary variable used between adb_open() and adb_gadget_bind() */ +static struct adb_dev *_adb_dev; + +static inline struct adb_dev *func_to_adb(struct usb_function *f) +{ + return container_of(f, struct adb_dev, function); +} + + +static struct usb_request *adb_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void adb_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static inline int adb_lock(atomic_t *excl) +{ + if (atomic_inc_return(excl) == 1) { + return 0; + } else { + atomic_dec(excl); + return -1; + } +} + +static inline void adb_unlock(atomic_t *excl) +{ + atomic_dec(excl); +} + +/* add a request to the tail of a list */ +void adb_req_put(struct adb_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +struct usb_request *adb_req_get(struct adb_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void adb_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct adb_dev *dev = _adb_dev; + + if (req->status != 0) + dev->error = 1; + + adb_req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +static void adb_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct adb_dev *dev = _adb_dev; + + dev->rx_done = 1; + if (req->status != 0) + dev->error = 1; + + wake_up(&dev->read_wq); +} + +static int adb_create_bulk_endpoints(struct adb_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for adb ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + /* now allocate requests for our endpoints */ + req = adb_request_new(dev->ep_out, ADB_BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = adb_complete_out; + dev->rx_req = req; + + for (i = 0; i < TX_REQ_MAX; i++) { + req = adb_request_new(dev->ep_in, ADB_BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = adb_complete_in; + adb_req_put(dev, &dev->tx_idle, req); + } + + return 0; + +fail: + printk(KERN_ERR "adb_bind() could not allocate requests\n"); + return -1; +} + +static ssize_t adb_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct adb_dev *dev = fp->private_data; + struct usb_request *req; + int r = count, xfer; + int ret; + + pr_debug("adb_read(%d)\n", count); + if (!_adb_dev) + return -ENODEV; + + if (count > ADB_BULK_BUFFER_SIZE) + return -EINVAL; + + if (adb_lock(&dev->read_excl)) + return -EBUSY; + + /* we will block until we're online */ + while (!(dev->online || dev->error)) { + pr_debug("adb_read: waiting for online state\n"); + ret = wait_event_interruptible(dev->read_wq, + (dev->online || dev->error)); + if (ret < 0) { + adb_unlock(&dev->read_excl); + return ret; + } + } + if (dev->error) { + r = -EIO; + goto done; + } + +requeue_req: + /* queue a request */ + req = dev->rx_req; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC); + if (ret < 0) { + pr_debug("adb_read: failed to queue req %p (%d)\n", req, ret); + r = -EIO; + dev->error = 1; + goto done; + } else { + pr_debug("rx %p queue\n", req); + } + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + dev->error = 1; + r = ret; + usb_ep_dequeue(dev->ep_out, req); + goto done; + } + if (!dev->error) { + /* 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); + xfer = (req->actual < count) ? req->actual : count; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + + } else + r = -EIO; + +done: + adb_unlock(&dev->read_excl); + pr_debug("adb_read returning %d\n", r); + return r; +} + +static ssize_t adb_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct adb_dev *dev = fp->private_data; + struct usb_request *req = 0; + int r = count, xfer; + int ret; + + if (!_adb_dev) + return -ENODEV; + pr_debug("adb_write(%d)\n", count); + + if (adb_lock(&dev->write_excl)) + return -EBUSY; + + while (count > 0) { + if (dev->error) { + pr_debug("adb_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + (req = adb_req_get(dev, &dev->tx_idle)) || dev->error); + + if (ret < 0) { + r = ret; + break; + } + + if (req != 0) { + if (count > ADB_BULK_BUFFER_SIZE) + xfer = ADB_BULK_BUFFER_SIZE; + else + xfer = count; + if (copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC); + if (ret < 0) { + pr_debug("adb_write: xfer error %d\n", ret); + dev->error = 1; + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + } + + if (req) + adb_req_put(dev, &dev->tx_idle, req); + + adb_unlock(&dev->write_excl); + pr_debug("adb_write returning %d\n", r); + return r; +} + +static int adb_open(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "adb_open\n"); + if (!_adb_dev) + return -ENODEV; + + if (adb_lock(&_adb_dev->open_excl)) + return -EBUSY; + + fp->private_data = _adb_dev; + + /* clear the error latch */ + _adb_dev->error = 0; + + return 0; +} + +static int adb_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "adb_release\n"); + adb_unlock(&_adb_dev->open_excl); + return 0; +} + +/* file operations for ADB device /dev/android_adb */ +static const struct file_operations adb_fops = { + .owner = THIS_MODULE, + .read = adb_read, + .write = adb_write, + .open = adb_open, + .release = adb_release, +}; + +static struct miscdevice adb_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = adb_shortname, + .fops = &adb_fops, +}; + + + + +static int +adb_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct adb_dev *dev = func_to_adb(f); + int id; + int ret; + + dev->cdev = cdev; + DBG(cdev, "adb_function_bind dev: %p\n", dev); + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + adb_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = adb_create_bulk_endpoints(dev, &adb_fullspeed_in_desc, + &adb_fullspeed_out_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + adb_highspeed_in_desc.bEndpointAddress = + adb_fullspeed_in_desc.bEndpointAddress; + adb_highspeed_out_desc.bEndpointAddress = + adb_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +adb_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct adb_dev *dev = func_to_adb(f); + struct usb_request *req; + + + dev->online = 0; + dev->error = 1; + + wake_up(&dev->read_wq); + + adb_request_free(dev->rx_req, dev->ep_out); + while ((req = adb_req_get(dev, &dev->tx_idle))) + adb_request_free(req, dev->ep_in); +} + +static int adb_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct adb_dev *dev = func_to_adb(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_in); + if (ret) + return ret; + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_out); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + dev->online = 1; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void adb_function_disable(struct usb_function *f) +{ + struct adb_dev *dev = func_to_adb(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "adb_function_disable cdev %p\n", cdev); + dev->online = 0; + dev->error = 1; + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +static int adb_bind_config(struct usb_configuration *c) +{ + struct adb_dev *dev = _adb_dev; + + printk(KERN_INFO "adb_bind_config\n"); + + dev->cdev = c->cdev; + dev->function.name = "adb"; + dev->function.descriptors = fs_adb_descs; + dev->function.hs_descriptors = hs_adb_descs; + dev->function.bind = adb_function_bind; + dev->function.unbind = adb_function_unbind; + dev->function.set_alt = adb_function_set_alt; + dev->function.disable = adb_function_disable; + + return usb_add_function(c, &dev->function); +} + +static int adb_setup(void) +{ + struct adb_dev *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->lock); + + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + + atomic_set(&dev->open_excl, 0); + atomic_set(&dev->read_excl, 0); + atomic_set(&dev->write_excl, 0); + + INIT_LIST_HEAD(&dev->tx_idle); + + _adb_dev = dev; + + ret = misc_register(&adb_device); + if (ret) + goto err; + + return 0; + +err: + kfree(dev); + printk(KERN_ERR "adb gadget driver failed to initialize\n"); + return ret; +} + +static void adb_cleanup(void) +{ + misc_deregister(&adb_device); + + kfree(_adb_dev); + _adb_dev = NULL; +} From 235656656b7d142e68ce68e8ffe845730965f0fb Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 19 Dec 2011 14:39:37 -0800 Subject: [PATCH 104/358] usb: gadget: accessory: Add Android Accessory function USB accessory mode allows users to connect USB host hardware specifically designed for Android-powered devices. The accessories must adhere to the Android accessory protocol outlined in the http://accessories.android.com documentation. This allows Android devices that cannot act as a USB host to still interact with USB hardware. When an Android device is in USB accessory mode, the attached Android USB accessory acts as the host, provides power to the USB bus, and enumerates connected devices. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/android.c | 45 ++ drivers/usb/gadget/f_accessory.c | 796 +++++++++++++++++++++++++++++++ include/linux/usb/f_accessory.h | 83 ++++ 3 files changed, 924 insertions(+) create mode 100644 drivers/usb/gadget/f_accessory.c create mode 100644 include/linux/usb/f_accessory.h diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index b9cb65d2be12..b39ae6737624 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -47,6 +47,7 @@ #include "f_acm.c" #include "f_adb.c" #include "f_mtp.c" +#include "f_accessory.c" #define USB_ETH_RNDIS y #include "f_rndis.c" #include "rndis.c" @@ -633,6 +634,39 @@ static struct android_usb_function mass_storage_function = { }; +static int accessory_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + return acc_setup(); +} + +static void accessory_function_cleanup(struct android_usb_function *f) +{ + acc_cleanup(); +} + +static int accessory_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + return acc_bind_config(c); +} + +static int accessory_function_ctrlrequest(struct android_usb_function *f, + struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *c) +{ + return acc_ctrlrequest(cdev, c); +} + +static struct android_usb_function accessory_function = { + .name = "accessory", + .init = accessory_function_init, + .cleanup = accessory_function_cleanup, + .bind_config = accessory_function_bind_config, + .ctrlrequest = accessory_function_ctrlrequest, +}; + + static struct android_usb_function *supported_functions[] = { &adb_function, &acm_function, @@ -640,6 +674,7 @@ static struct android_usb_function *supported_functions[] = { &ptp_function, &rndis_function, &mass_storage_function, + &accessory_function, NULL }; @@ -825,6 +860,10 @@ static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, struct usb_composite_dev *cdev = dev->cdev; int enabled = 0; + + if (!cdev) + return -ENODEV; + mutex_lock(&dev->mutex); sscanf(buff, "%d", &enabled); @@ -1072,6 +1111,12 @@ android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) } } + /* Special case the accessory function. + * It needs to handle control requests before it is enabled. + */ + if (value < 0) + value = acc_ctrlrequest(cdev, c); + if (value < 0) value = composite_setup(gadget, c); diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c new file mode 100644 index 000000000000..a5818227611a --- /dev/null +++ b/drivers/usb/gadget/f_accessory.c @@ -0,0 +1,796 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define BULK_BUFFER_SIZE 16384 +#define ACC_STRING_SIZE 256 + +#define PROTOCOL_VERSION 1 + +/* String IDs */ +#define INTERFACE_STRING_INDEX 0 + +/* number of tx and rx requests to allocate */ +#define TX_REQ_MAX 4 +#define RX_REQ_MAX 2 + +struct acc_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + + /* set to 1 when we connect */ + int online:1; + /* Set to 1 when we disconnect. + * Not cleared until our file is closed. + */ + int disconnected:1; + + /* strings sent by the host */ + char manufacturer[ACC_STRING_SIZE]; + char model[ACC_STRING_SIZE]; + char description[ACC_STRING_SIZE]; + char version[ACC_STRING_SIZE]; + char uri[ACC_STRING_SIZE]; + char serial[ACC_STRING_SIZE]; + + /* for acc_complete_set_string */ + int string_index; + + /* set to 1 if we have a pending start request */ + int start_requested; + + /* synchronize access to our device file */ + atomic_t open_excl; + + struct list_head tx_idle; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req[RX_REQ_MAX]; + int rx_done; + struct delayed_work work; +}; + +static struct usb_interface_descriptor acc_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, +}; + +static struct usb_endpoint_descriptor acc_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acc_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acc_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor acc_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_acc_descs[] = { + (struct usb_descriptor_header *) &acc_interface_desc, + (struct usb_descriptor_header *) &acc_fullspeed_in_desc, + (struct usb_descriptor_header *) &acc_fullspeed_out_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_acc_descs[] = { + (struct usb_descriptor_header *) &acc_interface_desc, + (struct usb_descriptor_header *) &acc_highspeed_in_desc, + (struct usb_descriptor_header *) &acc_highspeed_out_desc, + NULL, +}; + +static struct usb_string acc_string_defs[] = { + [INTERFACE_STRING_INDEX].s = "Android Accessory Interface", + { }, /* end of list */ +}; + +static struct usb_gadget_strings acc_string_table = { + .language = 0x0409, /* en-US */ + .strings = acc_string_defs, +}; + +static struct usb_gadget_strings *acc_strings[] = { + &acc_string_table, + NULL, +}; + +/* temporary variable used between acc_open() and acc_gadget_bind() */ +static struct acc_dev *_acc_dev; + +static inline struct acc_dev *func_to_dev(struct usb_function *f) +{ + return container_of(f, struct acc_dev, function); +} + +static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void acc_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +/* add a request to the tail of a list */ +static void req_put(struct acc_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void acc_set_disconnected(struct acc_dev *dev) +{ + dev->online = 0; + dev->disconnected = 1; +} + +static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = _acc_dev; + + if (req->status != 0) + acc_set_disconnected(dev); + + req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +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) + acc_set_disconnected(dev); + + wake_up(&dev->read_wq); +} + +static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = ep->driver_data; + char *string_dest = NULL; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_set_string, err %d\n", req->status); + return; + } + + switch (dev->string_index) { + case ACCESSORY_STRING_MANUFACTURER: + string_dest = dev->manufacturer; + break; + case ACCESSORY_STRING_MODEL: + string_dest = dev->model; + break; + case ACCESSORY_STRING_DESCRIPTION: + string_dest = dev->description; + break; + case ACCESSORY_STRING_VERSION: + string_dest = dev->version; + break; + case ACCESSORY_STRING_URI: + string_dest = dev->uri; + break; + case ACCESSORY_STRING_SERIAL: + string_dest = dev->serial; + break; + } + if (string_dest) { + unsigned long flags; + + if (length >= ACC_STRING_SIZE) + length = ACC_STRING_SIZE - 1; + + spin_lock_irqsave(&dev->lock, flags); + memcpy(string_dest, req->buf, length); + /* ensure zero termination */ + string_dest[length] = 0; + spin_unlock_irqrestore(&dev->lock, flags); + } else { + pr_err("unknown accessory string index %d\n", + dev->string_index); + } +} + +static int __init create_bulk_endpoints(struct acc_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + /* now allocate requests for our endpoints */ + for (i = 0; i < TX_REQ_MAX; i++) { + req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = acc_complete_in; + req_put(dev, &dev->tx_idle, req); + } + for (i = 0; i < RX_REQ_MAX; i++) { + req = acc_request_new(dev->ep_out, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = acc_complete_out; + dev->rx_req[i] = req; + } + + return 0; + +fail: + printk(KERN_ERR "acc_bind() could not allocate requests\n"); + while ((req = req_get(dev, &dev->tx_idle))) + acc_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + acc_request_free(dev->rx_req[i], dev->ep_out); + return -1; +} + +static ssize_t acc_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct acc_dev *dev = fp->private_data; + struct usb_request *req; + int r = count, xfer; + int ret = 0; + + pr_debug("acc_read(%d)\n", count); + + if (dev->disconnected) + return -ENODEV; + + if (count > BULK_BUFFER_SIZE) + count = BULK_BUFFER_SIZE; + + /* we will block until we're online */ + pr_debug("acc_read: waiting for online\n"); + ret = wait_event_interruptible(dev->read_wq, dev->online); + if (ret < 0) { + r = ret; + goto done; + } + +requeue_req: + /* queue a request */ + req = dev->rx_req[0]; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + goto done; + } else { + pr_debug("rx %p queue\n", req); + } + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + r = ret; + usb_ep_dequeue(dev->ep_out, req); + goto done; + } + 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); + xfer = (req->actual < count) ? req->actual : count; + r = xfer; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + } else + r = -EIO; + +done: + pr_debug("acc_read returning %d\n", r); + return r; +} + +static ssize_t acc_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct acc_dev *dev = fp->private_data; + struct usb_request *req = 0; + int r = count, xfer; + int ret; + + pr_debug("acc_write(%d)\n", count); + + if (!dev->online || dev->disconnected) + return -ENODEV; + + while (count > 0) { + if (!dev->online) { + pr_debug("acc_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); + if (!req) { + r = ret; + break; + } + + if (count > BULK_BUFFER_SIZE) + xfer = BULK_BUFFER_SIZE; + else + xfer = count; + if (copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + pr_debug("acc_write: xfer error %d\n", ret); + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + req_put(dev, &dev->tx_idle, req); + + pr_debug("acc_write returning %d\n", r); + return r; +} + +static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) +{ + struct acc_dev *dev = fp->private_data; + char *src = NULL; + int ret; + + switch (code) { + case ACCESSORY_GET_STRING_MANUFACTURER: + src = dev->manufacturer; + break; + case ACCESSORY_GET_STRING_MODEL: + src = dev->model; + break; + case ACCESSORY_GET_STRING_DESCRIPTION: + src = dev->description; + break; + case ACCESSORY_GET_STRING_VERSION: + src = dev->version; + break; + case ACCESSORY_GET_STRING_URI: + src = dev->uri; + break; + case ACCESSORY_GET_STRING_SERIAL: + src = dev->serial; + break; + case ACCESSORY_IS_START_REQUESTED: + return dev->start_requested; + } + if (!src) + return -EINVAL; + + ret = strlen(src) + 1; + if (copy_to_user((void __user *)value, src, ret)) + ret = -EFAULT; + return ret; +} + +static int acc_open(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "acc_open\n"); + if (atomic_xchg(&_acc_dev->open_excl, 1)) + return -EBUSY; + + _acc_dev->disconnected = 0; + fp->private_data = _acc_dev; + return 0; +} + +static int acc_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "acc_release\n"); + + WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); + _acc_dev->disconnected = 0; + return 0; +} + +/* file operations for /dev/acc_usb */ +static const struct file_operations acc_fops = { + .owner = THIS_MODULE, + .read = acc_read, + .write = acc_write, + .unlocked_ioctl = acc_ioctl, + .open = acc_open, + .release = acc_release, +}; + +static struct miscdevice acc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "usb_accessory", + .fops = &acc_fops, +}; + + +static int acc_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl) +{ + struct acc_dev *dev = _acc_dev; + int value = -EOPNOTSUPP; + u8 b_requestType = ctrl->bRequestType; + u8 b_request = ctrl->bRequest; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + +/* + printk(KERN_INFO "acc_ctrlrequest " + "%02x.%02x v%04x i%04x l%u\n", + b_requestType, b_request, + w_value, w_index, w_length); +*/ + + if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { + if (b_request == ACCESSORY_START) { + dev->start_requested = 1; + schedule_delayed_work( + &dev->work, msecs_to_jiffies(10)); + value = 0; + } else if (b_request == ACCESSORY_SEND_STRING) { + dev->string_index = w_index; + cdev->gadget->ep0->driver_data = dev; + cdev->req->complete = acc_complete_set_string; + value = w_length; + } + } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { + if (b_request == ACCESSORY_GET_PROTOCOL) { + *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; + value = sizeof(u16); + + /* clear any string left over from a previous session */ + memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); + memset(dev->model, 0, sizeof(dev->model)); + memset(dev->description, 0, sizeof(dev->description)); + memset(dev->version, 0, sizeof(dev->version)); + memset(dev->uri, 0, sizeof(dev->uri)); + memset(dev->serial, 0, sizeof(dev->serial)); + dev->start_requested = 0; + } + } + + if (value >= 0) { + cdev->req->zero = 0; + cdev->req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "%s setup response queue error\n", + __func__); + } + + if (value == -EOPNOTSUPP) + VDBG(cdev, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + return value; +} + +static int +acc_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct acc_dev *dev = func_to_dev(f); + int id; + int ret; + + DBG(cdev, "acc_function_bind dev: %p\n", dev); + + dev->start_requested = 0; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + acc_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = create_bulk_endpoints(dev, &acc_fullspeed_in_desc, + &acc_fullspeed_out_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + acc_highspeed_in_desc.bEndpointAddress = + acc_fullspeed_in_desc.bEndpointAddress; + acc_highspeed_out_desc.bEndpointAddress = + acc_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +acc_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_request *req; + int i; + + while ((req = req_get(dev, &dev->tx_idle))) + acc_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + acc_request_free(dev->rx_req[i], dev->ep_out); +} + +static void acc_work(struct work_struct *data) +{ + char *envp[2] = { "ACCESSORY=START", NULL }; + kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); +} + +static int acc_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_in); + if (ret) + return ret; + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_out); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + + dev->online = 1; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void acc_function_disable(struct usb_function *f) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "acc_function_disable\n"); + acc_set_disconnected(dev); + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +static int acc_bind_config(struct usb_configuration *c) +{ + struct acc_dev *dev = _acc_dev; + int ret; + + printk(KERN_INFO "acc_bind_config\n"); + + /* allocate a string ID for our interface */ + if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + acc_string_defs[INTERFACE_STRING_INDEX].id = ret; + acc_interface_desc.iInterface = ret; + } + + dev->cdev = c->cdev; + dev->function.name = "accessory"; + dev->function.strings = acc_strings, + dev->function.descriptors = fs_acc_descs; + dev->function.hs_descriptors = hs_acc_descs; + dev->function.bind = acc_function_bind; + dev->function.unbind = acc_function_unbind; + dev->function.set_alt = acc_function_set_alt; + dev->function.disable = acc_function_disable; + + return usb_add_function(c, &dev->function); +} + +static int acc_setup(void) +{ + struct acc_dev *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->lock); + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + atomic_set(&dev->open_excl, 0); + INIT_LIST_HEAD(&dev->tx_idle); + INIT_DELAYED_WORK(&dev->work, acc_work); + + /* _acc_dev must be set before calling usb_gadget_register_driver */ + _acc_dev = dev; + + ret = misc_register(&acc_device); + if (ret) + goto err; + + return 0; + +err: + kfree(dev); + printk(KERN_ERR "USB accessory gadget driver failed to initialize\n"); + return ret; +} + +static void acc_cleanup(void) +{ + misc_deregister(&acc_device); + kfree(_acc_dev); + _acc_dev = NULL; +} diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h new file mode 100644 index 000000000000..5b2dcf9728e1 --- /dev/null +++ b/include/linux/usb/f_accessory.h @@ -0,0 +1,83 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * 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_USB_F_ACCESSORY_H +#define __LINUX_USB_F_ACCESSORY_H + +/* Use Google Vendor ID when in accessory mode */ +#define USB_ACCESSORY_VENDOR_ID 0x18D1 + + +/* Product ID to use when in accessory mode */ +#define USB_ACCESSORY_PRODUCT_ID 0x2D00 + +/* Product ID to use when in accessory mode and adb is enabled */ +#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 + +/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 + +/* Control request for retrieving device's protocol version (currently 1) + * + * requestType: USB_DIR_IN | USB_TYPE_VENDOR + * request: ACCESSORY_GET_PROTOCOL + * value: 0 + * index: 0 + * data version number (16 bits little endian) + */ +#define ACCESSORY_GET_PROTOCOL 51 + +/* Control request for host to send a string to the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_STRING + * value: 0 + * index: string ID + * data zero terminated UTF8 string + * + * The device can later retrieve these strings via the + * ACCESSORY_GET_STRING_* ioctls + */ +#define ACCESSORY_SEND_STRING 52 + +/* Control request for starting device in accessory mode. + * The host sends this after setting all its strings to the device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_START + * value: 0 + * index: 0 + * data none + */ +#define ACCESSORY_START 53 + +/* ioctls for retrieving strings set by the host */ +#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) +#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) +#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) +#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) +#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) +#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) +/* returns 1 if there is a start request pending */ +#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) + +#endif /* __LINUX_USB_F_ACCESSORY_H */ From 128514adf016ac2b51a9ab965d659081f3814558 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 5 Mar 2012 13:29:45 -0800 Subject: [PATCH 105/358] usb: gadget: adb: allow freezing in adb_read wait_event_interruptible in adb_read might return -ERESTARTSYS if userspace is frozen during adb_read or another signal is delivered to adb. If so, don't set dev->error to avoid resetting the adb connection. Change-Id: I5a7baa013a9a3a3b5305de7e6a0d18546a560018 Signed-off-by: Colin Cross --- drivers/usb/gadget/f_adb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c index 5415353ab2c1..4433a4dbdfb1 100644 --- a/drivers/usb/gadget/f_adb.c +++ b/drivers/usb/gadget/f_adb.c @@ -313,7 +313,8 @@ requeue_req: /* wait for a request to complete */ ret = wait_event_interruptible(dev->read_wq, dev->rx_done); if (ret < 0) { - dev->error = 1; + if (ret != -ERESTARTSYS) + dev->error = 1; r = ret; usb_ep_dequeue(dev->ep_out, req); goto done; From d4f37a54c862c7fd41dc1a1c6af6383a81b033c0 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 8 Mar 2012 17:57:51 -0800 Subject: [PATCH 106/358] usb: gadget: adb: do not set error flag when dequeuing req When an ep_out req is dequeued because of userspace freezing, don't set the error flag. Change-Id: I680f1a1059b8ac2244aaa069e7d42dc44abf98e9 Signed-off-by: Colin Cross --- drivers/usb/gadget/f_adb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c index 4433a4dbdfb1..3827715f832d 100644 --- a/drivers/usb/gadget/f_adb.c +++ b/drivers/usb/gadget/f_adb.c @@ -205,7 +205,7 @@ static void adb_complete_out(struct usb_ep *ep, struct usb_request *req) struct adb_dev *dev = _adb_dev; dev->rx_done = 1; - if (req->status != 0) + if (req->status != 0 && req->status != -ECONNRESET) dev->error = 1; wake_up(&dev->read_wq); From 5dd722cf719843673e3ad49400da76d32bb2ea46 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Mar 2012 16:28:45 -0700 Subject: [PATCH 107/358] ARM: fiq_debugger: fix compiling for v3.3 Call kernel_restart instead of arch_reset, the ARM reset handling has changed. Remove localtimer irq printing, they now show up in the regular irq stats. Change-Id: I523da343b292c5711f3e1cbfd766d32eea2da84e Signed-off-by: Colin Cross --- arch/arm/common/fiq_debugger.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index 3ed18ae2ed80..0e33748edd60 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -37,8 +38,6 @@ #include #include -#include - #include #include "fiq_debugger_ringbuf.h" @@ -357,7 +356,6 @@ static void dump_allregs(struct fiq_debugger_state *state, unsigned *regs) static void dump_irqs(struct fiq_debugger_state *state) { int n; - unsigned int cpu; debug_printf(state, "irqnr total since-last status name\n"); for (n = 0; n < NR_IRQS; n++) { @@ -371,16 +369,6 @@ static void dump_irqs(struct fiq_debugger_state *state) (act && act->name) ? act->name : "???"); state->last_irqs[n] = kstat_irqs(n); } - - for (cpu = 0; cpu < NR_CPUS; cpu++) { - - debug_printf(state, "LOC %d: %10u %11u\n", cpu, - __IRQ_STAT(cpu, local_timer_irqs), - __IRQ_STAT(cpu, local_timer_irqs) - - state->last_local_timer_irqs[cpu]); - state->last_local_timer_irqs[cpu] = - __IRQ_STAT(cpu, local_timer_irqs); - } } struct stacktrace_state { @@ -605,7 +593,7 @@ static bool debug_fiq_exec(struct fiq_debugger_state *state, } else if (!strcmp(cmd, "bt")) { dump_stacktrace(state, (struct pt_regs *)regs, 100, svc_sp); } else if (!strcmp(cmd, "reboot")) { - arch_reset(0, 0); + kernel_restart(NULL); } else if (!strcmp(cmd, "irqs")) { dump_irqs(state); } else if (!strcmp(cmd, "kmsg")) { From 2dbe62012f483e81a3b360292ce42a49b6450a01 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Mar 2012 16:29:47 -0700 Subject: [PATCH 108/358] ARM: fiq_debugger: add support for reboot commands Pass the rest of the reboot command to kernel_restart to allow reboot bootloader to work from FIQ debugger. Change-Id: I4e7b366a69268dda17ffcf4c84f2373d15cb1271 Signed-off-by: Colin Cross --- arch/arm/common/fiq_debugger.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index 0e33748edd60..3f75495fab02 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -592,8 +592,17 @@ static bool debug_fiq_exec(struct fiq_debugger_state *state, dump_allregs(state, regs); } else if (!strcmp(cmd, "bt")) { dump_stacktrace(state, (struct pt_regs *)regs, 100, svc_sp); - } else if (!strcmp(cmd, "reboot")) { - kernel_restart(NULL); + } else if (!strncmp(cmd, "reboot", 6)) { + cmd += 6; + while (*cmd == ' ') + cmd++; + if (*cmd) { + char tmp_cmd[32]; + strlcpy(tmp_cmd, cmd, sizeof(tmp_cmd)); + kernel_restart(tmp_cmd); + } else { + kernel_restart(NULL); + } } else if (!strcmp(cmd, "irqs")) { dump_irqs(state); } else if (!strcmp(cmd, "kmsg")) { From 5ca5456338b7bb057e1266f0c210bce47bf724f7 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 15 Mar 2012 12:57:20 -0700 Subject: [PATCH 109/358] ARM: fiq_debugger: add debug_putc Convert all the calls to state->pdata->uart_putc to a debug_putc helper. Change-Id: Idc007bd170ff1b51d0325e238105ae0c86d23777 Signed-off-by: Colin Cross --- arch/arm/common/fiq_debugger.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index 3f75495fab02..909ef56596e8 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -174,13 +174,18 @@ static void debug_uart_flush(struct fiq_debugger_state *state) state->pdata->uart_flush(state->pdev); } +static void debug_putc(struct fiq_debugger_state *state, char c) +{ + state->pdata->uart_putc(state->pdev, c); +} + static void debug_puts(struct fiq_debugger_state *state, char *s) { unsigned c; while ((c = *s++)) { if (c == '\n') - state->pdata->uart_putc(state->pdev, '\r'); - state->pdata->uart_putc(state->pdev, c); + debug_putc(state, '\r'); + debug_putc(state, c); } } @@ -777,19 +782,19 @@ static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state, } else if ((c >= ' ') && (c < 127)) { if (state->debug_count < (DEBUG_MAX - 1)) { state->debug_buf[state->debug_count++] = c; - state->pdata->uart_putc(state->pdev, c); + debug_putc(state, c); } } else if ((c == 8) || (c == 127)) { if (state->debug_count > 0) { state->debug_count--; - state->pdata->uart_putc(state->pdev, 8); - state->pdata->uart_putc(state->pdev, ' '); - state->pdata->uart_putc(state->pdev, 8); + debug_putc(state, 8); + debug_putc(state, ' '); + debug_putc(state, 8); } } else if ((c == 13) || (c == 10)) { if (c == '\r' || (c == '\n' && last_c != '\r')) { - state->pdata->uart_putc(state->pdev, '\r'); - state->pdata->uart_putc(state->pdev, '\n'); + debug_putc(state, '\r'); + debug_putc(state, '\n'); } if (state->debug_count) { state->debug_buf[state->debug_count] = 0; @@ -898,8 +903,8 @@ static void debug_console_write(struct console *co, debug_uart_enable(state); while (count--) { if (*s == '\n') - state->pdata->uart_putc(state->pdev, '\r'); - state->pdata->uart_putc(state->pdev, *s++); + debug_putc(state, '\r'); + debug_putc(state, *s++); } debug_uart_flush(state); debug_uart_disable(state); @@ -941,7 +946,7 @@ int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) debug_uart_enable(state); for (i = 0; i < count; i++) - state->pdata->uart_putc(state->pdev, *buf++); + debug_putc(state, *buf++); debug_uart_disable(state); return count; From 51aa7fccb244c013e90ba3e82ef620f7a65e5f74 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Mar 2012 19:23:29 -0700 Subject: [PATCH 110/358] ARM: fiq_debugger: add support for kgdb Adds polling tty ops to the fiq debugger console tty, which allows kgdb to run against an fiq debugger console. Add a check in do_sysrq to prevent enabling kgdb from the fiq debugger unless a flag (writable only by root) has been set. This should make it safe to enable KGDB on a production device. Also add a shortcut to enable the console and kgdb together, to allow kgdb to be enabled when the shell on the console is not responding. Change-Id: Ifc65239ca96c9887431a6a36b9b44a539002f544 Signed-off-by: Colin Cross --- arch/arm/common/fiq_debugger.c | 73 +++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index 909ef56596e8..1f64d7dc83b4 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -106,9 +106,12 @@ static bool initial_debug_enable; static bool initial_console_enable; #endif +static bool fiq_kgdb_enable; + module_param_named(no_sleep, initial_no_sleep, bool, 0644); module_param_named(debug_enable, initial_debug_enable, bool, 0644); module_param_named(console_enable, initial_console_enable, bool, 0644); +module_param_named(kgdb_enable, fiq_kgdb_enable, bool, 0644); #ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON static inline void enable_wakeup_irq(struct fiq_debugger_state *state) {} @@ -526,11 +529,29 @@ static void end_syslog_dump(struct fiq_debugger_state *state) static void do_sysrq(struct fiq_debugger_state *state, char rq) { + if ((rq == 'g' || rq == 'G') && !fiq_kgdb_enable) { + debug_printf(state, "sysrq-g blocked\n"); + return; + } begin_syslog_dump(state); handle_sysrq(rq); end_syslog_dump(state); } +#ifdef CONFIG_KGDB +static void do_kgdb(struct fiq_debugger_state *state) +{ + if (!fiq_kgdb_enable) { + debug_printf(state, "kgdb through fiq debugger not enabled\n"); + return; + } + + debug_printf(state, "enabling console and triggering kgdb\n"); + state->console_enable = true; + handle_sysrq('g'); +} +#endif + /* This function CANNOT be called in FIQ context */ static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd) { @@ -540,6 +561,10 @@ static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd) do_sysrq(state, 'h'); if (!strncmp(cmd, "sysrq ", 6)) do_sysrq(state, cmd[6]); +#ifdef CONFIG_KGDB + if (!strcmp(cmd, "kgdb")) + do_kgdb(state); +#endif } static void debug_help(struct fiq_debugger_state *state) @@ -561,6 +586,9 @@ static void debug_help(struct fiq_debugger_state *state) debug_printf(state, " ps Process list\n" " sysrq sysrq options\n" " sysrq Execute sysrq with \n"); +#ifdef CONFIG_KGDB + debug_printf(state, " kgdb Enter kernel debugger\n"); +#endif } static void take_affinity(void *info) @@ -724,7 +752,8 @@ static void debug_handle_irq_context(struct fiq_debugger_state *state) #endif if (state->debug_busy) { debug_irq_exec(state, state->debug_cmd); - debug_prompt(state); + if (!state->console_enable) + debug_prompt(state); state->debug_busy = 0; } } @@ -957,11 +986,53 @@ int fiq_tty_write_room(struct tty_struct *tty) return 1024; } +#ifdef CONFIG_CONSOLE_POLL +static int fiq_tty_poll_init(struct tty_driver *driver, int line, char *options) +{ + return 0; +} + +static int fiq_tty_poll_get_char(struct tty_driver *driver, int line) +{ + struct fiq_debugger_state *state = driver->ttys[line]->driver_data; + int c = NO_POLL_CHAR; + + debug_uart_enable(state); + if (debug_have_fiq(state)) { + int count = fiq_debugger_ringbuf_level(state->tty_rbuf); + if (count > 0) { + c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0); + fiq_debugger_ringbuf_consume(state->tty_rbuf, 1); + } + } else { + c = debug_getc(state); + if (c == FIQ_DEBUGGER_NO_CHAR) + c = NO_POLL_CHAR; + } + debug_uart_disable(state); + + return c; +} + +static void fiq_tty_poll_put_char(struct tty_driver *driver, int line, char ch) +{ + struct fiq_debugger_state *state = driver->ttys[line]->driver_data; + debug_uart_enable(state); + debug_putc(state, ch); + debug_uart_disable(state); +} +#endif + static const struct tty_operations fiq_tty_driver_ops = { .write = fiq_tty_write, .write_room = fiq_tty_write_room, .open = fiq_tty_open, .close = fiq_tty_close, +#ifdef CONFIG_CONSOLE_POLL + .poll_init = fiq_tty_poll_init, + .poll_get_char = fiq_tty_poll_get_char, + .poll_put_char = fiq_tty_poll_put_char, +#endif }; static int fiq_debugger_tty_init(struct fiq_debugger_state *state) From 801b807fc42702e9d6ff595877a354b82486e3c1 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Mar 2012 19:26:53 -0700 Subject: [PATCH 111/358] kdb: support new lines without carriage returns kdb expects carriage returns through the serial port to terminate commands. Modify it to accept the first seen carriage return or new line as a terminator, but not treat \r\n as two terminators. Change-Id: I06166017e7703d24310eefcb71c3a7d427088db7 Signed-off-by: Colin Cross --- kernel/debug/kdb/kdb_io.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c index 14ff4849262c..4b0fb2fb779c 100644 --- a/kernel/debug/kdb/kdb_io.c +++ b/kernel/debug/kdb/kdb_io.c @@ -216,7 +216,7 @@ static char *kdb_read(char *buffer, size_t bufsize) int i; int diag, dtab_count; int key; - + static int last_crlf; diag = kdbgetintenv("DTABCOUNT", &dtab_count); if (diag) @@ -237,6 +237,9 @@ poll_again: return buffer; if (key != 9) tab = 0; + if (key != 10 && key != 13) + last_crlf = 0; + switch (key) { case 8: /* backspace */ if (cp > buffer) { @@ -254,7 +257,12 @@ poll_again: *cp = tmp; } break; - case 13: /* enter */ + case 10: /* new line */ + case 13: /* carriage return */ + /* handle \n after \r */ + if (last_crlf && last_crlf != key) + break; + last_crlf = key; *lastchar++ = '\n'; *lastchar++ = '\0'; if (!KDB_STATE(KGDB_TRANS)) { From ace9c95f3f6dbaed9389eebe19a8dbd1ba05f091 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Sun, 18 Mar 2012 15:25:55 -0700 Subject: [PATCH 112/358] ARM: fiq_debugger: fix multiple consoles and make it a preferred console Fix setting up consoles on multiple fiq debugger devices by splitting the tty driver init into the initcall, and initializing the single tty device during probe. Has the side effect of moving the tty device node to /dev/ttyFIQx, where x is the platform device id, which should normally match the serial port. To avoid having to pass a different console=/dev/ttyFIQx for every device, make the fiq debugger a preferred console that will be used by default if no console was passed on the command line. Change-Id: I6cc2670628a41e84615859bc96adba189966d647 Signed-off-by: Colin Cross --- arch/arm/common/fiq_debugger.c | 113 ++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 29 deletions(-) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index 1f64d7dc83b4..ac952241fdd2 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -45,6 +45,8 @@ #define DEBUG_MAX 64 #define MAX_UNHANDLED_FIQ_COUNT 1000000 +#define MAX_FIQ_DEBUGGER_PORTS 4 + #define THREAD_INFO(sp) ((struct thread_info *) \ ((unsigned long)(sp) & ~(THREAD_SIZE - 1))) @@ -81,7 +83,6 @@ struct fiq_debugger_state { #ifdef CONFIG_FIQ_DEBUGGER_CONSOLE struct console console; - struct tty_driver *tty_driver; struct tty_struct *tty; int tty_open_count; struct fiq_debugger_ringbuf *tty_rbuf; @@ -92,6 +93,10 @@ struct fiq_debugger_state { unsigned int last_local_timer_irqs[NR_CPUS]; }; +#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE +struct tty_driver *fiq_tty_driver; +#endif + #ifdef CONFIG_FIQ_DEBUGGER_NO_SLEEP static bool initial_no_sleep = true; #else @@ -913,10 +918,8 @@ static void debug_resume(struct fiq_glue_handler *h) #if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) struct tty_driver *debug_console_device(struct console *co, int *index) { - struct fiq_debugger_state *state; - state = container_of(co, struct fiq_debugger_state, console); - *index = 0; - return state->tty_driver; + *index = co->index; + return fiq_tty_driver; } static void debug_console_write(struct console *co, @@ -948,7 +951,9 @@ static struct console fiq_debugger_console = { int fiq_tty_open(struct tty_struct *tty, struct file *filp) { - struct fiq_debugger_state *state = tty->driver->driver_state; + int line = tty->index; + struct fiq_debugger_state **states = tty->driver->driver_state; + struct fiq_debugger_state *state = states[line]; if (state->tty_open_count++) return 0; @@ -1035,36 +1040,66 @@ static const struct tty_operations fiq_tty_driver_ops = { #endif }; -static int fiq_debugger_tty_init(struct fiq_debugger_state *state) +static int fiq_debugger_tty_init(void) { - int ret = -EINVAL; + int ret; + struct fiq_debugger_state **states = NULL; - state->tty_driver = alloc_tty_driver(1); - if (!state->tty_driver) { - pr_err("Failed to allocate fiq debugger tty\n"); + states = kzalloc(sizeof(*states) * MAX_FIQ_DEBUGGER_PORTS, GFP_KERNEL); + if (!states) { + pr_err("Failed to allocate fiq debugger state structres\n"); return -ENOMEM; } - state->tty_driver->owner = THIS_MODULE; - state->tty_driver->driver_name = "fiq-debugger"; - state->tty_driver->name = "ttyFIQ"; - state->tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - state->tty_driver->subtype = SERIAL_TYPE_NORMAL; - state->tty_driver->init_termios = tty_std_termios; - state->tty_driver->init_termios.c_cflag = - B115200 | CS8 | CREAD | HUPCL | CLOCAL; - state->tty_driver->init_termios.c_ispeed = - state->tty_driver->init_termios.c_ospeed = 115200; - state->tty_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(state->tty_driver, &fiq_tty_driver_ops); - state->tty_driver->driver_state = state; + fiq_tty_driver = alloc_tty_driver(MAX_FIQ_DEBUGGER_PORTS); + if (!fiq_tty_driver) { + pr_err("Failed to allocate fiq debugger tty\n"); + ret = -ENOMEM; + goto err_free_state; + } - ret = tty_register_driver(state->tty_driver); + fiq_tty_driver->owner = THIS_MODULE; + fiq_tty_driver->driver_name = "fiq-debugger"; + fiq_tty_driver->name = "ttyFIQ"; + fiq_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + fiq_tty_driver->subtype = SERIAL_TYPE_NORMAL; + fiq_tty_driver->init_termios = tty_std_termios; + fiq_tty_driver->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; + fiq_tty_driver->driver_state = states; + + fiq_tty_driver->init_termios.c_cflag = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; + fiq_tty_driver->init_termios.c_ispeed = 115200; + fiq_tty_driver->init_termios.c_ospeed = 115200; + + tty_set_operations(fiq_tty_driver, &fiq_tty_driver_ops); + + ret = tty_register_driver(fiq_tty_driver); if (ret) { pr_err("Failed to register fiq tty: %d\n", ret); - goto err; + goto err_free_tty; } + pr_info("Registered FIQ tty driver\n"); + return 0; + +err_free_tty: + put_tty_driver(fiq_tty_driver); + fiq_tty_driver = NULL; +err_free_state: + kfree(states); + return ret; +} + +static int fiq_debugger_tty_init_one(struct fiq_debugger_state *state) +{ + int ret; + struct device *tty_dev; + struct fiq_debugger_state **states = fiq_tty_driver->driver_state; + + states[state->pdev->id] = state; + state->tty_rbuf = fiq_debugger_ringbuf_alloc(1024); if (!state->tty_rbuf) { pr_err("Failed to allocate fiq debugger ringbuf\n"); @@ -1072,13 +1107,23 @@ static int fiq_debugger_tty_init(struct fiq_debugger_state *state) goto err; } - pr_info("Registered FIQ tty driver %p\n", state->tty_driver); + tty_dev = tty_register_device(fiq_tty_driver, state->pdev->id, + &state->pdev->dev); + if (IS_ERR(tty_dev)) { + pr_err("Failed to register fiq debugger tty device\n"); + ret = PTR_ERR(tty_dev); + goto err; + } + + device_set_wakeup_capable(tty_dev, 1); + + pr_info("Registered fiq debugger ttyFIQ%d\n", state->pdev->id); + return 0; err: fiq_debugger_ringbuf_free(state->tty_rbuf); state->tty_rbuf = NULL; - put_tty_driver(state->tty_driver); return ret; } #endif @@ -1111,6 +1156,9 @@ static int fiq_debugger_probe(struct platform_device *pdev) int fiq; int uart_irq; + if (pdev->id >= MAX_FIQ_DEBUGGER_PORTS) + return -EINVAL; + if (!pdata->uart_getc || !pdata->uart_putc) return -EINVAL; if ((pdata->uart_enable && !pdata->uart_disable) || @@ -1228,8 +1276,12 @@ static int fiq_debugger_probe(struct platform_device *pdev) #if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) state->console = fiq_debugger_console; + state->console.index = pdev->id; + if (!console_set_on_cmdline) + add_preferred_console(state->console.name, + state->console.index, NULL); register_console(&state->console); - fiq_debugger_tty_init(state); + fiq_debugger_tty_init_one(state); #endif return 0; @@ -1263,6 +1315,9 @@ static struct platform_driver fiq_debugger_driver = { static int __init fiq_debugger_init(void) { +#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) + fiq_debugger_tty_init(); +#endif return platform_driver_register(&fiq_debugger_driver); } From 75d7847718211c08d1479055a875ea82b1b7792a Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 19 Mar 2012 18:56:52 -0700 Subject: [PATCH 113/358] usb: gadget: adb: Only enable the gadget when adbd is ready When adb is enabled, only connect the gadget when adbd is ready. If adbd dies or is restarted (e.g. "adb root"), the gadget is disconnected when the adb device is close, and it is re-connected once adb re-open the device. - Add callbacks to adb, similar to FunctionFs callbacks, to notify the gadget when the daemon is ready or closed. - Refcount calls to android_enable/android_disable to enable the gadget only once all the function daemons are ready. - Add enable/disble to android_usb_function to notify the function when it is added/removed from the list of enabled functions. Change-Id: Id54ff85aec9cf8715c94b4f9bd6137a79ad58bfc Signed-off-by: Benoit Goby --- drivers/usb/gadget/android.c | 117 ++++++++++++++++++++++++++++++++--- drivers/usb/gadget/f_adb.c | 11 +++- 2 files changed, 118 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index b39ae6737624..d2c3393237b6 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -79,6 +79,11 @@ struct android_usb_function { int (*init)(struct android_usb_function *, struct usb_composite_dev *); /* Optional: cleanup during gadget unbind */ void (*cleanup)(struct android_usb_function *); + /* Optional: called when the function is added the list of + * enabled functions */ + void (*enable)(struct android_usb_function *); + /* Optional: called when it is removed */ + void (*disable)(struct android_usb_function *); int (*bind_config)(struct android_usb_function *, struct usb_configuration *); @@ -99,6 +104,7 @@ struct android_dev { struct device *dev; bool enabled; + int disable_depth; struct mutex mutex; bool connected; bool sw_connected; @@ -183,20 +189,55 @@ static void android_work(struct work_struct *data) } } +static void android_enable(struct android_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + + if (WARN_ON(!dev->disable_depth)) + return; + + if (--dev->disable_depth == 0) { + usb_add_config(cdev, &android_config_driver, + android_bind_config); + usb_gadget_connect(cdev->gadget); + } +} + +static void android_disable(struct android_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + + if (dev->disable_depth++ == 0) { + usb_gadget_disconnect(cdev->gadget); + /* Cancel pending control requests */ + usb_ep_dequeue(cdev->gadget->ep0, cdev->req); + usb_remove_config(cdev, &android_config_driver); + } +} /*-------------------------------------------------------------------------*/ /* Supported functions initialization */ +struct adb_data { + bool opened; + bool enabled; +}; + static int adb_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) { + f->config = kzalloc(sizeof(struct adb_data), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + return adb_setup(); } static void adb_function_cleanup(struct android_usb_function *f) { adb_cleanup(); + kfree(f->config); } static int @@ -206,13 +247,69 @@ adb_function_bind_config(struct android_usb_function *f, return adb_bind_config(c); } +static void adb_android_function_enable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = f->config; + + data->enabled = true; + + /* Disable the gadget until adbd is ready */ + if (!data->opened) + android_disable(dev); +} + +static void adb_android_function_disable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = f->config; + + data->enabled = false; + + /* Balance the disable that was called in closed_callback */ + if (!data->opened) + android_enable(dev); +} + static struct android_usb_function adb_function = { .name = "adb", + .enable = adb_android_function_enable, + .disable = adb_android_function_disable, .init = adb_function_init, .cleanup = adb_function_cleanup, .bind_config = adb_function_bind_config, }; +static void adb_ready_callback(void) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = adb_function.config; + + mutex_lock(&dev->mutex); + + data->opened = true; + + if (data->enabled) + android_enable(dev); + + mutex_unlock(&dev->mutex); +} + +static void adb_closed_callback(void) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = adb_function.config; + + mutex_lock(&dev->mutex); + + data->opened = false; + + if (data->enabled) + android_disable(dev); + + mutex_unlock(&dev->mutex); +} + #define MAX_ACM_INSTANCES 4 struct acm_function_config { @@ -858,6 +955,7 @@ static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, { struct android_dev *dev = dev_get_drvdata(pdev); struct usb_composite_dev *cdev = dev->cdev; + struct android_usb_function *f; int enabled = 0; @@ -879,16 +977,18 @@ static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, cdev->desc.bDeviceClass = device_desc.bDeviceClass; cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass; cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol; - - usb_add_config(cdev, &android_config_driver, - android_bind_config); - usb_gadget_connect(cdev->gadget); + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->enable) + f->enable(f); + } + android_enable(dev); dev->enabled = true; } else if (!enabled && dev->enabled) { - usb_gadget_disconnect(cdev->gadget); - /* Cancel pending control requests */ - usb_ep_dequeue(cdev->gadget->ep0, cdev->req); - usb_remove_config(cdev, &android_config_driver); + android_disable(dev); + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->disable) + f->disable(f); + } dev->enabled = false; } else { pr_err("android_usb: already %s\n", @@ -1184,6 +1284,7 @@ static int __init init(void) if (!dev) return -ENOMEM; + dev->disable_depth = 1; dev->functions = supported_functions; INIT_LIST_HEAD(&dev->enabled_functions); INIT_WORK(&dev->work, android_work); diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c index 3827715f832d..1629ffb5b979 100644 --- a/drivers/usb/gadget/f_adb.c +++ b/drivers/usb/gadget/f_adb.c @@ -111,6 +111,8 @@ static struct usb_descriptor_header *hs_adb_descs[] = { NULL, }; +static void adb_ready_callback(void); +static void adb_closed_callback(void); /* temporary variable used between adb_open() and adb_gadget_bind() */ static struct adb_dev *_adb_dev; @@ -407,7 +409,7 @@ static ssize_t adb_write(struct file *fp, const char __user *buf, static int adb_open(struct inode *ip, struct file *fp) { - printk(KERN_INFO "adb_open\n"); + pr_info("adb_open\n"); if (!_adb_dev) return -ENODEV; @@ -419,12 +421,17 @@ static int adb_open(struct inode *ip, struct file *fp) /* clear the error latch */ _adb_dev->error = 0; + adb_ready_callback(); + return 0; } static int adb_release(struct inode *ip, struct file *fp) { - printk(KERN_INFO "adb_release\n"); + pr_info("adb_release\n"); + + adb_closed_callback(); + adb_unlock(&_adb_dev->open_excl); return 0; } From 2141cd4c03f1c9289e1f3e937ef60d0d2f3fae6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 28 Mar 2012 21:03:13 -0700 Subject: [PATCH 114/358] ARM: etm: Wait for etm/ptm(s) to stop before requesting PowerDown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When PowerDown was requested at the same time as ProgBit, the formatter flush command that follows could get stuck. Change-Id: Iafb665f61f055819e64ca1dcb60398c656f593e4 Signed-off-by: Arve Hjønnevåg --- arch/arm/kernel/etm.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 4e51d123aa0b..c1590244cb79 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -200,11 +200,13 @@ static int trace_stop_etm(struct tracectx *t, int id) etm_unlock(t, id); - etm_writel(t, id, 0x441, ETMR_CTRL); + etm_writel(t, id, 0x440, ETMR_CTRL); while (!(etm_readl(t, id, ETMR_CTRL) & ETMCTRL_PROGRAM) && --timeout) ; if (!timeout) { - dev_dbg(t->dev, "Waiting for progbit to assert timed out\n"); + dev_err(t->dev, + "etm%d: Waiting for progbit to assert timed out\n", + id); etm_lock(t, id); return -EFAULT; } @@ -213,18 +215,36 @@ static int trace_stop_etm(struct tracectx *t, int id) return 0; } +static int trace_power_down_etm(struct tracectx *t, int id) +{ + unsigned long timeout = TRACER_TIMEOUT; + etm_unlock(t, id); + while (!(etm_readl(t, id, ETMR_STATUS) & ETMST_PROGBIT) && --timeout) + ; + if (!timeout) { + dev_err(t->dev, "etm%d: Waiting for status progbit to assert timed out\n", + id); + etm_lock(t, id); + return -EFAULT; + } + + etm_writel(t, id, 0x441, ETMR_CTRL); + + etm_lock(t, id); + return 0; +} + static int trace_stop(struct tracectx *t) { int id; - int ret; unsigned long timeout = TRACER_TIMEOUT; u32 etb_fc = t->etb_fc; - for (id = 0; id < t->etm_regs_count; id++) { - ret = trace_stop_etm(t, id); - if (ret) - return ret; - } + for (id = 0; id < t->etm_regs_count; id++) + trace_stop_etm(t, id); + + for (id = 0; id < t->etm_regs_count; id++) + trace_power_down_etm(t, id); etb_unlock(t); if (etb_fc) { From db5250f772ffc49f3d0ca3a747d3755c936207b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 2 Apr 2012 20:18:56 -0700 Subject: [PATCH 115/358] ARM: etm: Check arch version and disable data tracing for ptm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: If2cb7928d0711f48348443d882a12416be9c5910 Signed-off-by: Arve Hjønnevåg --- arch/arm/include/asm/hardware/coresight.h | 5 +++++ arch/arm/kernel/etm.c | 23 ++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h index 8f8ae5833587..12b894ce55f8 100644 --- a/arch/arm/include/asm/hardware/coresight.h +++ b/arch/arm/include/asm/hardware/coresight.h @@ -60,6 +60,7 @@ /* ETM configuration code register */ #define ETMR_CONFCODE (0x04) +#define ETMCCR_ETMIDR_PRESENT BIT(31) /* ETM trace start/stop resource control register */ #define ETMR_TRACESSCTRL (0x18) @@ -126,6 +127,10 @@ ETMCTRL_BRANCH_OUTPUT | \ ETMCTRL_DO_CONTEXTID) +#define ETMR_ID 0x1e4 +#define ETMIDR_VERSION(x) (((x) >> 4) & 0xff) +#define ETMIDR_VERSION_PFT_1_0 0x30 + #define ETMR_TRACEIDR 0x200 /* ETM management registers, "ETM Architecture", 3.5.24 */ diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index c1590244cb79..7eb59c488570 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -721,6 +721,9 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) int ret = 0; void __iomem **new_regs; int new_count; + u32 etmccr; + u32 etmidr; + u8 etm_version = 0; mutex_lock(&t->mutex); new_count = t->etm_regs_count + 1; @@ -755,7 +758,12 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) /* dummy first read */ (void)etm_readl(&tracer, t->etm_regs_count, ETMMR_OSSRR); - t->ncmppairs = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE) & 0xf; + etmccr = etm_readl(t, t->etm_regs_count, ETMR_CONFCODE); + t->ncmppairs = etmccr & 0xf; + if (etmccr & ETMCCR_ETMIDR_PRESENT) { + etmidr = etm_readl(t, t->etm_regs_count, ETMR_ID); + etm_version = ETMIDR_VERSION(etmidr); + } etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL); etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR); etm_lock(t, t->etm_regs_count); @@ -778,10 +786,15 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) if (ret) dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n"); - ret = sysfs_create_file(&dev->dev.kobj, &trace_data_range_attr.attr); - if (ret) - dev_dbg(&dev->dev, - "Failed to create trace_data_range in sysfs\n"); + if (etm_version < ETMIDR_VERSION_PFT_1_0) { + ret = sysfs_create_file(&dev->dev.kobj, + &trace_data_range_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_data_range in sysfs\n"); + } else { + tracer.flags &= ~TRACER_TRACE_DATA; + } dev_dbg(&dev->dev, "ETM AMBA driver initialized.\n"); From 08d590c1b2ef1d0a368c305b37c2836b121659d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 2 Apr 2012 17:20:32 -0700 Subject: [PATCH 116/358] ARM: etm: Add sysfs entry to enable timestamps if supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Iff964ba2f6236ed81863e02ec7b3ec9fbc48044a Signed-off-by: Arve Hjønnevåg --- arch/arm/include/asm/hardware/coresight.h | 7 ++++ arch/arm/kernel/etm.c | 44 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h index 12b894ce55f8..1af9e719ec7e 100644 --- a/arch/arm/include/asm/hardware/coresight.h +++ b/arch/arm/include/asm/hardware/coresight.h @@ -18,10 +18,12 @@ #define TRACER_RUNNING_BIT 1 #define TRACER_CYCLE_ACC_BIT 2 #define TRACER_TRACE_DATA_BIT 3 +#define TRACER_TIMESTAMP_BIT 4 #define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT) #define TRACER_RUNNING BIT(TRACER_RUNNING_BIT) #define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT) #define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT) +#define TRACER_TIMESTAMP BIT(TRACER_TIMESTAMP_BIT) #define TRACER_TIMEOUT 10000 @@ -57,6 +59,7 @@ #define ETMCTRL_DATA_DO_BOTH (ETMCTRL_DATA_DO_DATA | ETMCTRL_DATA_DO_ADDR) #define ETMCTRL_BRANCH_OUTPUT (1 << 8) #define ETMCTRL_CYCLEACCURATE (1 << 12) +#define ETMCTRL_TIMESTAMP_EN (1 << 28) /* ETM configuration code register */ #define ETMR_CONFCODE (0x04) @@ -129,8 +132,12 @@ #define ETMR_ID 0x1e4 #define ETMIDR_VERSION(x) (((x) >> 4) & 0xff) +#define ETMIDR_VERSION_3_1 0x21 #define ETMIDR_VERSION_PFT_1_0 0x30 +#define ETMR_CCE 0x1e8 +#define ETMCCER_TIMESTAMPING_IMPLEMENTED BIT(22) + #define ETMR_TRACEIDR 0x200 /* ETM management registers, "ETM Architecture", 3.5.24 */ diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 7eb59c488570..0174afa2ceeb 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -116,6 +116,9 @@ static int trace_start_etm(struct tracectx *t, int id) if (t->flags & TRACER_TRACE_DATA) v |= ETMCTRL_DATA_DO_ADDR; + if (t->flags & TRACER_TIMESTAMP) + v |= ETMCTRL_TIMESTAMP_EN; + etm_unlock(t, id); etm_writel(t, id, v, ETMR_CTRL); @@ -644,6 +647,36 @@ static ssize_t trace_mode_store(struct kobject *kobj, static struct kobj_attribute trace_mode_attr = __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); +static ssize_t trace_timestamp_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_TIMESTAMP)); +} + +static ssize_t trace_timestamp_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int timestamp; + + if (sscanf(buf, "%u", ×tamp) != 1) + return -EINVAL; + + mutex_lock(&tracer.mutex); + if (timestamp) + tracer.flags |= TRACER_TIMESTAMP; + else + tracer.flags &= ~TRACER_TIMESTAMP; + mutex_unlock(&tracer.mutex); + + return n; +} + +static struct kobj_attribute trace_timestamp_attr = + __ATTR(trace_timestamp, 0644, + trace_timestamp_show, trace_timestamp_store); + static ssize_t trace_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -723,6 +756,7 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) int new_count; u32 etmccr; u32 etmidr; + u32 etmccer = 0; u8 etm_version = 0; mutex_lock(&t->mutex); @@ -763,6 +797,8 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) if (etmccr & ETMCCR_ETMIDR_PRESENT) { etmidr = etm_readl(t, t->etm_regs_count, ETMR_ID); etm_version = ETMIDR_VERSION(etmidr); + if (etm_version >= ETMIDR_VERSION_3_1) + etmccer = etm_readl(t, t->etm_regs_count, ETMR_CCE); } etm_writel(t, t->etm_regs_count, 0x441, ETMR_CTRL); etm_writel(t, t->etm_regs_count, new_count, ETMR_TRACEIDR); @@ -782,6 +818,14 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) if (ret) dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); + if (etmccer & ETMCCER_TIMESTAMPING_IMPLEMENTED) { + ret = sysfs_create_file(&dev->dev.kobj, + &trace_timestamp_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_timestamp in sysfs\n"); + } + ret = sysfs_create_file(&dev->dev.kobj, &trace_range_attr.attr); if (ret) dev_dbg(&dev->dev, "Failed to create trace_range in sysfs\n"); From a3034859b018580457b3ac10a74389c920a0bc8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 2 Apr 2012 17:20:32 -0700 Subject: [PATCH 117/358] ARM: etm: Add sysfs entry to set context-id-size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I520dfb6e593dac131de8b9b1db77f1c734f18c24 Signed-off-by: Arve Hjønnevåg --- arch/arm/include/asm/hardware/coresight.h | 5 ++- arch/arm/kernel/etm.c | 40 +++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h index 1af9e719ec7e..d682c55bef98 100644 --- a/arch/arm/include/asm/hardware/coresight.h +++ b/arch/arm/include/asm/hardware/coresight.h @@ -47,7 +47,7 @@ #define ETMCTRL_POWERDOWN 1 #define ETMCTRL_PROGRAM (1 << 10) #define ETMCTRL_PORTSEL (1 << 11) -#define ETMCTRL_DO_CONTEXTID (3 << 14) +#define ETMCTRL_CONTEXTIDSIZE(x) (((x) & 3) << 14) #define ETMCTRL_PORTMASK1 (7 << 4) #define ETMCTRL_PORTMASK2 (1 << 21) #define ETMCTRL_PORTMASK (ETMCTRL_PORTMASK1 | ETMCTRL_PORTMASK2) @@ -127,8 +127,7 @@ #define ETMVDC3_EXCLONLY BIT(16) #define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \ - ETMCTRL_BRANCH_OUTPUT | \ - ETMCTRL_DO_CONTEXTID) + ETMCTRL_BRANCH_OUTPUT) #define ETMR_ID 0x1e4 #define ETMIDR_VERSION(x) (((x) >> 4) & 0xff) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 0174afa2ceeb..67ed4fb94b18 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -43,6 +43,7 @@ struct tracectx { unsigned long flags; int ncmppairs; int etm_portsz; + int etm_contextid_size; u32 etb_fc; unsigned long range_start; unsigned long range_end; @@ -109,6 +110,7 @@ static int trace_start_etm(struct tracectx *t, int id) unsigned long timeout = TRACER_TIMEOUT; v = ETMCTRL_OPTS | ETMCTRL_PROGRAM | ETMCTRL_PORTSIZE(t->etm_portsz); + v |= ETMCTRL_CONTEXTIDSIZE(t->etm_contextid_size); if (t->flags & TRACER_CYCLE_ACC) v |= ETMCTRL_CYCLEACCURATE; @@ -647,6 +649,37 @@ static ssize_t trace_mode_store(struct kobject *kobj, static struct kobj_attribute trace_mode_attr = __ATTR(trace_mode, 0644, trace_mode_show, trace_mode_store); +static ssize_t trace_contextid_size_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + /* 0: No context id tracing, 1: One byte, 2: Two bytes, 3: Four bytes */ + return sprintf(buf, "%d\n", (1 << tracer.etm_contextid_size) >> 1); +} + +static ssize_t trace_contextid_size_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int contextid_size; + + if (sscanf(buf, "%u", &contextid_size) != 1) + return -EINVAL; + + if (contextid_size == 3 || contextid_size > 4) + return -EINVAL; + + mutex_lock(&tracer.mutex); + tracer.etm_contextid_size = fls(contextid_size); + mutex_unlock(&tracer.mutex); + + return n; +} + +static struct kobj_attribute trace_contextid_size_attr = + __ATTR(trace_contextid_size, 0644, + trace_contextid_size_show, trace_contextid_size_store); + static ssize_t trace_timestamp_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -786,6 +819,7 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA; t->etm_portsz = 1; + t->etm_contextid_size = 3; etm_unlock(t, t->etm_regs_count); (void)etm_readl(t, t->etm_regs_count, ETMMR_PDSR); @@ -818,6 +852,12 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) if (ret) dev_dbg(&dev->dev, "Failed to create trace_mode in sysfs\n"); + ret = sysfs_create_file(&dev->dev.kobj, + &trace_contextid_size_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_contextid_size in sysfs\n"); + if (etmccer & ETMCCER_TIMESTAMPING_IMPLEMENTED) { ret = sysfs_create_file(&dev->dev.kobj, &trace_timestamp_attr.attr); From c52dfb1452e1034b6a5776611188b9fe3965f00c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 3 Apr 2012 16:15:36 -0700 Subject: [PATCH 118/358] ARM: etm: Add sysfs entry to disable branch_output flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib91208a2c33621aa2d7bd9aa72bfbc670d9d5f1d Signed-off-by: Arve Hjønnevåg --- arch/arm/include/asm/hardware/coresight.h | 5 +-- arch/arm/kernel/etm.c | 41 ++++++++++++++++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h index d682c55bef98..f27706eb5f28 100644 --- a/arch/arm/include/asm/hardware/coresight.h +++ b/arch/arm/include/asm/hardware/coresight.h @@ -19,11 +19,13 @@ #define TRACER_CYCLE_ACC_BIT 2 #define TRACER_TRACE_DATA_BIT 3 #define TRACER_TIMESTAMP_BIT 4 +#define TRACER_BRANCHOUTPUT_BIT 5 #define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT) #define TRACER_RUNNING BIT(TRACER_RUNNING_BIT) #define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT) #define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT) #define TRACER_TIMESTAMP BIT(TRACER_TIMESTAMP_BIT) +#define TRACER_BRANCHOUTPUT BIT(TRACER_BRANCHOUTPUT_BIT) #define TRACER_TIMEOUT 10000 @@ -126,8 +128,7 @@ #define ETMR_VIEWDATACTRL3 0x3c #define ETMVDC3_EXCLONLY BIT(16) -#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT | \ - ETMCTRL_BRANCH_OUTPUT) +#define ETMCTRL_OPTS (ETMCTRL_DO_CPRT) #define ETMR_ID 0x1e4 #define ETMIDR_VERSION(x) (((x) >> 4) & 0xff) diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 67ed4fb94b18..02e500b3c79d 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -115,6 +115,9 @@ static int trace_start_etm(struct tracectx *t, int id) if (t->flags & TRACER_CYCLE_ACC) v |= ETMCTRL_CYCLEACCURATE; + if (t->flags & TRACER_BRANCHOUTPUT) + v |= ETMCTRL_BRANCH_OUTPUT; + if (t->flags & TRACER_TRACE_DATA) v |= ETMCTRL_DATA_DO_ADDR; @@ -680,6 +683,36 @@ static struct kobj_attribute trace_contextid_size_attr = __ATTR(trace_contextid_size, 0644, trace_contextid_size_show, trace_contextid_size_store); +static ssize_t trace_branch_output_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_BRANCHOUTPUT)); +} + +static ssize_t trace_branch_output_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int branch_output; + + if (sscanf(buf, "%u", &branch_output) != 1) + return -EINVAL; + + mutex_lock(&tracer.mutex); + if (branch_output) + tracer.flags |= TRACER_BRANCHOUTPUT; + else + tracer.flags &= ~TRACER_BRANCHOUTPUT; + mutex_unlock(&tracer.mutex); + + return n; +} + +static struct kobj_attribute trace_branch_output_attr = + __ATTR(trace_branch_output, 0644, + trace_branch_output_show, trace_branch_output_store); + static ssize_t trace_timestamp_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -817,7 +850,7 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) amba_set_drvdata(dev, t->etm_regs[t->etm_regs_count]); - t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA; + t->flags = TRACER_CYCLE_ACC | TRACER_TRACE_DATA | TRACER_BRANCHOUTPUT; t->etm_portsz = 1; t->etm_contextid_size = 3; @@ -858,6 +891,12 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) dev_dbg(&dev->dev, "Failed to create trace_contextid_size in sysfs\n"); + ret = sysfs_create_file(&dev->dev.kobj, + &trace_branch_output_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_branch_output in sysfs\n"); + if (etmccer & ETMCCER_TIMESTAMPING_IMPLEMENTED) { ret = sysfs_create_file(&dev->dev.kobj, &trace_timestamp_attr.attr); From 2bdd528c44b9654f423a6ac1e9d182300a29ef95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 3 Apr 2012 18:01:03 -0700 Subject: [PATCH 119/358] ARM: etm: Add sysfs entry to enable return stack if supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Icb73d60324ad0ddfc3e8a450a28bb3d90c702788 Signed-off-by: Arve Hjønnevåg --- arch/arm/include/asm/hardware/coresight.h | 4 ++ arch/arm/kernel/etm.c | 51 ++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/hardware/coresight.h b/arch/arm/include/asm/hardware/coresight.h index f27706eb5f28..b3507650b41c 100644 --- a/arch/arm/include/asm/hardware/coresight.h +++ b/arch/arm/include/asm/hardware/coresight.h @@ -20,12 +20,14 @@ #define TRACER_TRACE_DATA_BIT 3 #define TRACER_TIMESTAMP_BIT 4 #define TRACER_BRANCHOUTPUT_BIT 5 +#define TRACER_RETURN_STACK_BIT 6 #define TRACER_ACCESSED BIT(TRACER_ACCESSED_BIT) #define TRACER_RUNNING BIT(TRACER_RUNNING_BIT) #define TRACER_CYCLE_ACC BIT(TRACER_CYCLE_ACC_BIT) #define TRACER_TRACE_DATA BIT(TRACER_TRACE_DATA_BIT) #define TRACER_TIMESTAMP BIT(TRACER_TIMESTAMP_BIT) #define TRACER_BRANCHOUTPUT BIT(TRACER_BRANCHOUTPUT_BIT) +#define TRACER_RETURN_STACK BIT(TRACER_RETURN_STACK_BIT) #define TRACER_TIMEOUT 10000 @@ -62,6 +64,7 @@ #define ETMCTRL_BRANCH_OUTPUT (1 << 8) #define ETMCTRL_CYCLEACCURATE (1 << 12) #define ETMCTRL_TIMESTAMP_EN (1 << 28) +#define ETMCTRL_RETURN_STACK_EN (1 << 29) /* ETM configuration code register */ #define ETMR_CONFCODE (0x04) @@ -136,6 +139,7 @@ #define ETMIDR_VERSION_PFT_1_0 0x30 #define ETMR_CCE 0x1e8 +#define ETMCCER_RETURN_STACK_IMPLEMENTED BIT(23) #define ETMCCER_TIMESTAMPING_IMPLEMENTED BIT(22) #define ETMR_TRACEIDR 0x200 diff --git a/arch/arm/kernel/etm.c b/arch/arm/kernel/etm.c index 02e500b3c79d..a634e0ef6d1f 100644 --- a/arch/arm/kernel/etm.c +++ b/arch/arm/kernel/etm.c @@ -124,6 +124,9 @@ static int trace_start_etm(struct tracectx *t, int id) if (t->flags & TRACER_TIMESTAMP) v |= ETMCTRL_TIMESTAMP_EN; + if (t->flags & TRACER_RETURN_STACK) + v |= ETMCTRL_RETURN_STACK_EN; + etm_unlock(t, id); etm_writel(t, id, v, ETMR_CTRL); @@ -700,10 +703,13 @@ static ssize_t trace_branch_output_store(struct kobject *kobj, return -EINVAL; mutex_lock(&tracer.mutex); - if (branch_output) + if (branch_output) { tracer.flags |= TRACER_BRANCHOUTPUT; - else + /* Branch broadcasting is incompatible with the return stack */ + tracer.flags &= ~TRACER_RETURN_STACK; + } else { tracer.flags &= ~TRACER_BRANCHOUTPUT; + } mutex_unlock(&tracer.mutex); return n; @@ -713,6 +719,39 @@ static struct kobj_attribute trace_branch_output_attr = __ATTR(trace_branch_output, 0644, trace_branch_output_show, trace_branch_output_store); +static ssize_t trace_return_stack_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(tracer.flags & TRACER_RETURN_STACK)); +} + +static ssize_t trace_return_stack_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned int return_stack; + + if (sscanf(buf, "%u", &return_stack) != 1) + return -EINVAL; + + mutex_lock(&tracer.mutex); + if (return_stack) { + tracer.flags |= TRACER_RETURN_STACK; + /* Return stack is incompatible with branch broadcasting */ + tracer.flags &= ~TRACER_BRANCHOUTPUT; + } else { + tracer.flags &= ~TRACER_RETURN_STACK; + } + mutex_unlock(&tracer.mutex); + + return n; +} + +static struct kobj_attribute trace_return_stack_attr = + __ATTR(trace_return_stack, 0644, + trace_return_stack_show, trace_return_stack_store); + static ssize_t trace_timestamp_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -897,6 +936,14 @@ static int etm_probe(struct amba_device *dev, const struct amba_id *id) dev_dbg(&dev->dev, "Failed to create trace_branch_output in sysfs\n"); + if (etmccer & ETMCCER_RETURN_STACK_IMPLEMENTED) { + ret = sysfs_create_file(&dev->dev.kobj, + &trace_return_stack_attr.attr); + if (ret) + dev_dbg(&dev->dev, + "Failed to create trace_return_stack in sysfs\n"); + } + if (etmccer & ETMCCER_TIMESTAMPING_IMPLEMENTED) { ret = sysfs_create_file(&dev->dev.kobj, &trace_timestamp_attr.attr); From b38bb3811f730deeb3d20dab47f960b825b41b41 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 13 Apr 2012 19:22:35 -0700 Subject: [PATCH 120/358] netfilter: qtaguid: initialize a local var to keep compiler happy. There was a case that might have seemed like new_tag_stat was not initialized and actually used. Added comment explaining why it was impossible, and a BUG() in case the logic gets changed. Change-Id: I1eddd1b6f754c08a3bf89f7e9427e5dce1dfb081 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index b0a221806878..3d3928291efb 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1265,7 +1265,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, struct data_counters *uid_tag_counters; struct sock_tag *sock_tag_entry; struct iface_stat *iface_entry; - struct tag_stat *new_tag_stat; + struct tag_stat *new_tag_stat = NULL; MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", ifname, uid, sk, direction, proto, bytes); @@ -1330,8 +1330,19 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, } if (acct_tag) { + /* Create the child {acct_tag, uid_tag} and hook up parent. */ new_tag_stat = create_if_tag_stat(iface_entry, tag); new_tag_stat->parent_counters = uid_tag_counters; + } else { + /* + * For new_tag_stat to be still NULL here would require: + * {0, uid_tag} exists + * and {acct_tag, uid_tag} doesn't exist + * AND acct_tag == 0. + * Impossible. This reassures us that new_tag_stat + * below will always be assigned. + */ + BUG_ON(!new_tag_stat); } tag_stat_update(new_tag_stat, direction, proto, bytes); spin_unlock_bh(&iface_entry->tag_stat_list_lock); From e58ac65571f02eac9e832ea4d03e447d109ea8b0 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 17 Apr 2012 16:00:07 -0700 Subject: [PATCH 121/358] netfilter: xt_qtaguid: fix ipv6 protocol lookup When updating the stats for a given uid it would incorrectly assume IPV4 and pick up the wrong protocol when IPV6. Change-Id: Iea4a635012b4123bf7aa93809011b7b2040bb3d5 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 39 +++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 3d3928291efb..2c1170f89d0f 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -26,6 +26,10 @@ #include #include +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +#include +#endif + #include #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" @@ -1546,6 +1550,27 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return sk; } +static int ipx_proto(const struct sk_buff *skb, + struct xt_action_param *par) +{ + int thoff = 0, tproto; + + switch (par->family) { + case NFPROTO_IPV6: + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); + if (tproto < 0) + MT_DEBUG("%s(): transport header not found in ipv6" + " skb=%p\n", __func__, skb); + break; + case NFPROTO_IPV4: + tproto = ip_hdr(skb)->protocol; + break; + default: + tproto = IPPROTO_RAW; + } + return tproto; +} + static void account_for_uid(const struct sk_buff *skb, const struct sock *alternate_sk, uid_t uid, struct xt_action_param *par) @@ -1572,15 +1597,15 @@ static void account_for_uid(const struct sk_buff *skb, } else if (unlikely(!el_dev->name)) { pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); } else { - MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n", - par->hooknum, - el_dev->name, - el_dev->type); + int proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); if_tag_stat_update(el_dev->name, uid, skb->sk ? skb->sk : alternate_sk, par->in ? IFS_RX : IFS_TX, - ip_hdr(skb)->protocol, skb->len); + proto, skb->len); } } @@ -1625,8 +1650,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } else { atomic64_inc(&qtu_events.match_found_sk); } - MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", - par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", + par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); if (sk != NULL) { MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", par->hooknum, sk, sk->sk_socket, From 76d74018bd0ccd1d8ea8455cf91d4104c948789f Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 16 Feb 2012 16:27:59 -0800 Subject: [PATCH 122/358] cpufreq interactive governor: event tracing Change-Id: Ic13614a3da2faa2d4bd215ca3eb7191614f0cf66 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 19 ++++- include/trace/events/cpufreq_interactive.h | 87 ++++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 include/trace/events/cpufreq_interactive.h diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 8fc147c66f95..62e7456ae509 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -30,6 +30,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + #include static atomic_t active_count = ATOMIC_INIT(0); @@ -182,7 +185,11 @@ static void cpufreq_interactive_timer(unsigned long data) new_freq = pcpu->freq_table[index].frequency; if (pcpu->target_freq == new_freq) + { + trace_cpufreq_interactive_already(data, cpu_load, + pcpu->target_freq, new_freq); goto rearm_if_notmax; + } /* * Do not scale down unless we have been at this frequency for the @@ -190,10 +197,16 @@ static void cpufreq_interactive_timer(unsigned long data) */ if (new_freq < pcpu->target_freq) { if (pcpu->timer_run_time - pcpu->freq_change_time - < min_sample_time) + < min_sample_time) { + trace_cpufreq_interactive_notyet(data, cpu_load, + pcpu->target_freq, new_freq); goto rearm; + } } + trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, + new_freq); + if (new_freq < pcpu->target_freq) { pcpu->target_freq = new_freq; spin_lock_irqsave(&down_cpumask_lock, flags); @@ -381,6 +394,8 @@ static int cpufreq_interactive_up_task(void *data) pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); + trace_cpufreq_interactive_up(cpu, pcpu->target_freq, + pcpu->policy->cur); } } @@ -427,6 +442,8 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); + trace_cpufreq_interactive_down(cpu, pcpu->target_freq, + pcpu->policy->cur); } } diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h new file mode 100644 index 000000000000..3a90d3d609ba --- /dev/null +++ b/include/trace/events/cpufreq_interactive.h @@ -0,0 +1,87 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cpufreq_interactive + +#if !defined(_TRACE_CPUFREQ_INTERACTIVE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_CPUFREQ_INTERACTIVE_H + +#include + +DECLARE_EVENT_CLASS(set, + TP_PROTO(u32 cpu_id, unsigned long targfreq, + unsigned long actualfreq), + TP_ARGS(cpu_id, targfreq, actualfreq), + + TP_STRUCT__entry( + __field( u32, cpu_id ) + __field(unsigned long, targfreq ) + __field(unsigned long, actualfreq ) + ), + + TP_fast_assign( + __entry->cpu_id = (u32) cpu_id; + __entry->targfreq = targfreq; + __entry->actualfreq = actualfreq; + ), + + TP_printk("cpu=%u targ=%lu actual=%lu", + __entry->cpu_id, __entry->targfreq, + __entry->actualfreq) +); + +DEFINE_EVENT(set, cpufreq_interactive_up, + TP_PROTO(u32 cpu_id, unsigned long targfreq, + unsigned long actualfreq), + TP_ARGS(cpu_id, targfreq, actualfreq) +); + +DEFINE_EVENT(set, cpufreq_interactive_down, + TP_PROTO(u32 cpu_id, unsigned long targfreq, + unsigned long actualfreq), + TP_ARGS(cpu_id, targfreq, actualfreq) +); + +DECLARE_EVENT_CLASS(loadeval, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curfreq, unsigned long targfreq), + TP_ARGS(cpu_id, load, curfreq, targfreq), + + TP_STRUCT__entry( + __field(unsigned long, cpu_id ) + __field(unsigned long, load ) + __field(unsigned long, curfreq ) + __field(unsigned long, targfreq ) + ), + + TP_fast_assign( + __entry->cpu_id = cpu_id; + __entry->load = load; + __entry->curfreq = curfreq; + __entry->targfreq = targfreq; + ), + + TP_printk("cpu=%lu load=%lu cur=%lu targ=%lu", + __entry->cpu_id, __entry->load, __entry->curfreq, + __entry->targfreq) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_target, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curfreq, unsigned long targfreq), + TP_ARGS(cpu_id, load, curfreq, targfreq) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_already, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curfreq, unsigned long targfreq), + TP_ARGS(cpu_id, load, curfreq, targfreq) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curfreq, unsigned long targfreq), + TP_ARGS(cpu_id, load, curfreq, targfreq) +); +#endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ + +/* This part must be outside protection */ +#include From 9940a8609ea43ae791e1f6702b40a7ef262c9b09 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 6 Apr 2012 01:13:09 -0700 Subject: [PATCH 123/358] cpufreq: interactive: apply intermediate load to max speed not current Evaluate spikes in load (below go_hispeed_load) against the maximum speed supported by the device, not the current speed (which tends to make it too difficult to raise speed to intermediate levels until very busy). Change-Id: Ib937006abf8bedb60891a739acd733e89b732ae0 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 62e7456ae509..e3bbe820bb01 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -171,7 +171,7 @@ static void cpufreq_interactive_timer(unsigned long data) else new_freq = pcpu->policy->max * cpu_load / 100; } else { - new_freq = pcpu->policy->cur * cpu_load / 100; + new_freq = pcpu->policy->max * cpu_load / 100; } if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, From 071f6b174b21add012a75a1acb4f7ccdf994c3d4 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 6 Apr 2012 19:50:12 -0700 Subject: [PATCH 124/358] cpufreq: interactive: set at least hispeed when above hispeed load If load is above go_hispeed_load, always go to at least hispeed_freq, even when reducing speed from a higher speed, not just when jumping up from minimum speed. Avoids running at a lower than intended speed after a burst of even higher load. Change-Id: I5b9d2a15ba25ce609b21bac7c724265cf6838dee Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index e3bbe820bb01..12345c7bec28 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -166,10 +166,14 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = load_since_change; if (cpu_load >= go_hispeed_load) { - if (pcpu->policy->cur == pcpu->policy->min) + if (pcpu->policy->cur == pcpu->policy->min) { new_freq = hispeed_freq; - else + } else { new_freq = pcpu->policy->max * cpu_load / 100; + + if (new_freq < hispeed_freq) + new_freq = hispeed_freq; + } } else { new_freq = pcpu->policy->max * cpu_load / 100; } From 113b4ba300031cbf69c6356b7a7be92cd9903b45 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 6 Apr 2012 19:59:36 -0700 Subject: [PATCH 125/358] cpufreq: interactive: don't drop speed if recently at higher load Apply min_sample_time to the last time the current target speed was originally requested or re-validated as appropriate for the current load, not to the time since the current speed was originally set. Avoids periodic dips in speed during bursty loads. Change-Id: I250bda657985de60373f9897cc41f480664d51a1 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 38 ++++++++++++--------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 12345c7bec28..59e7779a4a09 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -44,8 +44,8 @@ struct cpufreq_interactive_cpuinfo { u64 idle_exit_time; u64 timer_run_time; int idling; - u64 freq_change_time; - u64 freq_change_time_in_idle; + u64 target_set_time; + u64 target_set_time_in_idle; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; @@ -148,8 +148,9 @@ static void cpufreq_interactive_timer(unsigned long data) else cpu_load = 100 * (delta_time - delta_idle) / delta_time; - delta_idle = (unsigned int)(now_idle - pcpu->freq_change_time_in_idle); - delta_time = (unsigned int)(pcpu->timer_run_time - pcpu->freq_change_time); + delta_idle = (unsigned int)(now_idle - pcpu->target_set_time_in_idle); + delta_time = (unsigned int)(pcpu->timer_run_time - + pcpu->target_set_time); if ((delta_time == 0) || (delta_idle > delta_time)) load_since_change = 0; @@ -188,19 +189,12 @@ static void cpufreq_interactive_timer(unsigned long data) new_freq = pcpu->freq_table[index].frequency; - if (pcpu->target_freq == new_freq) - { - trace_cpufreq_interactive_already(data, cpu_load, - pcpu->target_freq, new_freq); - goto rearm_if_notmax; - } - /* * Do not scale down unless we have been at this frequency for the * minimum sample time. */ if (new_freq < pcpu->target_freq) { - if (pcpu->timer_run_time - pcpu->freq_change_time + if (pcpu->timer_run_time - pcpu->target_set_time < min_sample_time) { trace_cpufreq_interactive_notyet(data, cpu_load, pcpu->target_freq, new_freq); @@ -208,6 +202,15 @@ static void cpufreq_interactive_timer(unsigned long data) } } + pcpu->target_set_time_in_idle = now_idle; + pcpu->target_set_time = pcpu->timer_run_time; + + if (pcpu->target_freq == new_freq) { + trace_cpufreq_interactive_already(data, cpu_load, + pcpu->target_freq, new_freq); + goto rearm_if_notmax; + } + trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, new_freq); @@ -394,10 +397,6 @@ static int cpufreq_interactive_up_task(void *data) max_freq, CPUFREQ_RELATION_H); mutex_unlock(&set_speed_lock); - - pcpu->freq_change_time_in_idle = - get_cpu_idle_time_us(cpu, - &pcpu->freq_change_time); trace_cpufreq_interactive_up(cpu, pcpu->target_freq, pcpu->policy->cur); } @@ -443,9 +442,6 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) CPUFREQ_RELATION_H); mutex_unlock(&set_speed_lock); - pcpu->freq_change_time_in_idle = - get_cpu_idle_time_us(cpu, - &pcpu->freq_change_time); trace_cpufreq_interactive_down(cpu, pcpu->target_freq, pcpu->policy->cur); } @@ -575,9 +571,9 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->policy = policy; pcpu->target_freq = policy->cur; pcpu->freq_table = freq_table; - pcpu->freq_change_time_in_idle = + pcpu->target_set_time_in_idle = get_cpu_idle_time_us(j, - &pcpu->freq_change_time); + &pcpu->target_set_time); pcpu->governor_enabled = 1; smp_wmb(); } From 021b50b5595d21e2d8d8ca6a4388b39d3e347718 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 13 Apr 2012 20:18:02 -0700 Subject: [PATCH 126/358] cpufreq: interactive: configurable delay before raising above hispeed Change-Id: I4d6ac40b23a3790d48e30c37408284e9f955e8fa Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 59e7779a4a09..154f7bd330b2 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -83,6 +83,13 @@ static unsigned long min_sample_time; #define DEFAULT_TIMER_RATE 20 * USEC_PER_MSEC static unsigned long timer_rate; +/* + * Wait this long before raising speed above hispeed, by default a single + * timer interval. + */ +#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE +static unsigned long above_hispeed_delay_val; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -174,6 +181,16 @@ static void cpufreq_interactive_timer(unsigned long data) if (new_freq < hispeed_freq) new_freq = hispeed_freq; + + if (pcpu->target_freq == hispeed_freq && + new_freq > hispeed_freq && + pcpu->timer_run_time - pcpu->target_set_time + < above_hispeed_delay_val) { + trace_cpufreq_interactive_notyet(data, cpu_load, + pcpu->target_freq, + new_freq); + goto rearm; + } } } else { new_freq = pcpu->policy->max * cpu_load / 100; @@ -515,6 +532,28 @@ static ssize_t store_min_sample_time(struct kobject *kobj, static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, show_min_sample_time, store_min_sample_time); +static ssize_t show_above_hispeed_delay(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", above_hispeed_delay_val); +} + +static ssize_t store_above_hispeed_delay(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + above_hispeed_delay_val = val; + return count; +} + +define_one_global_rw(above_hispeed_delay); + static ssize_t show_timer_rate(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -540,6 +579,7 @@ static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, static struct attribute *interactive_attributes[] = { &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, + &above_hispeed_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, NULL, @@ -660,6 +700,7 @@ static int __init cpufreq_interactive_init(void) go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY; timer_rate = DEFAULT_TIMER_RATE; /* Initalize per-cpu timers */ From 9d0dabc0581e65091c55dbcde0f6372e8f0bddd2 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 17 Apr 2012 17:39:34 -0700 Subject: [PATCH 127/358] cpufreq: interactive: adjust code and documentation to match Change-Id: If59c668d514a29febe5c35404fd9d01df8548eb1 Signed-off-by: Todd Poynor --- Documentation/cpu-freq/governors.txt | 16 +++++++++++++--- drivers/cpufreq/cpufreq_interactive.c | 6 +++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index 5726f1563640..64faeb1a3458 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -249,11 +249,21 @@ frequency before ramping down. This is to ensure that the governor has seen enough historic cpu load data to determine the appropriate workload. Default is 80000 uS. -go_maxspeed_load: The CPU load at which to ramp to max speed. Default -is 85. +hispeed_freq: An intermediate "hi speed" at which to initially ramp +when CPU load hits the value specified in go_hispeed_load. If load +stays high for the amount of time specified in above_hispeed_delay, +then speed may be bumped higher. Default is maximum speed. + +go_hispeed_load: The CPU load at which to ramp to the intermediate "hi +speed". Default is 85%. + +above_hispeed_delay: Once speed is set to hispeed_freq, wait for this +long before bumping speed higher in response to continued high load. +Default is 20000 uS. timer_rate: Sample rate for reevaluating cpu load when the system is -not idle. Default is 30000 uS. +not idle. Default is 20000 uS. + 3. The Governor Interface in the CPUfreq Core ============================================= diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 154f7bd330b2..bf8d7727311f 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -68,19 +68,19 @@ static struct mutex set_speed_lock; static u64 hispeed_freq; /* Go to hi speed when CPU load at or above this value. */ -#define DEFAULT_GO_HISPEED_LOAD 95 +#define DEFAULT_GO_HISPEED_LOAD 85 static unsigned long go_hispeed_load; /* * The minimum amount of time to spend at a frequency before we can ramp down. */ -#define DEFAULT_MIN_SAMPLE_TIME 20 * USEC_PER_MSEC +#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) static unsigned long min_sample_time; /* * The sample rate of the timer used to increase frequency */ -#define DEFAULT_TIMER_RATE 20 * USEC_PER_MSEC +#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) static unsigned long timer_rate; /* From 47549fc2d30156de539816002e5955a6a7810c69 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 19 Apr 2012 12:52:48 -0700 Subject: [PATCH 128/358] cpufreq: interactive: base hispeed bump on target freq, not actual For systems that set a common speed for all CPUs, checking current speed here could bypass the intermediate hispeed bump decision for this CPU when another CPU was already at hispeed. This could result in an overly high setting (for all CPUs) in situations where all CPUs were about to drop to load levels that map to hispeed or below. Change-Id: I186f23dcfc5e2b6336cab8b0327f0c8a9a4482bc Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index bf8d7727311f..b78174fd8cff 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -174,7 +174,7 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = load_since_change; if (cpu_load >= go_hispeed_load) { - if (pcpu->policy->cur == pcpu->policy->min) { + if (pcpu->target_freq <= pcpu->policy->min) { new_freq = hispeed_freq; } else { new_freq = pcpu->policy->max * cpu_load / 100; From 7eb37df06f0780a1af990677d104949582c60c8c Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 20 Apr 2012 13:18:32 -0700 Subject: [PATCH 129/358] cpufreq: interactive: Separate speed target revalidate time and initial set time Allow speed drop after min_sample_time elapses from last time the current speed was last re-validated as appropriate for current load / input boost. Allow speed bump after min_sample_time (or above_hispeed_delay) elapses from the time the current speed was originally set. Change-Id: Ic25687a7a53d25e6544c30c47d7ab6f27a47bee8 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index b78174fd8cff..083f79032b45 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -46,6 +46,8 @@ struct cpufreq_interactive_cpuinfo { int idling; u64 target_set_time; u64 target_set_time_in_idle; + u64 target_validate_time; + u64 target_validate_time_in_idle; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; @@ -208,10 +210,10 @@ static void cpufreq_interactive_timer(unsigned long data) /* * Do not scale down unless we have been at this frequency for the - * minimum sample time. + * minimum sample time since last validated. */ if (new_freq < pcpu->target_freq) { - if (pcpu->timer_run_time - pcpu->target_set_time + if (pcpu->timer_run_time - pcpu->target_validate_time < min_sample_time) { trace_cpufreq_interactive_notyet(data, cpu_load, pcpu->target_freq, new_freq); @@ -219,8 +221,8 @@ static void cpufreq_interactive_timer(unsigned long data) } } - pcpu->target_set_time_in_idle = now_idle; - pcpu->target_set_time = pcpu->timer_run_time; + pcpu->target_validate_time_in_idle = now_idle; + pcpu->target_validate_time = pcpu->timer_run_time; if (pcpu->target_freq == new_freq) { trace_cpufreq_interactive_already(data, cpu_load, @@ -230,6 +232,8 @@ static void cpufreq_interactive_timer(unsigned long data) trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, new_freq); + pcpu->target_set_time_in_idle = now_idle; + pcpu->target_set_time = pcpu->timer_run_time; if (new_freq < pcpu->target_freq) { pcpu->target_freq = new_freq; @@ -614,6 +618,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->target_set_time_in_idle = get_cpu_idle_time_us(j, &pcpu->target_set_time); + pcpu->target_validate_time = + pcpu->target_set_time; + pcpu->target_validate_time_in_idle = + pcpu->target_set_time_in_idle; pcpu->governor_enabled = 1; smp_wmb(); } From f3f36fd96bef9687356f37e065b1713d5f7cb254 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 2 Apr 2012 17:17:14 -0700 Subject: [PATCH 130/358] cpufreq: interactive: Boost frequency on touchscreen input Based on previous patches by Tero Kristo , Brian Steuer , David Ng , Antti P Miettinen , and Thomas Renninger Change-Id: Ic55fedcf6f9310f43a7022fb88e23b0392122769 Signed-off-by: Todd Poynor --- Documentation/cpu-freq/governors.txt | 3 + drivers/cpufreq/cpufreq_interactive.c | 164 ++++++++++++++++++++- include/trace/events/cpufreq_interactive.h | 12 ++ 3 files changed, 178 insertions(+), 1 deletion(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index 64faeb1a3458..d486981a8122 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -264,6 +264,9 @@ Default is 20000 uS. timer_rate: Sample rate for reevaluating cpu load when the system is not idle. Default is 20000 uS. +input_boost: If non-zero, boost speed of all CPUs to hispeed_freq on +touchscreen activity. Default is 0. + 3. The Governor Interface in the CPUfreq Core ============================================= diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 083f79032b45..1387cd5d2a6b 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #define CREATE_TRACE_POINTS #include @@ -92,6 +94,19 @@ static unsigned long timer_rate; #define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE static unsigned long above_hispeed_delay_val; +/* + * Boost to hispeed on touchscreen input. + */ + +static int input_boost_val; + +struct cpufreq_interactive_inputopen { + struct input_handle *handle; + struct work_struct inputopen_work; +}; + +static struct cpufreq_interactive_inputopen inputopen; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -468,6 +483,125 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) } } +static void cpufreq_interactive_boost(void) +{ + int i; + int anyboost = 0; + unsigned long flags; + struct cpufreq_interactive_cpuinfo *pcpu; + + trace_cpufreq_interactive_boost(hispeed_freq); + spin_lock_irqsave(&up_cpumask_lock, flags); + + for_each_online_cpu(i) { + pcpu = &per_cpu(cpuinfo, i); + + if (pcpu->target_freq < hispeed_freq) { + pcpu->target_freq = hispeed_freq; + cpumask_set_cpu(i, &up_cpumask); + pcpu->target_set_time_in_idle = + get_cpu_idle_time_us(i, &pcpu->target_set_time); + anyboost = 1; + } + + /* + * Refresh time at which current (possibly being + * boosted) speed last validated (reset timer for + * allowing speed to drop). + */ + + pcpu->target_validate_time_in_idle = + get_cpu_idle_time_us(i, &pcpu->target_validate_time); + } + + spin_unlock_irqrestore(&up_cpumask_lock, flags); + + if (anyboost) + wake_up_process(up_task); +} + +static void cpufreq_interactive_input_event(struct input_handle *handle, + unsigned int type, + unsigned int code, int value) +{ + if (input_boost_val && type == EV_SYN && code == SYN_REPORT) + cpufreq_interactive_boost(); +} + +static void cpufreq_interactive_input_open(struct work_struct *w) +{ + struct cpufreq_interactive_inputopen *io = + container_of(w, struct cpufreq_interactive_inputopen, + inputopen_work); + int error; + + error = input_open_device(io->handle); + if (error) + input_unregister_handle(io->handle); +} + +static int cpufreq_interactive_input_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + pr_info("%s: connect to %s\n", __func__, dev->name); + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "cpufreq_interactive"; + + error = input_register_handle(handle); + if (error) + goto err; + + inputopen.handle = handle; + queue_work(down_wq, &inputopen.inputopen_work); + return 0; +err: + kfree(handle); + return error; +} + +static void cpufreq_interactive_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id cpufreq_interactive_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, + .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = + BIT_MASK(ABS_MT_POSITION_X) | + BIT_MASK(ABS_MT_POSITION_Y) }, + }, /* multi-touch touchscreen */ + { + .flags = INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + .absbit = { [BIT_WORD(ABS_X)] = + BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, + }, /* touchpad */ + { }, +}; + +static struct input_handler cpufreq_interactive_input_handler = { + .event = cpufreq_interactive_input_event, + .connect = cpufreq_interactive_input_connect, + .disconnect = cpufreq_interactive_input_disconnect, + .name = "cpufreq_interactive", + .id_table = cpufreq_interactive_ids, +}; + static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -580,12 +714,34 @@ static ssize_t store_timer_rate(struct kobject *kobj, static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, show_timer_rate, store_timer_rate); +static ssize_t show_input_boost(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", input_boost_val); +} + +static ssize_t store_input_boost(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + input_boost_val = val; + return count; +} + +define_one_global_rw(input_boost); + static struct attribute *interactive_attributes[] = { &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, &above_hispeed_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, + &input_boost.attr, NULL, }; @@ -641,6 +797,11 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (rc) return rc; + rc = input_register_handler(&cpufreq_interactive_input_handler); + if (rc) + pr_warn("%s: failed to register input handler\n", + __func__); + break; case CPUFREQ_GOV_STOP: @@ -663,6 +824,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (atomic_dec_return(&active_count) > 0) return 0; + input_unregister_handler(&cpufreq_interactive_input_handler); sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); @@ -742,7 +904,7 @@ static int __init cpufreq_interactive_init(void) mutex_init(&set_speed_lock); idle_notifier_register(&cpufreq_interactive_idle_nb); - + INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); return cpufreq_register_governor(&cpufreq_gov_interactive); err_freeuptask: diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index 3a90d3d609ba..19e070b897ac 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -81,6 +81,18 @@ DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, unsigned long curfreq, unsigned long targfreq), TP_ARGS(cpu_id, load, curfreq, targfreq) ); + +TRACE_EVENT(cpufreq_interactive_boost, + TP_PROTO(unsigned long freq), + TP_ARGS(freq), + TP_STRUCT__entry( + __field(unsigned long, freq) + ), + TP_fast_assign( + __entry->freq = freq; + ), + TP_printk("freq=%lu", __entry->freq) +); #endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ /* This part must be outside protection */ From cfac056b755e9e5d46730b5e1f0603c7955a1658 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 23 Apr 2012 21:22:45 -0700 Subject: [PATCH 131/358] cpufreq: interactive: remove unused target_validate_time_in_idle Change-Id: I37c5085b91318242612440dfd775ad762996612f Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 1387cd5d2a6b..90db2c30e2f9 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -49,7 +49,6 @@ struct cpufreq_interactive_cpuinfo { u64 target_set_time; u64 target_set_time_in_idle; u64 target_validate_time; - u64 target_validate_time_in_idle; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; @@ -236,7 +235,6 @@ static void cpufreq_interactive_timer(unsigned long data) } } - pcpu->target_validate_time_in_idle = now_idle; pcpu->target_validate_time = pcpu->timer_run_time; if (pcpu->target_freq == new_freq) { @@ -510,8 +508,7 @@ static void cpufreq_interactive_boost(void) * allowing speed to drop). */ - pcpu->target_validate_time_in_idle = - get_cpu_idle_time_us(i, &pcpu->target_validate_time); + pcpu->target_validate_time = ktime_to_us(ktime_get()); } spin_unlock_irqrestore(&up_cpumask_lock, flags); @@ -776,8 +773,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, &pcpu->target_set_time); pcpu->target_validate_time = pcpu->target_set_time; - pcpu->target_validate_time_in_idle = - pcpu->target_set_time_in_idle; pcpu->governor_enabled = 1; smp_wmb(); } From e0a60f8b418a3380716c94afe76ec3fc436c569a Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 23 Apr 2012 20:42:41 -0700 Subject: [PATCH 132/358] cpufreq: interactive: Add sysfs boost interface for hints from userspace The explicit hint on/off version. Change-Id: Ibf62b6d45bf6fb8c9c055b9bdaf074ce9374c04f Signed-off-by: Todd Poynor --- Documentation/cpu-freq/governors.txt | 3 ++ drivers/cpufreq/cpufreq_interactive.c | 48 ++++++++++++++++++++-- include/trace/events/cpufreq_interactive.h | 13 ++++++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index d486981a8122..820deceb9f8d 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -267,6 +267,9 @@ not idle. Default is 20000 uS. input_boost: If non-zero, boost speed of all CPUs to hispeed_freq on touchscreen activity. Default is 0. +boost: If non-zero, immediately boost speed of all CPUs to +hispeed_freq. If zero, allow CPU speeds to drop below hispeed_freq. + 3. The Governor Interface in the CPUfreq Core ============================================= diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 90db2c30e2f9..07b7cafaffdd 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -31,12 +31,11 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include -#include - static atomic_t active_count = ATOMIC_INIT(0); struct cpufreq_interactive_cpuinfo { @@ -94,7 +93,7 @@ static unsigned long timer_rate; static unsigned long above_hispeed_delay_val; /* - * Boost to hispeed on touchscreen input. + * Boost pulse to hispeed on touchscreen input. */ static int input_boost_val; @@ -106,6 +105,12 @@ struct cpufreq_interactive_inputopen { static struct cpufreq_interactive_inputopen inputopen; +/* + * Non-zero means longer-term speed boost active. + */ + +static int boost_val; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -189,7 +194,7 @@ static void cpufreq_interactive_timer(unsigned long data) if (load_since_change > cpu_load) cpu_load = load_since_change; - if (cpu_load >= go_hispeed_load) { + if (cpu_load >= go_hispeed_load || boost_val) { if (pcpu->target_freq <= pcpu->policy->min) { new_freq = hispeed_freq; } else { @@ -517,6 +522,12 @@ static void cpufreq_interactive_boost(void) wake_up_process(up_task); } +/* + * Pulsed boost on input event raises CPUs to hispeed_freq and lets + * usual algorithm of min_sample_time decide when to allow speed + * to drop. + */ + static void cpufreq_interactive_input_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) @@ -732,6 +743,34 @@ static ssize_t store_input_boost(struct kobject *kobj, struct attribute *attr, define_one_global_rw(input_boost); +static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", boost_val); +} + +static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + boost_val = val; + + if (boost_val) + cpufreq_interactive_boost(); + else + trace_cpufreq_interactive_unboost(hispeed_freq); + + return count; +} + +define_one_global_rw(boost); + static struct attribute *interactive_attributes[] = { &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, @@ -739,6 +778,7 @@ static struct attribute *interactive_attributes[] = { &min_sample_time_attr.attr, &timer_rate_attr.attr, &input_boost.attr, + &boost.attr, NULL, }; diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index 19e070b897ac..ae6f232e498f 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -93,6 +93,19 @@ TRACE_EVENT(cpufreq_interactive_boost, ), TP_printk("freq=%lu", __entry->freq) ); + +TRACE_EVENT(cpufreq_interactive_unboost, + TP_PROTO(unsigned long freq), + TP_ARGS(freq), + TP_STRUCT__entry( + __field(unsigned long, freq) + ), + TP_fast_assign( + __entry->freq = freq; + ), + TP_printk("freq=%lu", __entry->freq) +); + #endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ /* This part must be outside protection */ From 69b3f51ce463974dfd11c64c4d1ebd3a90ace3dc Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 26 Apr 2012 21:41:40 -0700 Subject: [PATCH 133/358] cpufreq: interactive: set floor for boosted speed Allow speed to drop to flooor frequency but not below, don't pin to speed at last boost. Change-Id: I0147c2b7a2e61ba16820605af6baaf09570be787 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 07b7cafaffdd..fcd5b1d81fb6 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -47,10 +47,11 @@ struct cpufreq_interactive_cpuinfo { int idling; u64 target_set_time; u64 target_set_time_in_idle; - u64 target_validate_time; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; + unsigned int floor_freq; + u64 floor_validate_time; int governor_enabled; }; @@ -228,10 +229,10 @@ static void cpufreq_interactive_timer(unsigned long data) new_freq = pcpu->freq_table[index].frequency; /* - * Do not scale down unless we have been at this frequency for the - * minimum sample time since last validated. + * Do not scale below floor_freq unless we have been at or above the + * floor frequency for the minimum sample time since last validated. */ - if (new_freq < pcpu->target_freq) { + if (new_freq < pcpu->floor_freq) { if (pcpu->timer_run_time - pcpu->target_validate_time < min_sample_time) { trace_cpufreq_interactive_notyet(data, cpu_load, @@ -240,7 +241,8 @@ static void cpufreq_interactive_timer(unsigned long data) } } - pcpu->target_validate_time = pcpu->timer_run_time; + pcpu->floor_freq = new_freq; + pcpu->floor_validate_time = pcpu->timer_run_time; if (pcpu->target_freq == new_freq) { trace_cpufreq_interactive_already(data, cpu_load, @@ -508,12 +510,12 @@ static void cpufreq_interactive_boost(void) } /* - * Refresh time at which current (possibly being - * boosted) speed last validated (reset timer for - * allowing speed to drop). + * Set floor freq and (re)start timer for when last + * validated. */ - pcpu->target_validate_time = ktime_to_us(ktime_get()); + pcpu->floor_freq = hispeed_freq; + pcpu->floor_validate_time = ktime_to_us(ktime_get()); } spin_unlock_irqrestore(&up_cpumask_lock, flags); @@ -811,7 +813,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->target_set_time_in_idle = get_cpu_idle_time_us(j, &pcpu->target_set_time); - pcpu->target_validate_time = + pcpu->floor_freq = pcpu->target_freq; + pcpu->floor_validate_time = pcpu->target_set_time; pcpu->governor_enabled = 1; smp_wmb(); From 3c04f4f550b755304192fc27856bccc6d1e29ab9 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 3 May 2012 00:16:55 -0700 Subject: [PATCH 134/358] cpufreq: interactive: add boost pulse interface Change-Id: Icf1e86d2065cc8f0816ba9c6b065eb056d4e8249 Signed-off-by: Todd Poynor --- Documentation/cpu-freq/governors.txt | 9 ++++-- drivers/cpufreq/cpufreq_interactive.c | 32 ++++++++++++++++++---- include/trace/events/cpufreq_interactive.h | 20 +++++++------- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index 820deceb9f8d..f02f8d465863 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -267,8 +267,13 @@ not idle. Default is 20000 uS. input_boost: If non-zero, boost speed of all CPUs to hispeed_freq on touchscreen activity. Default is 0. -boost: If non-zero, immediately boost speed of all CPUs to -hispeed_freq. If zero, allow CPU speeds to drop below hispeed_freq. +boost: If non-zero, immediately boost speed of all CPUs to at least +hispeed_freq until zero is written to this attribute. If zero, allow +CPU speeds to drop below hispeed_freq according to load as usual. + +boostpulse: Immediately boost speed of all CPUs to hispeed_freq for +min_sample_time, after which speeds are allowed to drop below +hispeed_freq according to load as usual. 3. The Governor Interface in the CPUfreq Core diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index fcd5b1d81fb6..fcf0f217f20f 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -495,7 +495,6 @@ static void cpufreq_interactive_boost(void) unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; - trace_cpufreq_interactive_boost(hispeed_freq); spin_lock_irqsave(&up_cpumask_lock, flags); for_each_online_cpu(i) { @@ -534,8 +533,10 @@ static void cpufreq_interactive_input_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { - if (input_boost_val && type == EV_SYN && code == SYN_REPORT) + if (input_boost_val && type == EV_SYN && code == SYN_REPORT) { + trace_cpufreq_interactive_boost("input"); cpufreq_interactive_boost(); + } } static void cpufreq_interactive_input_open(struct work_struct *w) @@ -763,16 +764,36 @@ static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, boost_val = val; - if (boost_val) + if (boost_val) { + trace_cpufreq_interactive_boost("on"); cpufreq_interactive_boost(); - else - trace_cpufreq_interactive_unboost(hispeed_freq); + } else { + trace_cpufreq_interactive_unboost("off"); + } return count; } define_one_global_rw(boost); +static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + trace_cpufreq_interactive_boost("pulse"); + cpufreq_interactive_boost(); + return count; +} + +static struct global_attr boostpulse = + __ATTR(boostpulse, 0200, NULL, store_boostpulse); + static struct attribute *interactive_attributes[] = { &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, @@ -781,6 +802,7 @@ static struct attribute *interactive_attributes[] = { &timer_rate_attr.attr, &input_boost.attr, &boost.attr, + &boostpulse.attr, NULL, }; diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index ae6f232e498f..0791f2728c1b 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -83,27 +83,27 @@ DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, ); TRACE_EVENT(cpufreq_interactive_boost, - TP_PROTO(unsigned long freq), - TP_ARGS(freq), + TP_PROTO(char *s), + TP_ARGS(s), TP_STRUCT__entry( - __field(unsigned long, freq) + __field(char *, s) ), TP_fast_assign( - __entry->freq = freq; + __entry->s = s; ), - TP_printk("freq=%lu", __entry->freq) + TP_printk("%s", __entry->s) ); TRACE_EVENT(cpufreq_interactive_unboost, - TP_PROTO(unsigned long freq), - TP_ARGS(freq), + TP_PROTO(char *s), + TP_ARGS(s), TP_STRUCT__entry( - __field(unsigned long, freq) + __field(char *, s) ), TP_fast_assign( - __entry->freq = freq; + __entry->s = s; ), - TP_printk("freq=%lu", __entry->freq) + TP_printk("%s", __entry->s) ); #endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ From cd1396b63354d81264db12b18b1ff0682be5e2e1 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 1 May 2012 14:10:31 -0700 Subject: [PATCH 135/358] cpufreq-interactive: Compile fixup Looks like AOSP has a compile bug. Fix it up. Signed-off-by: John Stultz --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index fcf0f217f20f..7cb2e641fcdc 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -233,7 +233,7 @@ static void cpufreq_interactive_timer(unsigned long data) * floor frequency for the minimum sample time since last validated. */ if (new_freq < pcpu->floor_freq) { - if (pcpu->timer_run_time - pcpu->target_validate_time + if (pcpu->timer_run_time - pcpu->floor_validate_time < min_sample_time) { trace_cpufreq_interactive_notyet(data, cpu_load, pcpu->target_freq, new_freq); From 5b67ec03985710653185bc65ccf740303c772654 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 26 Mar 2012 16:54:15 +0530 Subject: [PATCH 136/358] security: Add proper checks for Android specific capability checks Commit b641072 ("security: Add AID_NET_RAW and AID_NET_ADMIN capability check in cap_capable().") introduces additional checks for AID_NET_xxx macros. Since the header file including those macros are conditionally included, the checks should also be conditionally executed. Change-Id: Iaec5208d5b95a46b1ac3f2db8449c661e803fa5b Signed-off-by: Tushar Behera Signed-off-by: Andrey Konovalov --- security/commoncap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/commoncap.c b/security/commoncap.c index 6c4e3ea459d3..b221506fb37b 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -82,10 +82,12 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, { struct user_namespace *ns = targ_ns; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) return 0; if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) return 0; +#endif /* See if cred has the capability in the target user namespace * by examining the target user namespace and all of the target From 4f200748255116f7d3e7d91235c896767a966d0e Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 23 Mar 2012 21:48:02 +0400 Subject: [PATCH 137/358] Include if_pppolac.h and if_pppopns.h into header-y target This is required to pass the headers_check Change-Id: Ic4c773973278cbdf1cb4eb66473826cb96ccbfb3 Signed-off-by: Andrey Konovalov --- include/linux/Kbuild | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 include/linux/Kbuild diff --git a/include/linux/Kbuild b/include/linux/Kbuild new file mode 100644 index 000000000000..a4608897a5f3 --- /dev/null +++ b/include/linux/Kbuild @@ -0,0 +1,2 @@ +header-y += if_pppolac.h +header-y += if_pppopns.h From 1062f9781924f211b3f55b8d830ea3e489f5bab4 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 26 Apr 2012 23:28:35 -0700 Subject: [PATCH 138/358] netfilter: xt_IDLETIMER: Add new netlink msg type Send notifications when the label becomes active after an idle period. Send netlink message notifications in addition to sysfs notifications. Using a uevent with subsystem=xt_idletimer INTERFACE=... STATE={active,inactive} This is backport from common android-3.0 commit: beb914e987cbbd368988d2b94a6661cb907c4d5a with uevent support instead of a new netlink message type. Change-Id: I31677ef00c94b5f82c8457e5bf9e5e584c23c523 Signed-off-by: Ashish Sharma Signed-off-by: JP Abgrall --- include/uapi/linux/netfilter/xt_IDLETIMER.h | 8 +++ net/netfilter/xt_IDLETIMER.c | 78 +++++++++++++++++++-- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_IDLETIMER.h b/include/uapi/linux/netfilter/xt_IDLETIMER.h index 208ae9387331..faaa28b3d061 100644 --- a/include/uapi/linux/netfilter/xt_IDLETIMER.h +++ b/include/uapi/linux/netfilter/xt_IDLETIMER.h @@ -4,6 +4,7 @@ * Header file for Xtables timer target module. * * Copyright (C) 2004, 2010 Nokia Corporation + * * Written by Timo Teras * * Converted to x_tables and forward-ported to 2.6.34 @@ -32,12 +33,19 @@ #include #define MAX_IDLETIMER_LABEL_SIZE 28 +#define NLMSG_MAX_SIZE 64 + +#define NL_EVENT_TYPE_INACTIVE 0 +#define NL_EVENT_TYPE_ACTIVE 1 struct idletimer_tg_info { __u32 timeout; char label[MAX_IDLETIMER_LABEL_SIZE]; + /* Use netlink messages for notification in addition to sysfs */ + __u8 send_nl_msg; + /* for kernel module internal use only */ struct idletimer_tg *timer __attribute__((aligned(8))); }; diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index f407ebc13481..df91e26f55f2 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -5,6 +5,7 @@ * After timer expires a kevent will be sent. * * Copyright (C) 2004, 2010 Nokia Corporation + * * Written by Timo Teras * * Converted to x_tables and reworked for upstream inclusion @@ -38,8 +39,10 @@ #include #include #include +#include #include #include +#include struct idletimer_tg_attr { struct attribute attr; @@ -56,6 +59,8 @@ struct idletimer_tg { struct idletimer_tg_attr attr; unsigned int refcnt; + bool send_nl_msg; + bool active; }; static LIST_HEAD(idletimer_tg_list); @@ -63,6 +68,32 @@ static DEFINE_MUTEX(list_mutex); static struct kobject *idletimer_tg_kobj; +static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) +{ + char iface_msg[NLMSG_MAX_SIZE]; + char state_msg[NLMSG_MAX_SIZE]; + char *envp[] = { iface_msg, state_msg, NULL }; + int res; + + res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s", + iface); + if (NLMSG_MAX_SIZE <= res) { + pr_err("message too long (%d)", res); + return; + } + res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s", + timer->active ? "active" : "inactive"); + if (NLMSG_MAX_SIZE <= res) { + pr_err("message too long (%d)", res); + return; + } + pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); + kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); + return; + + +} + static struct idletimer_tg *__idletimer_tg_find_by_label(const char *label) { @@ -83,6 +114,7 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, { struct idletimer_tg *timer; unsigned long expires = 0; + unsigned long now = jiffies; mutex_lock(&list_mutex); @@ -92,11 +124,15 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, mutex_unlock(&list_mutex); - if (time_after(expires, jiffies)) + if (time_after(expires, now)) return sprintf(buf, "%u\n", - jiffies_to_msecs(expires - jiffies) / 1000); + jiffies_to_msecs(expires - now) / 1000); - return sprintf(buf, "0\n"); + if (timer->send_nl_msg) + return sprintf(buf, "0 %d\n", + jiffies_to_msecs(now - expires) / 1000); + else + return sprintf(buf, "0\n"); } static void idletimer_tg_work(struct work_struct *work) @@ -105,6 +141,9 @@ static void idletimer_tg_work(struct work_struct *work) work); sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name); + + if (timer->send_nl_msg) + notify_netlink_uevent(timer->attr.attr.name, timer); } static void idletimer_tg_expired(unsigned long data) @@ -113,6 +152,7 @@ static void idletimer_tg_expired(unsigned long data) pr_debug("timer %s expired\n", timer->attr.attr.name); + timer->active = false; schedule_work(&timer->work); } @@ -145,6 +185,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) setup_timer(&info->timer->timer, idletimer_tg_expired, (unsigned long) info->timer); info->timer->refcnt = 1; + info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true; + info->timer->active = true; mod_timer(&info->timer->timer, msecs_to_jiffies(info->timeout * 1000) + jiffies); @@ -168,14 +210,24 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, const struct xt_action_param *par) { const struct idletimer_tg_info *info = par->targinfo; + unsigned long now = jiffies; pr_debug("resetting timer %s, timeout period %u\n", info->label, info->timeout); BUG_ON(!info->timer); + info->timer->active = true; + + if (time_before(info->timer->timer.expires, now)) { + schedule_work(&info->timer->work); + pr_debug("Starting timer %s (Expired, Jiffies): %lu, %lu\n", + info->label, info->timer->timer.expires, now); + } + + /* TODO: Avoid modifying timers on each packet */ mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + jiffies); + msecs_to_jiffies(info->timeout * 1000) + now); return XT_CONTINUE; } @@ -184,8 +236,9 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) { struct idletimer_tg_info *info = par->targinfo; int ret; + unsigned long now = jiffies; - pr_debug("checkentry targinfo%s\n", info->label); + pr_debug("checkentry targinfo %s\n", info->label); if (info->timeout == 0) { pr_debug("timeout value is zero\n"); @@ -204,8 +257,16 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) info->timer = __idletimer_tg_find_by_label(info->label); if (info->timer) { info->timer->refcnt++; + info->timer->active = true; + + if (time_before(info->timer->timer.expires, now)) { + schedule_work(&info->timer->work); + pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", + info->timer->timer.expires, now); + } + mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + jiffies); + msecs_to_jiffies(info->timeout * 1000) + now); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); @@ -219,6 +280,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) } mutex_unlock(&list_mutex); + return 0; } @@ -240,7 +302,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) kfree(info->timer); } else { pr_debug("decreased refcnt of timer %s to %u\n", - info->label, info->timer->refcnt); + info->label, info->timer->refcnt); } mutex_unlock(&list_mutex); @@ -248,6 +310,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) static struct xt_target idletimer_tg __read_mostly = { .name = "IDLETIMER", + .revision = 1, .family = NFPROTO_UNSPEC, .target = idletimer_tg_target, .targetsize = sizeof(struct idletimer_tg_info), @@ -313,3 +376,4 @@ MODULE_DESCRIPTION("Xtables: idle time monitor"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("ipt_IDLETIMER"); MODULE_ALIAS("ip6t_IDLETIMER"); +MODULE_ALIAS("arpt_IDLETIMER"); From eb0f5edbc30f41f60141ecfb2c091a7b6126875a Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 27 Apr 2012 12:57:39 -0700 Subject: [PATCH 139/358] netfilter: xt_qtaguid: start tracking iface rx/tx at low level qtaguid tracks the device stats by monitoring when it goes up and down, then it gets the dev_stats(). But devs don't correctly report stats (either they don't count headers symmetrically between rx/tx, or they count internal control messages). Now qtaguid counts the rx/tx bytes/packets during raw:prerouting and mangle:postrouting (nat is not available in ipv6). The results are in /proc/net/xt_qtaguid/iface_stat_fmt which outputs a format line (bash expansion): ifname total_skb_{rx,tx}_{bytes,packets} Added event counters for pre/post handling. Added extra ctrl_*() pid/uid debugging. Change-Id: Id84345d544ad1dd5f63e3842cab229e71d339297 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 277 ++++++++++++++++++++++------ net/netfilter/xt_qtaguid_internal.h | 5 +- net/netfilter/xt_qtaguid_print.c | 18 +- 3 files changed, 233 insertions(+), 67 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 2c1170f89d0f..9fd0ffa6c365 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -114,8 +114,15 @@ module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); /*---------------------------------------------------------------------------*/ static const char *iface_stat_procdirname = "iface_stat"; static struct proc_dir_entry *iface_stat_procdir; +/* + * The iface_stat_all* will go away once userspace gets use to the new fields + * that have a format line. + */ static const char *iface_stat_all_procfilename = "iface_stat_all"; static struct proc_dir_entry *iface_stat_all_procfile; +static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; +static struct proc_dir_entry *iface_stat_fmt_procfile; + /* * Ordering of locks: @@ -128,9 +135,9 @@ static struct proc_dir_entry *iface_stat_all_procfile; * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock * is acquired. * - * Call tree with all lock holders as of 2011-09-25: + * Call tree with all lock holders as of 2012-04-27: * - * iface_stat_all_proc_read() + * iface_stat_fmt_proc_read() * iface_stat_list_lock * (struct iface_stat) * @@ -781,13 +788,14 @@ done: return iface_entry; } -static int iface_stat_all_proc_read(char *page, char **num_items_returned, +static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, off_t items_to_skip, int char_count, int *eof, void *data) { char *outp = page; int item_index = 0; int len; + int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */ struct iface_stat *iface_entry; struct rtnl_link_stats64 dev_stats, *stats; struct rtnl_link_stats64 no_dev_stats = {0}; @@ -797,14 +805,32 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, return 0; } - CT_DEBUG("qtaguid:proc iface_stat_all " + CT_DEBUG("qtaguid:proc iface_stat_fmt " + "pid=%u tgid=%u uid=%u " "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", page, *num_items_returned, + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, items_to_skip, char_count, *eof); if (*eof) return 0; + if (fmt == 2 && item_index++ >= items_to_skip) { + len = snprintf(outp, char_count, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets\n" + ); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + /* * This lock will prevent iface_stat_update() from changing active, * and in turn prevent an interface from unregistering itself. @@ -820,18 +846,37 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, } else { stats = &no_dev_stats; } - len = snprintf(outp, char_count, - "%s %d " - "%llu %llu %llu %llu " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->active, - iface_entry->totals[IFS_RX].bytes, - iface_entry->totals[IFS_RX].packets, - iface_entry->totals[IFS_TX].bytes, - iface_entry->totals[IFS_TX].packets, - stats->rx_bytes, stats->rx_packets, - stats->tx_bytes, stats->tx_packets); + /* + * If the meaning of the data changes, then update the fmtX + * string. + */ + if (fmt == 1) { + len = snprintf( + outp, char_count, + "%s %d " + "%llu %llu %llu %llu " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals_via_dev[IFS_RX].bytes, + iface_entry->totals_via_dev[IFS_RX].packets, + iface_entry->totals_via_dev[IFS_TX].bytes, + iface_entry->totals_via_dev[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets + ); + } else { + len = snprintf( + outp, char_count, + "%s " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->totals_via_skb[IFS_RX].bytes, + iface_entry->totals_via_skb[IFS_RX].packets, + iface_entry->totals_via_skb[IFS_TX].bytes, + iface_entry->totals_via_skb[IFS_TX].packets + ); + } if (len >= char_count) { spin_unlock_bh(&iface_stat_list_lock); *outp = '\0'; @@ -865,13 +910,17 @@ static void iface_create_proc_worker(struct work_struct *work) new_iface->proc_ptr = proc_entry; create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_TX].bytes); + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].bytes); create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_RX].bytes); + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].bytes); create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_TX].packets); + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].packets); create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_RX].packets); + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].packets); create_proc_read_entry("active", proc_iface_perms, proc_entry, read_proc_bool, &new_iface->active); @@ -975,11 +1024,13 @@ static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, "iface reset its stats unexpectedly\n", __func__, net_dev->name); - iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes; - iface->totals[IFS_TX].packets += + iface->totals_via_dev[IFS_TX].bytes += + iface->last_known[IFS_TX].bytes; + iface->totals_via_dev[IFS_TX].packets += iface->last_known[IFS_TX].packets; - iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes; - iface->totals[IFS_RX].packets += + iface->totals_via_dev[IFS_RX].bytes += + iface->last_known[IFS_RX].bytes; + iface->totals_via_dev[IFS_RX].packets += iface->last_known[IFS_RX].packets; iface->last_known_valid = false; IF_DEBUG("qtaguid: %s(%s): iface=%p " @@ -1147,6 +1198,27 @@ static struct sock_tag *get_sock_stat(const struct sock *sk) return sock_tag_entry; } +static int ipx_proto(const struct sk_buff *skb, + struct xt_action_param *par) +{ + int thoff = 0, tproto; + + switch (par->family) { + case NFPROTO_IPV6: + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); + if (tproto < 0) + MT_DEBUG("%s(): transport header not found in ipv6" + " skb=%p\n", __func__, skb); + break; + case NFPROTO_IPV4: + tproto = ip_hdr(skb)->protocol; + break; + default: + tproto = IPPROTO_RAW; + } + return tproto; +} + static void data_counters_update(struct data_counters *dc, int set, enum ifs_tx_rx direction, int proto, int bytes) @@ -1207,10 +1279,10 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); return; } - entry->totals[IFS_TX].bytes += stats->tx_bytes; - entry->totals[IFS_TX].packets += stats->tx_packets; - entry->totals[IFS_RX].bytes += stats->rx_bytes; - entry->totals[IFS_RX].packets += stats->rx_packets; + entry->totals_via_dev[IFS_TX].bytes += stats->tx_bytes; + entry->totals_via_dev[IFS_TX].packets += stats->tx_packets; + entry->totals_via_dev[IFS_RX].bytes += stats->rx_bytes; + entry->totals_via_dev[IFS_RX].packets += stats->rx_packets; /* We don't need the last_known[] anymore */ entry->last_known_valid = false; _iface_stat_set_active(entry, net_dev, false); @@ -1220,6 +1292,67 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); } +/* + * Update stats for the specified interface from the skb. + * Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called on each sk. + */ +static void iface_stat_update_from_skb(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct iface_stat *entry; + const struct net_device *el_dev; + enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; + int bytes = skb->len; + + if (!skb->dev) { + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); + BUG(); + } else if (unlikely(!el_dev->name)) { + pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); + BUG(); + } else { + int proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); + } + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(el_dev->name); + if (entry == NULL) { + IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n", + __func__, el_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + el_dev->name, entry); + + entry->totals_via_skb[direction].bytes += bytes; + entry->totals_via_skb[direction].packets++; + spin_unlock_bh(&iface_stat_list_lock); +} + static void tag_stat_update(struct tag_stat *tag_entry, enum ifs_tx_rx direction, int proto, int bytes) { @@ -1467,18 +1600,31 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) parent_procdir); if (!iface_stat_all_procfile) { pr_err("qtaguid: iface_stat: init " - " failed to create stat_all proc entry\n"); + " failed to create stat_old proc entry\n"); err = -1; goto err_zap_entry; } - iface_stat_all_procfile->read_proc = iface_stat_all_proc_read; + iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_all_procfile->data = (void *)1; /* fmt1 */ + + iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_fmt_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_all proc entry\n"); + err = -1; + goto err_zap_all_stats_entry; + } + iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */ err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { pr_err("qtaguid: iface_stat: init " "failed to register dev event handler\n"); - goto err_zap_all_stats_entry; + goto err_zap_all_stats_entries; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { @@ -1499,6 +1645,8 @@ err_unreg_ip4_addr: unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); err_unreg_nd: unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_all_stats_entries: + remove_proc_entry(iface_stat_fmt_procfilename, parent_procdir); err_zap_all_stats_entry: remove_proc_entry(iface_stat_all_procfilename, parent_procdir); err_zap_entry: @@ -1550,27 +1698,6 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return sk; } -static int ipx_proto(const struct sk_buff *skb, - struct xt_action_param *par) -{ - int thoff = 0, tproto; - - switch (par->family) { - case NFPROTO_IPV6: - tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); - if (tproto < 0) - MT_DEBUG("%s(): transport header not found in ipv6" - " skb=%p\n", __func__, skb); - break; - case NFPROTO_IPV4: - tproto = ip_hdr(skb)->protocol; - break; - default: - tproto = IPPROTO_RAW; - } - return tproto; -} - static void account_for_uid(const struct sk_buff *skb, const struct sock *alternate_sk, uid_t uid, struct xt_action_param *par) @@ -1630,8 +1757,22 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) goto ret_res; } - sk = skb->sk; + switch (par->hooknum) { + case NF_INET_PRE_ROUTING: + case NF_INET_POST_ROUTING: + atomic64_inc(&qtu_events.match_calls_prepost); + iface_stat_update_from_skb(skb, par); + /* + * We are done in pre/post. The skb will get processed + * further alter. + */ + res = (info->match ^ info->invert); + goto ret_res; + break; + /* default: Fall through and do UID releated work */ + } + sk = skb->sk; if (sk == NULL) { /* * A missing sk->sk_socket happens when packets are in-flight @@ -1806,8 +1947,10 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, if (*eof) return 0; - CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", - page, items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u " + "page=%p off=%ld char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, items_to_skip, char_count, *eof); spin_lock_bh(&sock_tag_list_lock); for (node = rb_first(&sock_tag_tree); @@ -1851,6 +1994,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, "delete_cmds=%llu " "iface_events=%llu " "match_calls=%llu " + "match_calls_prepost=%llu " "match_found_sk=%llu " "match_found_sk_in_ct=%llu " "match_found_no_sk_in_ct=%llu " @@ -1862,6 +2006,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, atomic64_read(&qtu_events.delete_cmds), atomic64_read(&qtu_events.iface_events), atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_calls_prepost), atomic64_read(&qtu_events.match_found_sk), atomic64_read(&qtu_events.match_found_sk_in_ct), atomic64_read( @@ -2135,7 +2280,9 @@ static int ctrl_cmd_tag(const char *input) el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_tag(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); goto err; } CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2280,7 +2427,9 @@ static int ctrl_cmd_untag(const char *input) el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_untag(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); goto err; } CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2356,6 +2505,9 @@ static int qtaguid_ctrl_parse(const char *input, int count) char cmd; int res; + CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + cmd = input[0]; /* Collect params for commands */ switch (cmd) { @@ -2532,9 +2684,12 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, return len; } - CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", page, *num_items_returned, - items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, + items_to_skip, char_count, *eof); if (*eof) return 0; diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index 02479d6d317d..d79f8383abf4 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -202,7 +202,8 @@ struct iface_stat { /* net_dev is only valid for active iface_stat */ struct net_device *net_dev; - struct byte_packet_counters totals[IFS_MAX_DIRECTIONS]; + struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; + struct byte_packet_counters totals_via_skb[IFS_MAX_DIRECTIONS]; /* * We keep the last_known, because some devices reset their counters * just before NETDEV_UP, while some will reset just before @@ -254,6 +255,8 @@ struct qtaguid_event_counts { atomic64_t iface_events; /* Number of NETDEV_* events handled */ atomic64_t match_calls; /* Number of times iptables called mt */ + /* Number of times iptables called mt from pre or post routing hooks */ + atomic64_t match_calls_prepost; /* * match_found_sk_*: numbers related to the netfilter matching * function finding a sock for the sk_buff. diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 39176785c91f..8cbd8e42bcc4 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -183,7 +183,11 @@ char *pp_iface_stat(struct iface_stat *is) res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" "list=list_head{...}, " "ifname=%s, " - "total={rx={bytes=%llu, " + "total_dev={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "total_skb={rx={bytes=%llu, " "packets=%llu}, " "tx={bytes=%llu, " "packets=%llu}}, " @@ -198,10 +202,14 @@ char *pp_iface_stat(struct iface_stat *is) "tag_stat_tree=rb_root{...}}", is, is->ifname, - is->totals[IFS_RX].bytes, - is->totals[IFS_RX].packets, - is->totals[IFS_TX].bytes, - is->totals[IFS_TX].packets, + is->totals_via_dev[IFS_RX].bytes, + is->totals_via_dev[IFS_RX].packets, + is->totals_via_dev[IFS_TX].bytes, + is->totals_via_dev[IFS_TX].packets, + is->totals_via_skb[IFS_RX].bytes, + is->totals_via_skb[IFS_RX].packets, + is->totals_via_skb[IFS_TX].bytes, + is->totals_via_skb[IFS_TX].packets, is->last_known_valid, is->last_known[IFS_RX].bytes, is->last_known[IFS_RX].packets, From deedce75fcfdfa9f81c6be5d75f878df2d28adac Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 9 May 2012 16:09:50 -0700 Subject: [PATCH 140/358] trace: power: add trace_clock_set_parent Adds a new trace event to be called from clk_set_parent. Some cpufreq drivers, including Tegra, reparent the cpu clock to a slower clock while the main pll is relocking, tracing clk_set_parent allows traces to show how for long the cpu is running slower. Uses a separate TRACE_EVENT instead of the clock event class to allow the event to contain string names for the child and the parent. Signed-off-by: Colin Cross --- include/trace/events/power.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/trace/events/power.h b/include/trace/events/power.h index e5bf9a76f169..f2ec4647770f 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -232,6 +232,25 @@ DEFINE_EVENT(clock, clock_set_rate, TP_ARGS(name, state, cpu_id) ); +TRACE_EVENT(clock_set_parent, + + TP_PROTO(const char *name, const char *parent_name), + + TP_ARGS(name, parent_name), + + TP_STRUCT__entry( + __string( name, name ) + __string( parent_name, parent_name ) + ), + + TP_fast_assign( + __assign_str(name, name); + __assign_str(parent_name, parent_name); + ), + + TP_printk("%s parent=%s", __get_str(name), __get_str(parent_name)) +); + /* * The power domain events are used for power domains transitions */ From 7a3308111269800396a3ba30b379e97ede4bef7e Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Thu, 10 May 2012 16:41:40 -0700 Subject: [PATCH 141/358] usb: otg: otg-wakelock: Fix build for 3.4 Change-Id: I97e21e9e6645bf18522675039e512f85fe836794 Signed-off-by: Benoit Goby --- drivers/usb/phy/otg-wakelock.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/phy/otg-wakelock.c b/drivers/usb/phy/otg-wakelock.c index ffd8d8aa5dc8..e17e27299062 100644 --- a/drivers/usb/phy/otg-wakelock.c +++ b/drivers/usb/phy/otg-wakelock.c @@ -25,7 +25,7 @@ #define TEMPORARY_HOLD_TIME 2000 static bool enabled = true; -static struct otg_transceiver *otgwl_xceiv; +static struct usb_phy *otgwl_xceiv; static struct notifier_block otgwl_nb; /* @@ -139,10 +139,10 @@ static int __init otg_wakelock_init(void) { int ret; - otgwl_xceiv = otg_get_transceiver(); + otgwl_xceiv = usb_get_transceiver(); if (!otgwl_xceiv) { - pr_err("%s: No OTG transceiver found\n", __func__); + pr_err("%s: No USB transceiver found\n", __func__); return -ENODEV; } @@ -152,10 +152,10 @@ static int __init otg_wakelock_init(void) vbus_lock.name); otgwl_nb.notifier_call = otgwl_otg_notifications; - ret = otg_register_notifier(otgwl_xceiv, &otgwl_nb); + ret = usb_register_notifier(otgwl_xceiv, &otgwl_nb); if (ret) { - pr_err("%s: otg_register_notifier on transceiver %s" + pr_err("%s: usb_register_notifier on transceiver %s" " failed\n", __func__, dev_name(otgwl_xceiv->dev)); otgwl_xceiv = NULL; From 276a51541d5110384c0254cafe47a1d4b85cc1f7 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 17 May 2012 15:24:16 -0700 Subject: [PATCH 142/358] debug: add parameters to prevent entering debug mode on errors On non-developer devices kgdb prevents CONFIG_PANIC_TIMEOUT from rebooting the device after a panic. Add module parameters debug_core.break_on_exception and debug_core.break_on_panic to allow skipping debug on panics and exceptions respectively. Both default to true to preserve existing behavior. Change-Id: I75dce7263e96cee069a9750920cce83dc6f98e8c Signed-off-by: Colin Cross --- kernel/debug/debug_core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 334b3980ffc1..61d1d14b7623 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -86,6 +86,10 @@ static int kgdb_use_con; bool dbg_is_early = true; /* Next cpu to become the master debug core */ int dbg_switch_cpu; +/* Flag for entering kdb when a panic occurs */ +static bool break_on_panic = true; +/* Flag for entering kdb when an exception occurs */ +static bool break_on_exception = true; /* Use kdb or gdbserver mode */ int dbg_kdb_mode = 1; @@ -100,6 +104,8 @@ early_param("kgdbcon", opt_kgdb_con); module_param(kgdb_use_con, int, 0644); module_param(kgdbreboot, int, 0644); +module_param(break_on_panic, bool, 0644); +module_param(break_on_exception, bool, 0644); /* * Holds information about breakpoints in a kernel. These breakpoints are @@ -682,6 +688,9 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs) if (arch_kgdb_ops.enable_nmi) arch_kgdb_ops.enable_nmi(0); + if (unlikely(signo != SIGTRAP && !break_on_exception)) + return 1; + memset(ks, 0, sizeof(struct kgdb_state)); ks->cpu = raw_smp_processor_id(); ks->ex_vector = evector; @@ -813,6 +822,9 @@ static int kgdb_panic_event(struct notifier_block *self, unsigned long val, void *data) { + if (!break_on_panic) + return NOTIFY_DONE; + if (dbg_kdb_mode) kdb_printf("PANIC: %s\n", (char *)data); kgdb_breakpoint(); From f5b96a704d06bc94b28a469c687a4ff14a6e630e Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Tue, 15 May 2012 20:44:33 -0700 Subject: [PATCH 143/358] usb: gadget: composite: Fix corruption when changing configuration Remove the config from the configs list before releasing the spinlock. Otherwise the other cpu might be processing a SET_CONFIGURATION that will switch to the configuration that is being released. Change-Id: Id4da0d0e18ead63e20cb236cd1d3e8e6d116acce Signed-off-by: Benoit Goby --- drivers/usb/gadget/composite.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index d742bed7a5fa..e9169497a4c9 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -812,7 +812,7 @@ done: } EXPORT_SYMBOL_GPL(usb_add_config); -static void remove_config(struct usb_composite_dev *cdev, +static void unbind_config(struct usb_composite_dev *cdev, struct usb_configuration *config) { while (!list_empty(&config->functions)) { @@ -827,7 +827,6 @@ static void remove_config(struct usb_composite_dev *cdev, /* may free memory for "f" */ } } - list_del(&config->list); if (config->unbind) { DBG(cdev, "unbind config '%s'/%p\n", config->label, config); config->unbind(config); @@ -854,9 +853,11 @@ void usb_remove_config(struct usb_composite_dev *cdev, if (cdev->config == config) reset_config(cdev); + list_del(&config->list); + spin_unlock_irqrestore(&cdev->lock, flags); - remove_config(cdev, config); + unbind_config(cdev, config); } /*-------------------------------------------------------------------------*/ @@ -1537,7 +1538,8 @@ static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) struct usb_configuration *c; c = list_first_entry(&cdev->configs, struct usb_configuration, list); - remove_config(cdev, c); + list_del(&c->list); + unbind_config(cdev, c); } if (cdev->driver->unbind && unbind_driver) cdev->driver->unbind(cdev); From ce21c96db017f40422814a9f4c4facc17871dee3 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 10 May 2012 23:28:06 -0700 Subject: [PATCH 144/358] cpufreq: interactive: restart above_hispeed_delay at each hispeed load Change-Id: I2e5b91d45e8806b0ab94ca2301ed671c9af9ab13 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 7cb2e641fcdc..a4ed750f9d27 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -52,6 +52,7 @@ struct cpufreq_interactive_cpuinfo { unsigned int target_freq; unsigned int floor_freq; u64 floor_validate_time; + u64 hispeed_validate_time; int governor_enabled; }; @@ -206,7 +207,7 @@ static void cpufreq_interactive_timer(unsigned long data) if (pcpu->target_freq == hispeed_freq && new_freq > hispeed_freq && - pcpu->timer_run_time - pcpu->target_set_time + pcpu->timer_run_time - pcpu->hispeed_validate_time < above_hispeed_delay_val) { trace_cpufreq_interactive_notyet(data, cpu_load, pcpu->target_freq, @@ -218,6 +219,9 @@ static void cpufreq_interactive_timer(unsigned long data) new_freq = pcpu->policy->max * cpu_load / 100; } + if (new_freq <= hispeed_freq) + pcpu->hispeed_validate_time = pcpu->timer_run_time; + if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_H, &index)) { @@ -505,6 +509,7 @@ static void cpufreq_interactive_boost(void) cpumask_set_cpu(i, &up_cpumask); pcpu->target_set_time_in_idle = get_cpu_idle_time_us(i, &pcpu->target_set_time); + pcpu->hispeed_validate_time = pcpu->target_set_time; anyboost = 1; } @@ -838,6 +843,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->floor_freq = pcpu->target_freq; pcpu->floor_validate_time = pcpu->target_set_time; + pcpu->hispeed_validate_time = + pcpu->target_set_time; pcpu->governor_enabled = 1; smp_wmb(); } From 934e6da37d7959bbf332c5732680b8e1a917761f Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 11 May 2012 11:06:09 -0700 Subject: [PATCH 145/358] cpufreq: interactive: fixup trace of string params Change-Id: Iac47f62437e61b13724afbbf9df1a0729f58f236 Signed-off-by: Todd Poynor --- include/trace/events/cpufreq_interactive.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index 0791f2728c1b..ea83664a4e6d 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -83,27 +83,27 @@ DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, ); TRACE_EVENT(cpufreq_interactive_boost, - TP_PROTO(char *s), + TP_PROTO(const char *s), TP_ARGS(s), TP_STRUCT__entry( - __field(char *, s) + __string(s, s) ), TP_fast_assign( - __entry->s = s; + __assign_str(s, s); ), - TP_printk("%s", __entry->s) + TP_printk("%s", __get_str(s)) ); TRACE_EVENT(cpufreq_interactive_unboost, - TP_PROTO(char *s), + TP_PROTO(const char *s), TP_ARGS(s), TP_STRUCT__entry( - __field(char *, s) + __string(s, s) ), TP_fast_assign( - __entry->s = s; + __assign_str(s, s); ), - TP_printk("%s", __entry->s) + TP_printk("%s", __get_str(s)) ); #endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ From 0f57cdaf4b9a579ebee368ad25cd40b72dc9e3e3 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Tue, 29 May 2012 13:57:27 -0700 Subject: [PATCH 146/358] usb: gadget: android: Fix product name Product names may contain spaces and scanf %s only matches the 1st word. Use strlcpy instead. Change-Id: Ie8703fea9775f7fc17fe615a42597ca3816d36b0 Signed-off-by: Benoit Goby --- drivers/usb/gadget/android.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index d2c3393237b6..7a2af5844113 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -1053,10 +1053,7 @@ field ## _store(struct device *dev, struct device_attribute *attr, \ { \ if (size >= sizeof(buffer)) \ return -EINVAL; \ - if (sscanf(buf, "%s", buffer) == 1) { \ - return size; \ - } \ - return -1; \ + return strlcpy(buffer, buf, sizeof(buffer)); \ } \ static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); From 80ec6e8b9e7cafe0a4eec8a04db4c4b376ceddc3 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 29 May 2012 17:33:56 -0700 Subject: [PATCH 147/358] PM / Suspend: Print wall time at suspend entry and exit Change-Id: I92f252414c013b018b9a392eae1ee039aa0e89dc Signed-off-by: Todd Poynor --- kernel/power/suspend.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 62ee437b5c7e..4f1e813a2a58 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "power.h" @@ -358,6 +359,18 @@ static int enter_state(suspend_state_t state) return error; } +static void pm_suspend_marker(char *annotation) +{ + struct timespec ts; + struct rtc_time tm; + + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC\n", + annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); +} + /** * pm_suspend - Externally visible function for suspending the system. * @state: System sleep state to enter. @@ -372,6 +385,7 @@ int pm_suspend(suspend_state_t state) if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX) return -EINVAL; + pm_suspend_marker("entry"); error = enter_state(state); if (error) { suspend_stats.fail++; @@ -379,6 +393,7 @@ int pm_suspend(suspend_state_t state) } else { suspend_stats.success++; } + pm_suspend_marker("exit"); return error; } EXPORT_SYMBOL(pm_suspend); From 40b65478c6a8c405da8b17c945218787607ff490 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Thu, 29 Mar 2012 19:25:23 -0700 Subject: [PATCH 148/358] usb: gadget: android: Add FunctionFS Add support for FunctionFS (ffs) to implement usb functions in userspace. The aliases property stores the list of functions that are implemented using functionfs. For example: echo "adb,mtp" > /sys/class/android_usb/android0/f_ffs/aliases Then when the function are enabled: echo "adb,acm" > /sys/class/android_usb/android0/functions Internally, ffs and acm will be used. Change-Id: I44117b183d48a5a99ddbee3ef2cf8998be74598e Signed-off-by: Benoit Goby --- drivers/usb/gadget/android.c | 194 ++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 7a2af5844113..ff60af768228 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -42,6 +42,7 @@ #include "epautoconf.c" #include "composite.c" +#include "f_fs.c" #include "f_mass_storage.c" #include "u_serial.c" #include "f_acm.c" @@ -109,6 +110,7 @@ struct android_dev { bool connected; bool sw_connected; struct work_struct work; + char ffs_aliases[256]; }; static struct class *android_class; @@ -218,6 +220,158 @@ static void android_disable(struct android_dev *dev) /*-------------------------------------------------------------------------*/ /* Supported functions initialization */ +struct functionfs_config { + bool opened; + bool enabled; + struct ffs_data *data; +}; + +static int ffs_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + f->config = kzalloc(sizeof(struct functionfs_config), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + + return functionfs_init(); +} + +static void ffs_function_cleanup(struct android_usb_function *f) +{ + functionfs_cleanup(); + kfree(f->config); +} + +static void ffs_function_enable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = f->config; + + config->enabled = true; + + /* Disable the gadget until the function is ready */ + if (!config->opened) + android_disable(dev); +} + +static void ffs_function_disable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = f->config; + + config->enabled = false; + + /* Balance the disable that was called in closed_callback */ + if (!config->opened) + android_enable(dev); +} + +static int ffs_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct functionfs_config *config = f->config; + return functionfs_bind_config(c->cdev, c, config->data); +} + +static ssize_t +ffs_aliases_show(struct device *pdev, struct device_attribute *attr, char *buf) +{ + struct android_dev *dev = _android_dev; + int ret; + + mutex_lock(&dev->mutex); + ret = sprintf(buf, "%s\n", dev->ffs_aliases); + mutex_unlock(&dev->mutex); + + return ret; +} + +static ssize_t +ffs_aliases_store(struct device *pdev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct android_dev *dev = _android_dev; + char buff[256]; + + mutex_lock(&dev->mutex); + + if (dev->enabled) { + mutex_unlock(&dev->mutex); + return -EBUSY; + } + + strlcpy(buff, buf, sizeof(buff)); + strlcpy(dev->ffs_aliases, strim(buff), sizeof(dev->ffs_aliases)); + + mutex_unlock(&dev->mutex); + + return size; +} + +static DEVICE_ATTR(aliases, S_IRUGO | S_IWUSR, ffs_aliases_show, + ffs_aliases_store); +static struct device_attribute *ffs_function_attributes[] = { + &dev_attr_aliases, + NULL +}; + +static struct android_usb_function ffs_function = { + .name = "ffs", + .init = ffs_function_init, + .enable = ffs_function_enable, + .disable = ffs_function_disable, + .cleanup = ffs_function_cleanup, + .bind_config = ffs_function_bind_config, + .attributes = ffs_function_attributes, +}; + +static int functionfs_ready_callback(struct ffs_data *ffs) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = ffs_function.config; + int ret = 0; + + mutex_lock(&dev->mutex); + + ret = functionfs_bind(ffs, dev->cdev); + if (ret) + goto err; + + config->data = ffs; + config->opened = true; + + if (config->enabled) + android_enable(dev); + +err: + mutex_unlock(&dev->mutex); + return ret; +} + +static void functionfs_closed_callback(struct ffs_data *ffs) +{ + struct android_dev *dev = _android_dev; + struct functionfs_config *config = ffs_function.config; + + mutex_lock(&dev->mutex); + + if (config->enabled) + android_disable(dev); + + config->opened = false; + config->data = NULL; + + functionfs_unbind(ffs); + + mutex_unlock(&dev->mutex); +} + +static int functionfs_check_dev_callback(const char *dev_name) +{ + return 0; +} + + struct adb_data { bool opened; bool enabled; @@ -765,6 +919,7 @@ static struct android_usb_function accessory_function = { static struct android_usb_function *supported_functions[] = { + &ffs_function, &adb_function, &acm_function, &mtp_function, @@ -915,7 +1070,10 @@ functions_store(struct device *pdev, struct device_attribute *attr, struct android_dev *dev = dev_get_drvdata(pdev); char *name; char buf[256], *b; + char aliases[256], *a; int err; + int is_ffs; + int ffs_enabled = 0; mutex_lock(&dev->mutex); @@ -926,16 +1084,42 @@ functions_store(struct device *pdev, struct device_attribute *attr, INIT_LIST_HEAD(&dev->enabled_functions); - strncpy(buf, buff, sizeof(buf)); + strlcpy(buf, buff, sizeof(buf)); b = strim(buf); while (b) { name = strsep(&b, ","); - if (name) { - err = android_enable_function(dev, name); - if (err) - pr_err("android_usb: Cannot enable '%s'", name); + if (!name) + continue; + + is_ffs = 0; + strlcpy(aliases, dev->ffs_aliases, sizeof(aliases)); + a = aliases; + + while (a) { + char *alias = strsep(&a, ","); + if (alias && !strcmp(name, alias)) { + is_ffs = 1; + break; + } } + + if (is_ffs) { + if (ffs_enabled) + continue; + err = android_enable_function(dev, "ffs"); + if (err) + pr_err("android_usb: Cannot enable ffs (%d)", + err); + else + ffs_enabled = 1; + continue; + } + + err = android_enable_function(dev, name); + if (err) + pr_err("android_usb: Cannot enable '%s' (%d)", + name, err); } mutex_unlock(&dev->mutex); From f70957a79b9a09ef6418583fe0d74a532b5a5dc7 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 19 Jun 2012 21:06:47 -0700 Subject: [PATCH 149/358] gpio_input: convert from wakelocks to wakeup sources And add device names to wakeup source names Change-Id: Ia5f2723319a2e749f00d6ec7d846edff6af6d5c2 Signed-off-by: Todd Poynor --- drivers/input/misc/gpio_input.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c index 6a0c31510968..eefd02725aff 100644 --- a/drivers/input/misc/gpio_input.c +++ b/drivers/input/misc/gpio_input.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include enum { DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ @@ -45,7 +45,7 @@ struct gpio_input_state { int use_irq; int debounce_count; spinlock_t irq_lock; - struct wake_lock wake_lock; + struct wakeup_source *ws; struct gpio_key_state key_state[0]; }; @@ -153,7 +153,7 @@ static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) else if (!ds->use_irq) hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); else - wake_unlock(&ds->wake_lock); + __pm_relax(ds->ws); spin_unlock_irqrestore(&ds->irq_lock, irqflags); @@ -179,7 +179,7 @@ static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) if (ks->debounce & DEBOUNCE_WAIT_IRQ) { ks->debounce = DEBOUNCE_UNKNOWN; if (ds->debounce_count++ == 0) { - wake_lock(&ds->wake_lock); + __pm_stay_awake(ds->ws); hrtimer_start( &ds->timer, ds->info->debounce_time, HRTIMER_MODE_REL); @@ -262,6 +262,7 @@ int gpio_event_input_func(struct gpio_event_input_devs *input_devs, unsigned long irqflags; struct gpio_event_input_info *di; struct gpio_input_state *ds = *data; + char *wlname; di = container_of(info, struct gpio_event_input_info, info); @@ -297,7 +298,19 @@ int gpio_event_input_func(struct gpio_event_input_devs *input_devs, ds->debounce_count = di->keymap_size; ds->input_devs = input_devs; ds->info = di; - wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input"); + wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s", + input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : ""); + + ds->ws = wakeup_source_register(wlname); + kfree(wlname); + if (!ds->ws) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate wakeup source\n"); + goto err_ws_failed; + } + spin_lock_init(&ds->irq_lock); for (i = 0; i < di->keymap_size; i++) { @@ -369,7 +382,8 @@ err_gpio_request_failed: ; } err_bad_keymap: - wake_lock_destroy(&ds->wake_lock); + wakeup_source_unregister(ds->ws); +err_ws_failed: kfree(ds); err_ds_alloc_failed: return ret; From 3711c313725547fc44644d5e4090bb8c6d700d3e Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Fri, 22 Jun 2012 18:17:10 -0700 Subject: [PATCH 150/358] usb: gadget: accessory: Fix section mismatch create_bulk_endpoints should not be __init since it is called when accessory is enabled Change-Id: If827a4531f0f6c15af938345163923186368e2a5 Signed-off-by: Benoit Goby --- drivers/usb/gadget/f_accessory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index a5818227611a..04ceb0996560 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -296,7 +296,7 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) } } -static int __init create_bulk_endpoints(struct acc_dev *dev, +static int create_bulk_endpoints(struct acc_dev *dev, struct usb_endpoint_descriptor *in_desc, struct usb_endpoint_descriptor *out_desc) { From a44acf2821f9a60047ae6b2ad05ac1d745c672c4 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 3 Jul 2012 15:41:20 -0700 Subject: [PATCH 151/358] power_supply: Add custom property for USB High Current mode For smb347. Change-Id: I3323469072e1ee5085d61af8a89612b06b91f94a Signed-off-by: Todd Poynor --- drivers/power/power_supply_sysfs.c | 2 ++ include/linux/power_supply.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 44420d1e9094..2f63b8725ac3 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -193,6 +193,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), POWER_SUPPLY_ATTR(serial_number), + /* Local extensions */ + POWER_SUPPLY_ATTR(usb_hc), }; static struct attribute * diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index c9dc4e09854c..1e8bc298dd70 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -146,6 +146,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, + /* Local extensions */ + POWER_SUPPLY_PROP_USB_HC, }; enum power_supply_type { From 3986d2b6783e859f5b98dd8bf2ef5d9839daa032 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 12 Jul 2012 20:27:16 -0700 Subject: [PATCH 152/358] power: power_supply: move POWER_SUPPLY_PROP_USB_HC to type 'int' order Change-Id: I001af30ab5fe06dde5f368241f21b9e0864777a1 Signed-off-by: Todd Poynor --- drivers/power/power_supply_sysfs.c | 4 ++-- include/linux/power_supply.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 2f63b8725ac3..09c1a978bb6f 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -189,12 +189,12 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(time_to_full_avg), POWER_SUPPLY_ATTR(type), POWER_SUPPLY_ATTR(scope), + /* Local extensions */ + POWER_SUPPLY_ATTR(usb_hc), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), POWER_SUPPLY_ATTR(serial_number), - /* Local extensions */ - POWER_SUPPLY_ATTR(usb_hc), }; static struct attribute * diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 1e8bc298dd70..c9f5a762fe8e 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -142,12 +142,12 @@ enum power_supply_property { POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ POWER_SUPPLY_PROP_SCOPE, + /* Local extensions */ + POWER_SUPPLY_PROP_USB_HC, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_SERIAL_NUMBER, - /* Local extensions */ - POWER_SUPPLY_PROP_USB_HC, }; enum power_supply_type { From 474239158e69343ce641538719563f9e42037ceb Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 13 Jul 2012 13:30:04 -0700 Subject: [PATCH 153/358] power: power_supply: add POWER_SUPPLY_PROP_USB_OTG Change-Id: Idfc6ef2e37d62aad6f26cc8eafa53db642cd352b Signed-off-by: Todd Poynor --- drivers/power/power_supply_sysfs.c | 1 + include/linux/power_supply.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 09c1a978bb6f..c367777fd0a3 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -191,6 +191,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(scope), /* Local extensions */ POWER_SUPPLY_ATTR(usb_hc), + POWER_SUPPLY_ATTR(usb_otg), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index c9f5a762fe8e..493d42f64d4f 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -144,6 +144,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_SCOPE, /* Local extensions */ POWER_SUPPLY_PROP_USB_HC, + POWER_SUPPLY_PROP_USB_OTG, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, From e044cb9c86102367893979079ce1c3f1403d8034 Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Wed, 27 Jun 2012 10:12:04 -0700 Subject: [PATCH 154/358] cpufreq: interactive: take idle notifications only when active Register an idle notifier only when the governor is active. Also short-circuit work of idle end if the governor is not enabled. Signed-off-by: Sam Leffler Change-Id: I4cae36dd2e7389540d337d74745ffbaa0131870f --- drivers/cpufreq/cpufreq_interactive.c | 46 +++++++++++++++------------ 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index a4ed750f9d27..20389918e66c 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -364,6 +364,9 @@ static void cpufreq_interactive_idle_end(void) struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, smp_processor_id()); + if (!pcpu->governor_enabled) + return; + pcpu->idling = 0; smp_wmb(); @@ -816,6 +819,26 @@ static struct attribute_group interactive_attr_group = { .name = "interactive", }; +static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, + unsigned long val, + void *data) +{ + switch (val) { + case IDLE_START: + cpufreq_interactive_idle_start(); + break; + case IDLE_END: + cpufreq_interactive_idle_end(); + break; + } + + return 0; +} + +static struct notifier_block cpufreq_interactive_idle_nb = { + .notifier_call = cpufreq_interactive_idle_notifier, +}; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event) { @@ -869,6 +892,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pr_warn("%s: failed to register input handler\n", __func__); + idle_notifier_register(&cpufreq_interactive_idle_nb); break; case CPUFREQ_GOV_STOP: @@ -891,6 +915,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (atomic_dec_return(&active_count) > 0) return 0; + idle_notifier_unregister(&cpufreq_interactive_idle_nb); input_unregister_handler(&cpufreq_interactive_input_handler); sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); @@ -909,26 +934,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, return 0; } -static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, - unsigned long val, - void *data) -{ - switch (val) { - case IDLE_START: - cpufreq_interactive_idle_start(); - break; - case IDLE_END: - cpufreq_interactive_idle_end(); - break; - } - - return 0; -} - -static struct notifier_block cpufreq_interactive_idle_nb = { - .notifier_call = cpufreq_interactive_idle_notifier, -}; - static int __init cpufreq_interactive_init(void) { unsigned int i; @@ -970,7 +975,6 @@ static int __init cpufreq_interactive_init(void) spin_lock_init(&down_cpumask_lock); mutex_init(&set_speed_lock); - idle_notifier_register(&cpufreq_interactive_idle_nb); INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); return cpufreq_register_governor(&cpufreq_gov_interactive); From 8a2a1300ffe002c8f57ec6c692587c2c5b15627b Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Wed, 27 Jun 2012 12:55:56 -0700 Subject: [PATCH 155/358] cpufreq: interactive: keep freezer happy when not current governor Fix a problem where the hung task mechanism was deeming the interactive clock boost thread as hung. This was because the thread is created at module init but never run/woken up until needed. If the governor is not being used this can be forever. To workaround this explicitly wake up the thread once all the necessary data structures are initialized. The latter required some minor code shuffle. Signed-off-by: Sam Leffler Change-Id: Ie2c058dd75dcb6460ea10e7ac997e46baf66b1fe --- drivers/cpufreq/cpufreq_interactive.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 20389918e66c..184140aabda2 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -953,6 +953,10 @@ static int __init cpufreq_interactive_init(void) pcpu->cpu_timer.data = i; } + spin_lock_init(&up_cpumask_lock); + spin_lock_init(&down_cpumask_lock); + mutex_init(&set_speed_lock); + up_task = kthread_create(cpufreq_interactive_up_task, NULL, "kinteractiveup"); if (IS_ERR(up_task)) @@ -968,14 +972,12 @@ static int __init cpufreq_interactive_init(void) if (!down_wq) goto err_freeuptask; - INIT_WORK(&freq_scale_down_work, - cpufreq_interactive_freq_down); - - spin_lock_init(&up_cpumask_lock); - spin_lock_init(&down_cpumask_lock); - mutex_init(&set_speed_lock); - + INIT_WORK(&freq_scale_down_work, cpufreq_interactive_freq_down); INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); + + /* NB: wake up so the thread does not look hung to the freezer */ + wake_up_process(up_task); + return cpufreq_register_governor(&cpufreq_gov_interactive); err_freeuptask: From 31ab18911a5c1a94cebe234d5636dadecf2c3c1d Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 16 Jul 2012 17:07:15 -0700 Subject: [PATCH 156/358] cpufreq: interactive: handle speed up and down in the realtime task Not useful to have a separate, non-realtime workqueue for speed down events, avoid priority inversion for speed up events. Change-Id: Iddcd05545245c847aa1bbe0b8790092914c813d2 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 148 ++++++--------------- include/trace/events/cpufreq_interactive.h | 8 +- 2 files changed, 45 insertions(+), 111 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 184140aabda2..e9c85160e33c 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -58,15 +58,10 @@ struct cpufreq_interactive_cpuinfo { static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); -/* Workqueues handle frequency scaling */ -static struct task_struct *up_task; -static struct workqueue_struct *down_wq; -static struct work_struct freq_scale_down_work; -static cpumask_t up_cpumask; -static spinlock_t up_cpumask_lock; -static cpumask_t down_cpumask; -static spinlock_t down_cpumask_lock; -static struct mutex set_speed_lock; +/* realtime thread handles frequency scaling */ +static struct task_struct *speedchange_task; +static cpumask_t speedchange_cpumask; +static spinlock_t speedchange_cpumask_lock; /* Hi speed to bump to from lo speed when load burst (default max) */ static u64 hispeed_freq; @@ -106,6 +101,7 @@ struct cpufreq_interactive_inputopen { }; static struct cpufreq_interactive_inputopen inputopen; +static struct workqueue_struct *inputopen_wq; /* * Non-zero means longer-term speed boost active. @@ -259,19 +255,11 @@ static void cpufreq_interactive_timer(unsigned long data) pcpu->target_set_time_in_idle = now_idle; pcpu->target_set_time = pcpu->timer_run_time; - if (new_freq < pcpu->target_freq) { - pcpu->target_freq = new_freq; - spin_lock_irqsave(&down_cpumask_lock, flags); - cpumask_set_cpu(data, &down_cpumask); - spin_unlock_irqrestore(&down_cpumask_lock, flags); - queue_work(down_wq, &freq_scale_down_work); - } else { - pcpu->target_freq = new_freq; - spin_lock_irqsave(&up_cpumask_lock, flags); - cpumask_set_cpu(data, &up_cpumask); - spin_unlock_irqrestore(&up_cpumask_lock, flags); - wake_up_process(up_task); - } + pcpu->target_freq = new_freq; + spin_lock_irqsave(&speedchange_cpumask_lock, flags); + cpumask_set_cpu(data, &speedchange_cpumask); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); + wake_up_process(speedchange_task); rearm_if_notmax: /* @@ -394,7 +382,7 @@ static void cpufreq_interactive_idle_end(void) } -static int cpufreq_interactive_up_task(void *data) +static int cpufreq_interactive_speedchange_task(void *data) { unsigned int cpu; cpumask_t tmp_mask; @@ -403,22 +391,23 @@ static int cpufreq_interactive_up_task(void *data) while (1) { set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&up_cpumask_lock, flags); + spin_lock_irqsave(&speedchange_cpumask_lock, flags); - if (cpumask_empty(&up_cpumask)) { - spin_unlock_irqrestore(&up_cpumask_lock, flags); + if (cpumask_empty(&speedchange_cpumask)) { + spin_unlock_irqrestore(&speedchange_cpumask_lock, + flags); schedule(); if (kthread_should_stop()) break; - spin_lock_irqsave(&up_cpumask_lock, flags); + spin_lock_irqsave(&speedchange_cpumask_lock, flags); } set_current_state(TASK_RUNNING); - tmp_mask = up_cpumask; - cpumask_clear(&up_cpumask); - spin_unlock_irqrestore(&up_cpumask_lock, flags); + tmp_mask = speedchange_cpumask; + cpumask_clear(&speedchange_cpumask); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); for_each_cpu(cpu, &tmp_mask) { unsigned int j; @@ -430,8 +419,6 @@ static int cpufreq_interactive_up_task(void *data) if (!pcpu->governor_enabled) continue; - mutex_lock(&set_speed_lock); - for_each_cpu(j, pcpu->policy->cpus) { struct cpufreq_interactive_cpuinfo *pjcpu = &per_cpu(cpuinfo, j); @@ -444,8 +431,8 @@ static int cpufreq_interactive_up_task(void *data) __cpufreq_driver_target(pcpu->policy, max_freq, CPUFREQ_RELATION_H); - mutex_unlock(&set_speed_lock); - trace_cpufreq_interactive_up(cpu, pcpu->target_freq, + trace_cpufreq_interactive_setspeed(cpu, + pcpu->target_freq, pcpu->policy->cur); } } @@ -453,48 +440,6 @@ static int cpufreq_interactive_up_task(void *data) return 0; } -static void cpufreq_interactive_freq_down(struct work_struct *work) -{ - unsigned int cpu; - cpumask_t tmp_mask; - unsigned long flags; - struct cpufreq_interactive_cpuinfo *pcpu; - - spin_lock_irqsave(&down_cpumask_lock, flags); - tmp_mask = down_cpumask; - cpumask_clear(&down_cpumask); - spin_unlock_irqrestore(&down_cpumask_lock, flags); - - for_each_cpu(cpu, &tmp_mask) { - unsigned int j; - unsigned int max_freq = 0; - - pcpu = &per_cpu(cpuinfo, cpu); - smp_rmb(); - - if (!pcpu->governor_enabled) - continue; - - mutex_lock(&set_speed_lock); - - for_each_cpu(j, pcpu->policy->cpus) { - struct cpufreq_interactive_cpuinfo *pjcpu = - &per_cpu(cpuinfo, j); - - if (pjcpu->target_freq > max_freq) - max_freq = pjcpu->target_freq; - } - - if (max_freq != pcpu->policy->cur) - __cpufreq_driver_target(pcpu->policy, max_freq, - CPUFREQ_RELATION_H); - - mutex_unlock(&set_speed_lock); - trace_cpufreq_interactive_down(cpu, pcpu->target_freq, - pcpu->policy->cur); - } -} - static void cpufreq_interactive_boost(void) { int i; @@ -502,14 +447,14 @@ static void cpufreq_interactive_boost(void) unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; - spin_lock_irqsave(&up_cpumask_lock, flags); + spin_lock_irqsave(&speedchange_cpumask_lock, flags); for_each_online_cpu(i) { pcpu = &per_cpu(cpuinfo, i); if (pcpu->target_freq < hispeed_freq) { pcpu->target_freq = hispeed_freq; - cpumask_set_cpu(i, &up_cpumask); + cpumask_set_cpu(i, &speedchange_cpumask); pcpu->target_set_time_in_idle = get_cpu_idle_time_us(i, &pcpu->target_set_time); pcpu->hispeed_validate_time = pcpu->target_set_time; @@ -525,10 +470,10 @@ static void cpufreq_interactive_boost(void) pcpu->floor_validate_time = ktime_to_us(ktime_get()); } - spin_unlock_irqrestore(&up_cpumask_lock, flags); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); if (anyboost) - wake_up_process(up_task); + wake_up_process(speedchange_task); } /* @@ -580,7 +525,7 @@ static int cpufreq_interactive_input_connect(struct input_handler *handler, goto err; inputopen.handle = handle; - queue_work(down_wq, &inputopen.inputopen_work); + queue_work(inputopen_wq, &inputopen.inputopen_work); return 0; err: kfree(handle); @@ -911,7 +856,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->idle_exit_time = 0; } - flush_work(&freq_scale_down_work); + flush_work(&inputopen.inputopen_work); if (atomic_dec_return(&active_count) > 0) return 0; @@ -953,35 +898,30 @@ static int __init cpufreq_interactive_init(void) pcpu->cpu_timer.data = i; } - spin_lock_init(&up_cpumask_lock); - spin_lock_init(&down_cpumask_lock); - mutex_init(&set_speed_lock); + spin_lock_init(&speedchange_cpumask_lock); + speedchange_task = + kthread_create(cpufreq_interactive_speedchange_task, NULL, + "cfinteractive"); + if (IS_ERR(speedchange_task)) + return PTR_ERR(speedchange_task); - up_task = kthread_create(cpufreq_interactive_up_task, NULL, - "kinteractiveup"); - if (IS_ERR(up_task)) - return PTR_ERR(up_task); + sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); + get_task_struct(speedchange_task); - sched_setscheduler_nocheck(up_task, SCHED_FIFO, ¶m); - get_task_struct(up_task); + inputopen_wq = create_workqueue("cfinteractive"); - /* No rescuer thread, bind to CPU queuing the work for possibly - warm cache (probably doesn't matter much). */ - down_wq = alloc_workqueue("knteractive_down", 0, 1); + if (!inputopen_wq) + goto err_freetask; - if (!down_wq) - goto err_freeuptask; - - INIT_WORK(&freq_scale_down_work, cpufreq_interactive_freq_down); INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); /* NB: wake up so the thread does not look hung to the freezer */ - wake_up_process(up_task); + wake_up_process(speedchange_task); return cpufreq_register_governor(&cpufreq_gov_interactive); -err_freeuptask: - put_task_struct(up_task); +err_freetask: + put_task_struct(speedchange_task); return -ENOMEM; } @@ -994,9 +934,9 @@ module_init(cpufreq_interactive_init); static void __exit cpufreq_interactive_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_interactive); - kthread_stop(up_task); - put_task_struct(up_task); - destroy_workqueue(down_wq); + kthread_stop(speedchange_task); + put_task_struct(speedchange_task); + destroy_workqueue(inputopen_wq); } module_exit(cpufreq_interactive_exit); diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index ea83664a4e6d..ecec7970b871 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -28,13 +28,7 @@ DECLARE_EVENT_CLASS(set, __entry->actualfreq) ); -DEFINE_EVENT(set, cpufreq_interactive_up, - TP_PROTO(u32 cpu_id, unsigned long targfreq, - unsigned long actualfreq), - TP_ARGS(cpu_id, targfreq, actualfreq) -); - -DEFINE_EVENT(set, cpufreq_interactive_down, +DEFINE_EVENT(set, cpufreq_interactive_setspeed, TP_PROTO(u32 cpu_id, unsigned long targfreq, unsigned long actualfreq), TP_ARGS(cpu_id, targfreq, actualfreq) From c4711f52b1dc036b2729f2d776e0bb59c3f4ebac Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 16 Jul 2012 17:32:44 -0700 Subject: [PATCH 157/358] cpufreq: interactive: remove input_boost handling Now handled in userspace Power HAL instead. Change-Id: I78a4a2fd471308bfcd785bbefcc65fede27314cf Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 146 -------------------------- 1 file changed, 146 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index e9c85160e33c..4b60b6ec7ed5 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #define CREATE_TRACE_POINTS @@ -89,20 +88,6 @@ static unsigned long timer_rate; #define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE static unsigned long above_hispeed_delay_val; -/* - * Boost pulse to hispeed on touchscreen input. - */ - -static int input_boost_val; - -struct cpufreq_interactive_inputopen { - struct input_handle *handle; - struct work_struct inputopen_work; -}; - -static struct cpufreq_interactive_inputopen inputopen; -static struct workqueue_struct *inputopen_wq; - /* * Non-zero means longer-term speed boost active. */ @@ -476,96 +461,6 @@ static void cpufreq_interactive_boost(void) wake_up_process(speedchange_task); } -/* - * Pulsed boost on input event raises CPUs to hispeed_freq and lets - * usual algorithm of min_sample_time decide when to allow speed - * to drop. - */ - -static void cpufreq_interactive_input_event(struct input_handle *handle, - unsigned int type, - unsigned int code, int value) -{ - if (input_boost_val && type == EV_SYN && code == SYN_REPORT) { - trace_cpufreq_interactive_boost("input"); - cpufreq_interactive_boost(); - } -} - -static void cpufreq_interactive_input_open(struct work_struct *w) -{ - struct cpufreq_interactive_inputopen *io = - container_of(w, struct cpufreq_interactive_inputopen, - inputopen_work); - int error; - - error = input_open_device(io->handle); - if (error) - input_unregister_handle(io->handle); -} - -static int cpufreq_interactive_input_connect(struct input_handler *handler, - struct input_dev *dev, - const struct input_device_id *id) -{ - struct input_handle *handle; - int error; - - pr_info("%s: connect to %s\n", __func__, dev->name); - handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); - if (!handle) - return -ENOMEM; - - handle->dev = dev; - handle->handler = handler; - handle->name = "cpufreq_interactive"; - - error = input_register_handle(handle); - if (error) - goto err; - - inputopen.handle = handle; - queue_work(inputopen_wq, &inputopen.inputopen_work); - return 0; -err: - kfree(handle); - return error; -} - -static void cpufreq_interactive_input_disconnect(struct input_handle *handle) -{ - input_close_device(handle); - input_unregister_handle(handle); - kfree(handle); -} - -static const struct input_device_id cpufreq_interactive_ids[] = { - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | - INPUT_DEVICE_ID_MATCH_ABSBIT, - .evbit = { BIT_MASK(EV_ABS) }, - .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = - BIT_MASK(ABS_MT_POSITION_X) | - BIT_MASK(ABS_MT_POSITION_Y) }, - }, /* multi-touch touchscreen */ - { - .flags = INPUT_DEVICE_ID_MATCH_KEYBIT | - INPUT_DEVICE_ID_MATCH_ABSBIT, - .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, - .absbit = { [BIT_WORD(ABS_X)] = - BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, - }, /* touchpad */ - { }, -}; - -static struct input_handler cpufreq_interactive_input_handler = { - .event = cpufreq_interactive_input_event, - .connect = cpufreq_interactive_input_connect, - .disconnect = cpufreq_interactive_input_disconnect, - .name = "cpufreq_interactive", - .id_table = cpufreq_interactive_ids, -}; - static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -678,27 +573,6 @@ static ssize_t store_timer_rate(struct kobject *kobj, static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, show_timer_rate, store_timer_rate); -static ssize_t show_input_boost(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - return sprintf(buf, "%u\n", input_boost_val); -} - -static ssize_t store_input_boost(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - int ret; - unsigned long val; - - ret = strict_strtoul(buf, 0, &val); - if (ret < 0) - return ret; - input_boost_val = val; - return count; -} - -define_one_global_rw(input_boost); - static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -753,7 +627,6 @@ static struct attribute *interactive_attributes[] = { &above_hispeed_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, - &input_boost.attr, &boost.attr, &boostpulse.attr, NULL, @@ -832,11 +705,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (rc) return rc; - rc = input_register_handler(&cpufreq_interactive_input_handler); - if (rc) - pr_warn("%s: failed to register input handler\n", - __func__); - idle_notifier_register(&cpufreq_interactive_idle_nb); break; @@ -856,12 +724,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->idle_exit_time = 0; } - flush_work(&inputopen.inputopen_work); if (atomic_dec_return(&active_count) > 0) return 0; idle_notifier_unregister(&cpufreq_interactive_idle_nb); - input_unregister_handler(&cpufreq_interactive_input_handler); sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); @@ -908,21 +774,10 @@ static int __init cpufreq_interactive_init(void) sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); get_task_struct(speedchange_task); - inputopen_wq = create_workqueue("cfinteractive"); - - if (!inputopen_wq) - goto err_freetask; - - INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); - /* NB: wake up so the thread does not look hung to the freezer */ wake_up_process(speedchange_task); return cpufreq_register_governor(&cpufreq_gov_interactive); - -err_freetask: - put_task_struct(speedchange_task); - return -ENOMEM; } #ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE @@ -936,7 +791,6 @@ static void __exit cpufreq_interactive_exit(void) cpufreq_unregister_governor(&cpufreq_gov_interactive); kthread_stop(speedchange_task); put_task_struct(speedchange_task); - destroy_workqueue(inputopen_wq); } module_exit(cpufreq_interactive_exit); From 1833f5dd50593f865d932000edada14c1ab7e622 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 18 Jul 2012 16:28:50 -0700 Subject: [PATCH 158/358] power: power_supply: add POWER_SUPPLY_PROP_CHARGE_ENABLED Change-Id: I3e93b502452811cbfc4d904202b4f1d94edc143d Signed-off-by: Todd Poynor --- drivers/power/power_supply_sysfs.c | 1 + include/linux/power_supply.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index c367777fd0a3..7566e15701e5 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -192,6 +192,7 @@ static struct device_attribute power_supply_attrs[] = { /* Local extensions */ POWER_SUPPLY_ATTR(usb_hc), POWER_SUPPLY_ATTR(usb_otg), + POWER_SUPPLY_ATTR(charge_enabled), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 493d42f64d4f..62c2e9f48e1b 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -145,6 +145,7 @@ enum power_supply_property { /* Local extensions */ POWER_SUPPLY_PROP_USB_HC, POWER_SUPPLY_PROP_USB_OTG, + POWER_SUPPLY_PROP_CHARGE_ENABLED, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, From ccff4c22b959185927a11c3ed401c487d19b4151 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 19 Jul 2012 18:40:04 -0700 Subject: [PATCH 159/358] ARM: fiq_debugger: add process context reboot command kernel_restart cannot be called from interrupt context. Add support for commands called from a work function, and implement the "reboot" command there. Also rename the existing irq-mode command to "reset" and change it to use machine_restart instead of kernel_restart. Change-Id: I3c423147c01db03d89e95a5b99096ca89462079f Signed-off-by: Colin Cross --- arch/arm/common/fiq_debugger.c | 67 +++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index ac952241fdd2..a12810b5fb68 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -81,6 +81,10 @@ struct fiq_debugger_state { atomic_t unhandled_fiq_count; bool in_fiq; + struct work_struct work; + spinlock_t work_lock; + char work_cmd[DEBUG_MAX]; + #ifdef CONFIG_FIQ_DEBUGGER_CONSOLE struct console console; struct tty_struct *tty; @@ -557,6 +561,53 @@ static void do_kgdb(struct fiq_debugger_state *state) } #endif +static void debug_schedule_work(struct fiq_debugger_state *state, char *cmd) +{ + unsigned long flags; + + spin_lock_irqsave(&state->work_lock, flags); + if (state->work_cmd[0] != '\0') { + debug_printf(state, "work command processor busy\n"); + spin_unlock_irqrestore(&state->work_lock, flags); + return; + } + + strlcpy(state->work_cmd, cmd, sizeof(state->work_cmd)); + spin_unlock_irqrestore(&state->work_lock, flags); + + schedule_work(&state->work); +} + +static void debug_work(struct work_struct *work) +{ + struct fiq_debugger_state *state; + char work_cmd[DEBUG_MAX]; + char *cmd; + unsigned long flags; + + state = container_of(work, struct fiq_debugger_state, work); + + spin_lock_irqsave(&state->work_lock, flags); + + strlcpy(work_cmd, state->work_cmd, sizeof(work_cmd)); + state->work_cmd[0] = '\0'; + + spin_unlock_irqrestore(&state->work_lock, flags); + + cmd = work_cmd; + if (!strncmp(cmd, "reboot", 6)) { + cmd += 6; + while (*cmd == ' ') + cmd++; + if (cmd != '\0') + kernel_restart(cmd); + else + kernel_restart(NULL); + } else { + debug_printf(state, "unknown work command '%s'\n", work_cmd); + } +} + /* This function CANNOT be called in FIQ context */ static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd) { @@ -570,6 +621,8 @@ static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd) if (!strcmp(cmd, "kgdb")) do_kgdb(state); #endif + if (!strncmp(cmd, "reboot", 6)) + debug_schedule_work(state, cmd); } static void debug_help(struct fiq_debugger_state *state) @@ -579,7 +632,8 @@ static void debug_help(struct fiq_debugger_state *state) " regs Register dump\n" " allregs Extended Register dump\n" " bt Stack trace\n" - " reboot Reboot\n" + " reboot [] Reboot with command \n" + " reset [] Hard reset with command \n" " irqs Interupt status\n" " kmsg Kernel log\n" " version Kernel version\n"); @@ -630,16 +684,16 @@ static bool debug_fiq_exec(struct fiq_debugger_state *state, dump_allregs(state, regs); } else if (!strcmp(cmd, "bt")) { dump_stacktrace(state, (struct pt_regs *)regs, 100, svc_sp); - } else if (!strncmp(cmd, "reboot", 6)) { - cmd += 6; + } else if (!strncmp(cmd, "reset", 5)) { + cmd += 5; while (*cmd == ' ') cmd++; if (*cmd) { char tmp_cmd[32]; strlcpy(tmp_cmd, cmd, sizeof(tmp_cmd)); - kernel_restart(tmp_cmd); + machine_restart(tmp_cmd); } else { - kernel_restart(NULL); + machine_restart(NULL); } } else if (!strcmp(cmd, "irqs")) { dump_irqs(state); @@ -1189,6 +1243,9 @@ static int fiq_debugger_probe(struct platform_device *pdev) state->signal_irq = platform_get_irq_byname(pdev, "signal"); state->wakeup_irq = platform_get_irq_byname(pdev, "wakeup"); + INIT_WORK(&state->work, debug_work); + spin_lock_init(&state->work_lock); + platform_set_drvdata(pdev, state); spin_lock_init(&state->sleep_timer_lock); From 20a42366274d23dfeef7c858ad705f30167b1e43 Mon Sep 17 00:00:00 2001 From: Hosung Kim Date: Mon, 23 Jul 2012 17:33:17 +0900 Subject: [PATCH 160/358] mmc: sdio: Fix sdio_reset_comm for sync mmc_sdio_init_card function is doing necessary initialization Change-Id: I7d2e432b2af8a76267378acba07e3e4e8fd1e6bc Signed-off-by: Hosung Kim --- drivers/mmc/core/sdio.c | 40 +--------------------------------------- 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 413063b04d5f..156c028c2f9c 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1298,48 +1298,10 @@ int sdio_reset_comm(struct mmc_card *card) goto err; } - err = mmc_send_io_op_cond(host, host->ocr, &ocr); + err = mmc_sdio_init_card(host, host->ocr, card, 0); if (err) goto err; - if (mmc_host_is_spi(host)) { - err = mmc_spi_set_crc(host, use_spi_crc); - if (err) - goto err; - } - - if (!mmc_host_is_spi(host)) { - err = mmc_send_relative_addr(host, &card->rca); - if (err) - goto err; - mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); - } - if (!mmc_host_is_spi(host)) { - err = mmc_select_card(card); - if (err) - goto err; - } - - /* - * Switch to high-speed (if supported). - */ - err = sdio_enable_hs(card); - if (err > 0) - mmc_sd_go_highspeed(card); - else if (err) - goto err; - - /* - * Change to the card's maximum speed. - */ - mmc_set_clock(host, mmc_sdio_get_max_clock(card)); - - err = sdio_enable_4bit_bus(card); - if (err > 0) - mmc_set_bus_width(host, MMC_BUS_WIDTH_4); - else if (err) - goto err; - mmc_release_host(host); return 0; err: From 7474140a4eb7d587c056974249f9f131bc66373e Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 27 Jul 2012 11:27:33 -0700 Subject: [PATCH 161/358] power: android battery: add generic android battery driver Add a generic battery power supply and glue logic for talking to the board battery driver. This driver handles common chores such as: * periodic battery level and health monitoring * kernel log reporting and other debugging features for key properties provided by different charger, fuel gauge, etc. components * ensure properties such as battery health are made available to userspace * common processing for board-level battery/case temperature sensors and policy for charging status based on battery health Based on work by himihee.seo@samsung.com, ms925.kim@samsung.com, and joshua.chang@samsung.com. Change-Id: I5fa8e8d68811d84820b7a130b0245ad2b5b6d36b Signed-off-by: Todd Poynor --- drivers/power/Kconfig | 10 + drivers/power/Makefile | 1 + drivers/power/android_battery.c | 475 ++++++++++++++++++ include/linux/platform_data/android_battery.h | 42 ++ 4 files changed, 528 insertions(+) create mode 100644 drivers/power/android_battery.c create mode 100644 include/linux/platform_data/android_battery.h diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index ba6975123071..1a9b52f9fc13 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -205,6 +205,16 @@ config BATTERY_MAX17042 with MAX17042. This driver also supports max17047/50 chips which are improved version of max17042. +config BATTERY_ANDROID + tristate "Battery driver for Android" + help + Say Y to enable generic support for battery charging according + to common Android policies. + This driver adds periodic battery level and health monitoring, + kernel log reporting and other debugging features, common board + battery file glue logic for battery/case temperature sensors, + etc. + config BATTERY_Z2 tristate "Z2 battery driver" depends on I2C && MACH_ZIPIT2 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index ee54a3e4c90a..5f6f23ffe777 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_WM8350_POWER) += wm8350_power.o obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o +obj-$(CONFIG_BATTERY_ANDROID) += android_battery.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c new file mode 100644 index 000000000000..ded435029677 --- /dev/null +++ b/drivers/power/android_battery.c @@ -0,0 +1,475 @@ +/* + * android_battery.c + * Android Battery Driver + * + * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2012 Samsung Electronics + * + * Based on work by himihee.seo@samsung.com, ms925.kim@samsung.com, and + * joshua.chang@samsung.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct android_bat_data { + struct android_bat_platform_data *pdata; + struct android_bat_callbacks callbacks; + + struct device *dev; + + struct power_supply psy_bat; + + struct wake_lock monitor_wake_lock; + struct wake_lock charger_wake_lock; + + int charge_source; + + int batt_temp; + int batt_current; + unsigned int batt_health; + unsigned int batt_vcell; + unsigned int batt_soc; + unsigned int charging_status; + + struct workqueue_struct *monitor_wqueue; + struct delayed_work monitor_work; + struct work_struct charger_work; + + bool slow_poll; + ktime_t last_poll; +}; + +static enum power_supply_property android_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static void android_bat_update_data(struct android_bat_data *battery); + +static char *charge_source_str(int charge_source) +{ + switch (charge_source) { + case CHARGE_SOURCE_NONE: + return "none"; + case CHARGE_SOURCE_AC: + return "ac"; + case CHARGE_SOURCE_USB: + return "usb"; + default: + break; + } + + return "?"; +} + +static int android_bat_get_property(struct power_supply *ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct android_bat_data *battery = + container_of(ps, struct android_bat_data, psy_bat); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = battery->charging_status; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = battery->batt_health; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = battery->batt_temp; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + android_bat_update_data(battery); + val->intval = battery->batt_vcell; + if (val->intval == -1) + return -EINVAL; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = battery->batt_soc; + if (val->intval == -1) + return -EINVAL; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + android_bat_update_data(battery); + val->intval = battery->batt_current; + break; + default: + return -EINVAL; + } + return 0; +} + +static void android_bat_get_temp(struct android_bat_data *battery) +{ + int batt_temp = 25000; + int health = battery->batt_health; + + if (battery->pdata->get_temperature) + battery->pdata->get_temperature(&batt_temp); + + if (batt_temp >= battery->pdata->temp_high_threshold) { + if (health != POWER_SUPPLY_HEALTH_OVERHEAT && + health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { + pr_info("battery overheat (%d>=%d), charging unavailable\n", + batt_temp, battery->pdata->temp_high_threshold); + battery->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + } + } else if (batt_temp <= battery->pdata->temp_high_recovery && + batt_temp >= battery->pdata->temp_low_recovery) { + if (health == POWER_SUPPLY_HEALTH_OVERHEAT || + health == POWER_SUPPLY_HEALTH_COLD) { + pr_info("battery recovery (%d,%d~%d), charging available\n", + batt_temp, battery->pdata->temp_low_recovery, + battery->pdata->temp_high_recovery); + battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; + } + } else if (batt_temp <= battery->pdata->temp_low_threshold) { + if (health != POWER_SUPPLY_HEALTH_COLD && + health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { + pr_info("battery cold (%d <= %d), charging unavailable\n", + batt_temp, battery->pdata->temp_low_threshold); + battery->batt_health = POWER_SUPPLY_HEALTH_COLD; + } + } + + battery->batt_temp = batt_temp/1000; +} + +static void android_bat_update_data(struct android_bat_data *battery) +{ + int ret; + int v; + + if (battery->pdata->poll_charge_source) + battery->charge_source = battery->pdata->poll_charge_source(); + + if (battery->pdata->get_voltage_now) { + ret = battery->pdata->get_voltage_now(); + battery->batt_vcell = ret >= 0 ? ret : -1; + } + + if (battery->pdata->get_capacity) { + ret = battery->pdata->get_capacity(); + battery->batt_soc = ret >= 0 ? ret : -1; + } + + if (battery->pdata->get_current_now) { + ret = battery->pdata->get_current_now(&v); + + if (!ret) + battery->batt_current = v; + } + + android_bat_get_temp(battery); +} + +static int android_bat_enable_charging(struct android_bat_data *battery, + bool enable) +{ + if (enable && (battery->batt_health != POWER_SUPPLY_HEALTH_GOOD)) { + battery->charging_status = + POWER_SUPPLY_STATUS_NOT_CHARGING; + return -EPERM; + } + + if (enable) { + if (battery->pdata && battery->pdata->set_charging_current) + battery->pdata->set_charging_current + (battery->charge_source); + } + + if (battery->pdata && battery->pdata->set_charging_enable) + battery->pdata->set_charging_enable(enable); + + pr_info("battery: enable=%d charger: %s\n", enable, + charge_source_str(battery->charge_source)); + + return 0; +} + +static void android_bat_charge_source_changed(struct android_bat_callbacks *ptr, + int charge_source) +{ + struct android_bat_data *battery; + + battery = container_of(ptr, struct android_bat_data, callbacks); + wake_lock(&battery->charger_wake_lock); + battery->charge_source = charge_source; + + pr_info("battery: charge source type was changed: %s\n", + charge_source_str(battery->charge_source)); + + queue_work(battery->monitor_wqueue, &battery->charger_work); +} + +static void android_bat_charger_work(struct work_struct *work) +{ + struct android_bat_data *battery = + container_of(work, struct android_bat_data, charger_work); + + switch (battery->charge_source) { + case CHARGE_SOURCE_NONE: + battery->charging_status = POWER_SUPPLY_STATUS_DISCHARGING; + android_bat_enable_charging(battery, false); + if (battery->batt_health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) + battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; + break; + case CHARGE_SOURCE_USB: + case CHARGE_SOURCE_AC: + battery->charging_status = POWER_SUPPLY_STATUS_CHARGING; + android_bat_enable_charging(battery, true); + break; + default: + pr_err("%s: Invalid charger type\n", __func__); + break; + } + + wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); +} + +static void android_bat_monitor_work(struct work_struct *work) +{ + struct android_bat_data *battery; + battery = container_of(work, struct android_bat_data, + monitor_work.work); + + wake_lock(&battery->monitor_wake_lock); + android_bat_update_data(battery); + + switch (battery->charging_status) { + case POWER_SUPPLY_STATUS_FULL: + case POWER_SUPPLY_STATUS_DISCHARGING: + break; + case POWER_SUPPLY_STATUS_CHARGING: + switch (battery->batt_health) { + case POWER_SUPPLY_HEALTH_OVERHEAT: + case POWER_SUPPLY_HEALTH_COLD: + case POWER_SUPPLY_HEALTH_OVERVOLTAGE: + case POWER_SUPPLY_HEALTH_DEAD: + case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: + battery->charging_status = + POWER_SUPPLY_STATUS_NOT_CHARGING; + android_bat_enable_charging(battery, false); + + pr_info("battery: Not charging, health=%d\n", + battery->batt_health); + break; + default: + break; + } + break; + case POWER_SUPPLY_STATUS_NOT_CHARGING: + if (battery->batt_health == POWER_SUPPLY_HEALTH_GOOD) { + pr_info("battery: battery health recovered\n"); + if (battery->charge_source != CHARGE_SOURCE_NONE) { + android_bat_enable_charging(battery, true); + battery->charging_status + = POWER_SUPPLY_STATUS_CHARGING; + } else + battery->charging_status + = POWER_SUPPLY_STATUS_DISCHARGING; + } + break; + default: + wake_unlock(&battery->monitor_wake_lock); + pr_err("%s: Undefined battery status: %d\n", __func__, + battery->charging_status); + return; + } + + pr_info("battery: l=%d v=%d c=%d temp=%d h=%d st=%d type=%s\n", + battery->batt_soc, battery->batt_vcell/1000, + battery->batt_current, battery->batt_temp, battery->batt_health, + battery->charging_status, + charge_source_str(battery->charge_source)); + power_supply_changed(&battery->psy_bat); + queue_delayed_work(battery->monitor_wqueue, + &battery->monitor_work, msecs_to_jiffies(50000)); + wake_unlock(&battery->monitor_wake_lock); + + return; +} + +static int android_bat_probe(struct platform_device *pdev) +{ + struct android_bat_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct android_bat_data *battery; + int ret = 0; + + dev_info(&pdev->dev, "Android Battery Driver\n"); + battery = kzalloc(sizeof(*battery), GFP_KERNEL); + if (!battery) + return -ENOMEM; + + battery->pdata = pdata; + if (!battery->pdata) { + pr_err("%s : No platform data\n", __func__); + ret = -EINVAL; + goto err_pdata; + } + + battery->dev = &pdev->dev; + platform_set_drvdata(pdev, battery); + battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; + + battery->psy_bat.name = "android-battery", + battery->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY, + battery->psy_bat.properties = android_battery_props, + battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props), + battery->psy_bat.get_property = android_bat_get_property, + + battery->batt_vcell = -1; + battery->batt_soc = -1; + + wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND, + "android-battery-monitor"); + wake_lock_init(&battery->charger_wake_lock, WAKE_LOCK_SUSPEND, + "android-chargerdetect"); + + ret = power_supply_register(&pdev->dev, &battery->psy_bat); + if (ret) { + dev_err(battery->dev, "%s: failed to register psy_bat\n", + __func__); + goto err_psy_reg; + } + + battery->monitor_wqueue = + create_singlethread_workqueue(dev_name(&pdev->dev)); + if (!battery->monitor_wqueue) { + dev_err(battery->dev, "%s: fail to create workqueue\n", + __func__); + goto err_wq; + } + + INIT_DELAYED_WORK_DEFERRABLE(&battery->monitor_work, + android_bat_monitor_work); + INIT_WORK(&battery->charger_work, android_bat_charger_work); + + battery->callbacks.charge_source_changed = + android_bat_charge_source_changed; + if (battery->pdata && battery->pdata->register_callbacks) + battery->pdata->register_callbacks(&battery->callbacks); + + /* get initial charger status */ + if (battery->pdata->poll_charge_source) + battery->charge_source = battery->pdata->poll_charge_source(); + + wake_lock(&battery->charger_wake_lock); + queue_work(battery->monitor_wqueue, &battery->charger_work); + + queue_delayed_work(battery->monitor_wqueue, + &battery->monitor_work, msecs_to_jiffies(0)); + return 0; + + +err_wq: + power_supply_unregister(&battery->psy_bat); +err_psy_reg: + wake_lock_destroy(&battery->monitor_wake_lock); + wake_lock_destroy(&battery->charger_wake_lock); +err_pdata: + kfree(battery); + + return ret; +} + +static int android_bat_remove(struct platform_device *pdev) +{ + struct android_bat_data *battery = platform_get_drvdata(pdev); + + flush_workqueue(battery->monitor_wqueue); + destroy_workqueue(battery->monitor_wqueue); + power_supply_unregister(&battery->psy_bat); + wake_lock_destroy(&battery->monitor_wake_lock); + wake_lock_destroy(&battery->charger_wake_lock); + kfree(battery); + return 0; +} + +static int android_bat_suspend(struct device *dev) +{ + struct android_bat_data *battery = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&battery->monitor_work); + return 0; +} + +static int android_bat_resume(struct device *dev) +{ + struct android_bat_data *battery = dev_get_drvdata(dev); + + queue_delayed_work(battery->monitor_wqueue, + &battery->monitor_work, msecs_to_jiffies(0)); + return 0; +} + +static const struct dev_pm_ops android_bat_pm_ops = { + .suspend = android_bat_suspend, + .resume = android_bat_resume, +}; + +static struct platform_driver android_bat_driver = { + .driver = { + .name = "android-battery", + .owner = THIS_MODULE, + .pm = &android_bat_pm_ops, + }, + .probe = android_bat_probe, + .remove = android_bat_remove, +}; + +static int __init android_bat_init(void) +{ + return platform_driver_register(&android_bat_driver); +} + +static void __exit android_bat_exit(void) +{ + platform_driver_unregister(&android_bat_driver); +} + +late_initcall(android_bat_init); +module_exit(android_bat_exit); + +MODULE_DESCRIPTION("Android battery driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/android_battery.h b/include/linux/platform_data/android_battery.h new file mode 100644 index 000000000000..a0749f5b9d3b --- /dev/null +++ b/include/linux/platform_data/android_battery.h @@ -0,0 +1,42 @@ +/* + * android_battery.h + * + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_ANDROID_BATTERY_H +#define _LINUX_ANDROID_BATTERY_H + +enum { + CHARGE_SOURCE_NONE = 0, + CHARGE_SOURCE_AC, + CHARGE_SOURCE_USB, +}; + +struct android_bat_callbacks { + void (*charge_source_changed) + (struct android_bat_callbacks *, int); +}; + +struct android_bat_platform_data { + void (*register_callbacks)(struct android_bat_callbacks *); + void (*unregister_callbacks)(void); + void (*set_charging_current) (int); + void (*set_charging_enable) (int); + int (*poll_charge_source) (void); + int (*get_capacity) (void); + int (*get_temperature) (int *); + int (*get_voltage_now)(void); + int (*get_current_now)(int *); + + int temp_high_threshold; + int temp_high_recovery; + int temp_low_recovery; + int temp_low_threshold; +}; + +#endif From f550ef22a98d65ac710a7926c5d7aa92f2e48145 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 31 Jul 2012 20:14:05 -0700 Subject: [PATCH 162/358] power: android: add /d/android-power Change-Id: I306ae5928bb5c32dc986811cfa3a6dd98519a2d7 Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 38 ++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index ded435029677..f638e18b579c 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -32,6 +32,7 @@ #include #include #include +#include #include struct android_bat_data { @@ -60,6 +61,8 @@ struct android_bat_data { bool slow_poll; ktime_t last_poll; + + struct dentry *debugfs_entry; }; static enum power_supply_property android_battery_props[] = { @@ -330,6 +333,32 @@ static void android_bat_monitor_work(struct work_struct *work) return; } +static int android_power_debug_dump(struct seq_file *s, void *unused) +{ + struct android_bat_data *battery = s->private; + + android_bat_update_data(battery); + seq_printf(s, "l=%d v=%d c=%d temp=%d h=%d st=%d type=%s\n", + battery->batt_soc, battery->batt_vcell/1000, + battery->batt_current, battery->batt_temp, + battery->batt_health, battery->charging_status, + charge_source_str(battery->charge_source)); + + return 0; +} + +static int android_power_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, android_power_debug_dump, inode->i_private); +} + +static const struct file_operations android_power_debug_fops = { + .open = android_power_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int android_bat_probe(struct platform_device *pdev) { struct android_bat_platform_data *pdata = dev_get_platdata(&pdev->dev); @@ -399,8 +428,14 @@ static int android_bat_probe(struct platform_device *pdev) queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, msecs_to_jiffies(0)); - return 0; + battery->debugfs_entry = + debugfs_create_file("android-power", S_IRUGO, NULL, + battery, &android_power_debug_fops); + if (!battery->debugfs_entry) + pr_err("failed to create android-power debugfs entry\n"); + + return 0; err_wq: power_supply_unregister(&battery->psy_bat); @@ -422,6 +457,7 @@ static int android_bat_remove(struct platform_device *pdev) power_supply_unregister(&battery->psy_bat); wake_lock_destroy(&battery->monitor_wake_lock); wake_lock_destroy(&battery->charger_wake_lock); + debugfs_remove(battery->debugfs_entry); kfree(battery); return 0; } From d8b36fff57d3cab160be3410c905b084f861c85a Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 1 Aug 2012 18:22:22 -0700 Subject: [PATCH 163/358] power: android: battery monitor polling with wakeup alarms Based on Tuna code from 3.0 ported to 3.4 alarm API. Change-Id: I9b13209963d5a5043237e9e29f3cb07c40583af2 Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 57 +++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index f638e18b579c..867e6b59b26b 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -31,10 +31,14 @@ #include #include #include +#include #include #include #include +#define FAST_POLL (1 * 60) +#define SLOW_POLL (10 * 60) + struct android_bat_data { struct android_bat_platform_data *pdata; struct android_bat_callbacks callbacks; @@ -56,11 +60,11 @@ struct android_bat_data { unsigned int charging_status; struct workqueue_struct *monitor_wqueue; - struct delayed_work monitor_work; + struct work_struct monitor_work; struct work_struct charger_work; - bool slow_poll; - ktime_t last_poll; + struct alarm monitor_alarm; + ktime_t last_poll; struct dentry *debugfs_entry; }; @@ -270,11 +274,18 @@ static void android_bat_charger_work(struct work_struct *work) wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); } + +static void android_bat_monitor_set_alarm(struct android_bat_data *battery, + int seconds) +{ + alarm_start(&battery->monitor_alarm, + ktime_add(battery->last_poll, ktime_set(seconds, 0))); +} + static void android_bat_monitor_work(struct work_struct *work) { struct android_bat_data *battery; - battery = container_of(work, struct android_bat_data, - monitor_work.work); + battery = container_of(work, struct android_bat_data, monitor_work); wake_lock(&battery->monitor_wake_lock); android_bat_update_data(battery); @@ -314,10 +325,9 @@ static void android_bat_monitor_work(struct work_struct *work) } break; default: - wake_unlock(&battery->monitor_wake_lock); pr_err("%s: Undefined battery status: %d\n", __func__, battery->charging_status); - return; + break; } pr_info("battery: l=%d v=%d c=%d temp=%d h=%d st=%d type=%s\n", @@ -326,13 +336,23 @@ static void android_bat_monitor_work(struct work_struct *work) battery->charging_status, charge_source_str(battery->charge_source)); power_supply_changed(&battery->psy_bat); - queue_delayed_work(battery->monitor_wqueue, - &battery->monitor_work, msecs_to_jiffies(50000)); + battery->last_poll = ktime_get_boottime(); + android_bat_monitor_set_alarm(battery, FAST_POLL); wake_unlock(&battery->monitor_wake_lock); - return; } +static enum alarmtimer_restart android_bat_monitor_alarm( + struct alarm *alarm, ktime_t now) +{ + struct android_bat_data *battery = + container_of(alarm, struct android_bat_data, monitor_alarm); + + wake_lock(&battery->monitor_wake_lock); + queue_work(battery->monitor_wqueue, &battery->monitor_work); + return ALARMTIMER_NORESTART; +} + static int android_power_debug_dump(struct seq_file *s, void *unused) { struct android_bat_data *battery = s->private; @@ -410,8 +430,7 @@ static int android_bat_probe(struct platform_device *pdev) goto err_wq; } - INIT_DELAYED_WORK_DEFERRABLE(&battery->monitor_work, - android_bat_monitor_work); + INIT_WORK(&battery->monitor_work, android_bat_monitor_work); INIT_WORK(&battery->charger_work, android_bat_charger_work); battery->callbacks.charge_source_changed = @@ -426,8 +445,11 @@ static int android_bat_probe(struct platform_device *pdev) wake_lock(&battery->charger_wake_lock); queue_work(battery->monitor_wqueue, &battery->charger_work); - queue_delayed_work(battery->monitor_wqueue, - &battery->monitor_work, msecs_to_jiffies(0)); + wake_lock(&battery->monitor_wake_lock); + battery->last_poll = ktime_get_boottime(); + alarm_init(&battery->monitor_alarm, ALARM_BOOTTIME, + android_bat_monitor_alarm); + queue_work(battery->monitor_wqueue, &battery->monitor_work); battery->debugfs_entry = debugfs_create_file("android-power", S_IRUGO, NULL, @@ -452,6 +474,7 @@ static int android_bat_remove(struct platform_device *pdev) { struct android_bat_data *battery = platform_get_drvdata(pdev); + alarm_cancel(&battery->monitor_alarm); flush_workqueue(battery->monitor_wqueue); destroy_workqueue(battery->monitor_wqueue); power_supply_unregister(&battery->psy_bat); @@ -466,7 +489,8 @@ static int android_bat_suspend(struct device *dev) { struct android_bat_data *battery = dev_get_drvdata(dev); - cancel_delayed_work_sync(&battery->monitor_work); + cancel_work_sync(&battery->monitor_work); + android_bat_monitor_set_alarm(battery, SLOW_POLL); return 0; } @@ -474,8 +498,7 @@ static int android_bat_resume(struct device *dev) { struct android_bat_data *battery = dev_get_drvdata(dev); - queue_delayed_work(battery->monitor_wqueue, - &battery->monitor_work, msecs_to_jiffies(0)); + android_bat_monitor_set_alarm(battery, FAST_POLL); return 0; } From 41772299c50b8ba13640c53ada8332e232e3f4f9 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 3 Aug 2012 18:58:46 -0700 Subject: [PATCH 164/358] power: android: switch to nn.n format for temperature readings Change-Id: I67b44e5c706c94f5e2031da9dab3dddb1a4b6b3f Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index 867e6b59b26b..85f85f9e4ed8 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -148,7 +148,7 @@ static int android_bat_get_property(struct power_supply *ps, static void android_bat_get_temp(struct android_bat_data *battery) { - int batt_temp = 25000; + int batt_temp = 250000; /* 25.0C */ int health = battery->batt_health; if (battery->pdata->get_temperature) @@ -330,9 +330,10 @@ static void android_bat_monitor_work(struct work_struct *work) break; } - pr_info("battery: l=%d v=%d c=%d temp=%d h=%d st=%d type=%s\n", + pr_info("battery: l=%d v=%d c=%d temp=%d.%d h=%d st=%d type=%s\n", battery->batt_soc, battery->batt_vcell/1000, - battery->batt_current, battery->batt_temp, battery->batt_health, + battery->batt_current, battery->batt_temp / 10, + battery->batt_temp % 10, battery->batt_health, battery->charging_status, charge_source_str(battery->charge_source)); power_supply_changed(&battery->psy_bat); From 0d98b800f5c02def5f2ecb1d0631dc129292647b Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 6 Aug 2012 12:01:51 -0700 Subject: [PATCH 165/358] power: android_battery: fix negative temperature display Change-Id: Ic9798bc5194b6192fec726a8f136867f111e4765 Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index 85f85f9e4ed8..80c0bd228aa2 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -330,11 +330,11 @@ static void android_bat_monitor_work(struct work_struct *work) break; } - pr_info("battery: l=%d v=%d c=%d temp=%d.%d h=%d st=%d type=%s\n", + pr_info("battery: l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d type=%s\n", battery->batt_soc, battery->batt_vcell/1000, - battery->batt_current, battery->batt_temp / 10, - battery->batt_temp % 10, battery->batt_health, - battery->charging_status, + battery->batt_current, battery->batt_temp < 0 ? "-" : "", + abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), + battery->batt_health, battery->charging_status, charge_source_str(battery->charge_source)); power_supply_changed(&battery->psy_bat); battery->last_poll = ktime_get_boottime(); From 3587f80ea0ec829fae8af00795799f9c2cdb90a5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 6 Aug 2012 18:18:31 -0700 Subject: [PATCH 166/358] power: android_battery: nn.n temperature format for /d/android-power Change-Id: I0c20effcadd3c0673d04be190133883b3c2bf599 Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index 80c0bd228aa2..a9708561eaf8 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -359,9 +359,10 @@ static int android_power_debug_dump(struct seq_file *s, void *unused) struct android_bat_data *battery = s->private; android_bat_update_data(battery); - seq_printf(s, "l=%d v=%d c=%d temp=%d h=%d st=%d type=%s\n", + seq_printf(s, "l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d type=%s\n", battery->batt_soc, battery->batt_vcell/1000, - battery->batt_current, battery->batt_temp, + battery->batt_current, battery->batt_temp < 0 ? "-" : "", + abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), battery->batt_health, battery->charging_status, charge_source_str(battery->charge_source)); From b94fc6ae458941f7f0037df0dd0fe85371bb3702 Mon Sep 17 00:00:00 2001 From: Ashish Sharma Date: Mon, 13 Aug 2012 13:19:57 -0700 Subject: [PATCH 167/358] netfilter: xt_IDLETIMER: Rename INTERFACE to LABEL in netlink notification. Change-Id: Iaeca5dd2d7878c0733923ae03309a2a7b86979ca Signed-off-by: Ashish Sharma --- net/netfilter/xt_IDLETIMER.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index df91e26f55f2..f4ba86348660 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -68,15 +68,15 @@ static DEFINE_MUTEX(list_mutex); static struct kobject *idletimer_tg_kobj; -static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) +static void notify_netlink_uevent(const char *label, struct idletimer_tg *timer) { - char iface_msg[NLMSG_MAX_SIZE]; + char label_msg[NLMSG_MAX_SIZE]; char state_msg[NLMSG_MAX_SIZE]; - char *envp[] = { iface_msg, state_msg, NULL }; + char *envp[] = { label_msg, state_msg, NULL }; int res; - res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s", - iface); + res = snprintf(label_msg, NLMSG_MAX_SIZE, "LABEL=%s", + label); if (NLMSG_MAX_SIZE <= res) { pr_err("message too long (%d)", res); return; @@ -87,7 +87,7 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) pr_err("message too long (%d)", res); return; } - pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); + pr_debug("putting nlmsg: <%s> <%s>\n", label_msg, state_msg); kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); return; From 0808371a574b616fb13671bd65a0a71c23ddc0e2 Mon Sep 17 00:00:00 2001 From: "Mike J. Chen" Date: Thu, 24 May 2012 15:12:36 -0700 Subject: [PATCH 168/358] ARM: disable preemption in machine_shutdown Since the smp call to stop the other cpus are handled in those cpus in interrupt context, there's a potential for those smp handlers to interrupt threads holding spin locks (such as the one a mutex holds). This prevents those threads from ever releasing their spin lock, so if the cpu doing the shutdown is allowed to switch to another thread that tries to grab the same lock/mutex, we could get into a deadlock (the spin lock call is called with preemption disabled in the mutex lock code). To avoid that possibility, disable preemption before doing the smp_send_stop(). Change-Id: I7976c5382d7173fcb3cd14da8cc5083d442b2544 Signed-off-by: Mike J. Chen --- arch/arm/kernel/process.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index dc7eda3b4ee4..618117c8e063 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -227,6 +227,16 @@ void arch_cpu_idle(void) */ void machine_shutdown(void) { +#ifdef CONFIG_SMP + /* + * Disable preemption so we're guaranteed to + * run to power off or reboot and prevent + * the possibility of switching to another + * thread that might wind up blocking on + * one of the stopped CPUs. + */ + preempt_disable(); +#endif disable_nonboot_cpus(); } From f03455fa499537fc1001022b511e3e1936d6eadf Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 22 Aug 2012 19:02:26 -0700 Subject: [PATCH 169/358] power: android-battery: Provide fake values when fuel gauge missing Avoid shutdown due to battery capacity or temperature out of range. Change-Id: I54abe4dc54bf2fb8a9b972a3940e6fd93cc60473 Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index a9708561eaf8..cc8e62887ffb 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -148,7 +148,7 @@ static int android_bat_get_property(struct power_supply *ps, static void android_bat_get_temp(struct android_bat_data *battery) { - int batt_temp = 250000; /* 25.0C */ + int batt_temp = 42000; /* 4.2C */ int health = battery->batt_health; if (battery->pdata->get_temperature) @@ -192,12 +192,12 @@ static void android_bat_update_data(struct android_bat_data *battery) if (battery->pdata->get_voltage_now) { ret = battery->pdata->get_voltage_now(); - battery->batt_vcell = ret >= 0 ? ret : -1; + battery->batt_vcell = ret >= 0 ? ret : 4242000; } if (battery->pdata->get_capacity) { ret = battery->pdata->get_capacity(); - battery->batt_soc = ret >= 0 ? ret : -1; + battery->batt_soc = ret >= 0 ? ret : 42; } if (battery->pdata->get_current_now) { From e5f0b22978bfcff64be4ed1d8778a05c4eab3e27 Mon Sep 17 00:00:00 2001 From: "hongmin.son" Date: Thu, 23 Aug 2012 13:11:30 -0700 Subject: [PATCH 170/358] power: android-battery: Add USB and AC battery power supplies For reflecting board-level decisions on whether USB or AC charger is connected, which may differ from charger power supply notion of USB input path (which may be connected to an AC charger) vs. AC input path. Change-Id: I9d2eb446db8b4d9496ba7ce1472d0e7a4d4e24ef [toddpoynor@google.com: refactoring] Signed-off-by: hongmin.son Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 81 ++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index cc8e62887ffb..39f459303882 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -46,6 +46,8 @@ struct android_bat_data { struct device *dev; struct power_supply psy_bat; + struct power_supply psy_usb; + struct power_supply psy_ac; struct wake_lock monitor_wake_lock; struct wake_lock charger_wake_lock; @@ -69,6 +71,10 @@ struct android_bat_data { struct dentry *debugfs_entry; }; +static char *supply_list[] = { + "android-battery", +}; + static enum power_supply_property android_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, @@ -81,6 +87,10 @@ static enum power_supply_property android_battery_props[] = { POWER_SUPPLY_PROP_CURRENT_NOW, }; +static enum power_supply_property android_power_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + static void android_bat_update_data(struct android_bat_data *battery); static char *charge_source_str(int charge_source) @@ -146,6 +156,36 @@ static int android_bat_get_property(struct power_supply *ps, return 0; } +static int android_usb_get_property(struct power_supply *ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct android_bat_data *battery = container_of(ps, + struct android_bat_data, psy_usb); + + if (psp != POWER_SUPPLY_PROP_ONLINE) + return -EINVAL; + + val->intval = (battery->charge_source == CHARGE_SOURCE_USB); + + return 0; +} + +static int android_ac_get_property(struct power_supply *ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct android_bat_data *battery = container_of(ps, + struct android_bat_data, psy_ac); + + if (psp != POWER_SUPPLY_PROP_ONLINE) + return -EINVAL; + + val->intval = (battery->charge_source == CHARGE_SOURCE_AC); + + return 0; +} + static void android_bat_get_temp(struct android_bat_data *battery) { int batt_temp = 42000; /* 4.2C */ @@ -271,6 +311,9 @@ static void android_bat_charger_work(struct work_struct *work) break; } + power_supply_changed(&battery->psy_ac); + power_supply_changed(&battery->psy_usb); + wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); } @@ -409,6 +452,22 @@ static int android_bat_probe(struct platform_device *pdev) battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props), battery->psy_bat.get_property = android_bat_get_property, + battery->psy_usb.name = "android-usb", + battery->psy_usb.type = POWER_SUPPLY_TYPE_USB, + battery->psy_usb.supplied_to = supply_list, + battery->psy_usb.num_supplicants = ARRAY_SIZE(supply_list), + battery->psy_usb.properties = android_power_props, + battery->psy_usb.num_properties = ARRAY_SIZE(android_power_props), + battery->psy_usb.get_property = android_usb_get_property, + + battery->psy_ac.name = "android-ac", + battery->psy_ac.type = POWER_SUPPLY_TYPE_MAINS, + battery->psy_ac.supplied_to = supply_list, + battery->psy_ac.num_supplicants = ARRAY_SIZE(supply_list), + battery->psy_ac.properties = android_power_props, + battery->psy_ac.num_properties = ARRAY_SIZE(android_power_props), + battery->psy_ac.get_property = android_ac_get_property; + battery->batt_vcell = -1; battery->batt_soc = -1; @@ -421,7 +480,21 @@ static int android_bat_probe(struct platform_device *pdev) if (ret) { dev_err(battery->dev, "%s: failed to register psy_bat\n", __func__); - goto err_psy_reg; + goto err_psy_bat_reg; + } + + ret = power_supply_register(&pdev->dev, &battery->psy_usb); + if (ret) { + dev_err(battery->dev, "%s: failed to register psy_usb\n", + __func__); + goto err_psy_usb_reg; + } + + ret = power_supply_register(&pdev->dev, &battery->psy_ac); + if (ret) { + dev_err(battery->dev, "%s: failed to register psy_ac\n", + __func__); + goto err_psy_ac_reg; } battery->monitor_wqueue = @@ -462,8 +535,12 @@ static int android_bat_probe(struct platform_device *pdev) return 0; err_wq: + power_supply_unregister(&battery->psy_ac); +err_psy_ac_reg: + power_supply_unregister(&battery->psy_usb); +err_psy_usb_reg: power_supply_unregister(&battery->psy_bat); -err_psy_reg: +err_psy_bat_reg: wake_lock_destroy(&battery->monitor_wake_lock); wake_lock_destroy(&battery->charger_wake_lock); err_pdata: From 64fff6a384d9a5ac98a822760fa14841fa19c69b Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 23 Aug 2012 13:41:54 -0700 Subject: [PATCH 171/358] power: android-battery: use 1/10th deg C units for temperature Same units as power_supply, don't need 1/10000 deg C and conversion is error-prone. Change-Id: I8cf146d2a7725df2dacc415daa66b2986b69f2a7 Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index 39f459303882..e3b1f5e87b28 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -188,7 +188,7 @@ static int android_ac_get_property(struct power_supply *ps, static void android_bat_get_temp(struct android_bat_data *battery) { - int batt_temp = 42000; /* 4.2C */ + int batt_temp = 42; /* 4.2C */ int health = battery->batt_health; if (battery->pdata->get_temperature) @@ -219,7 +219,7 @@ static void android_bat_get_temp(struct android_bat_data *battery) } } - battery->batt_temp = batt_temp/1000; + battery->batt_temp = batt_temp; } static void android_bat_update_data(struct android_bat_data *battery) From 8bf1968c939a447247654ce50dc49d42a1b546f9 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 28 Aug 2012 16:53:32 -0700 Subject: [PATCH 172/358] netfilter: xt_qtaguid: report only uid tags to non-privileged processes In the past, a process could only see its own stats (uid-based summary, and details). Now we allow any process to see other UIDs uid-based stats, but still hide the detailed stats. Change-Id: I7666961ed244ac1d9359c339b048799e5db9facc Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 9fd0ffa6c365..14b003da1423 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2588,8 +2588,9 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) } else { tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); - - if (!can_read_other_uid_stats(stat_uid)) { + /* Detailed tags are not available to everybody */ + if (get_atag_from_tag(tag) + && !can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " "from pid=%u tgid=%u uid=%u\n", From d70f9a9d6445d1d997d177180571d0ff50094bdb Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 30 Aug 2012 23:09:14 -0700 Subject: [PATCH 173/358] w1: ds2482: Manage SLPZ pin sleep state Place SLPZ pin in sleep state at system suspend time if a GPIO is provided by board platform data. Change-Id: I93c61fa0ae474e968e0f909209c9bfcaafe3dd2c Signed-off-by: Todd Poynor --- drivers/w1/masters/ds2482.c | 47 +++++++++++++++++++++++++++- include/linux/platform_data/ds2482.h | 21 +++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 include/linux/platform_data/ds2482.h diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index e033491fe308..ab29939ea4cd 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include "../w1.h" @@ -84,7 +86,8 @@ static const u8 ds2482_chan_rd[8] = static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id); static int ds2482_remove(struct i2c_client *client); - +static int ds2482_suspend(struct device *dev); +static int ds2482_resume(struct device *dev); /** * Driver data (common to all clients) @@ -94,10 +97,16 @@ static const struct i2c_device_id ds2482_id[] = { { } }; +static const struct dev_pm_ops ds2482_pm_ops = { + .suspend = ds2482_suspend, + .resume = ds2482_resume, +}; + static struct i2c_driver ds2482_driver = { .driver = { .owner = THIS_MODULE, .name = "ds2482", + .pm = &ds2482_pm_ops, }, .probe = ds2482_probe, .remove = ds2482_remove, @@ -119,6 +128,7 @@ struct ds2482_w1_chan { struct ds2482_data { struct i2c_client *client; struct mutex access_lock; + int slpz_gpio; /* 1-wire interface(s) */ int w1_count; /* 1 or 8 */ @@ -444,11 +454,31 @@ static u8 ds2482_w1_set_pullup(void *data, int delay) return retval; } +static int ds2482_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds2482_data *data = i2c_get_clientdata(client); + + if (data->slpz_gpio >= 0) + gpio_set_value(data->slpz_gpio, 0); + return 0; +} + +static int ds2482_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds2482_data *data = i2c_get_clientdata(client); + + if (data->slpz_gpio >= 0) + gpio_set_value(data->slpz_gpio, 1); + return 0; +} static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ds2482_data *data; + struct ds2482_platform_data *pdata; int err = -ENODEV; int temp1; int idx; @@ -515,6 +545,16 @@ static int ds2482_probe(struct i2c_client *client, } } + pdata = client->dev.platform_data; + data->slpz_gpio = pdata ? pdata->slpz_gpio : -1; + + if (data->slpz_gpio >= 0) { + err = gpio_request_one(data->slpz_gpio, GPIOF_OUT_INIT_HIGH, + "ds2482.slpz"); + if (err < 0) + goto exit_w1_remove; + } + return 0; exit_w1_remove: @@ -539,6 +579,11 @@ static int ds2482_remove(struct i2c_client *client) w1_remove_master_device(&data->w1_ch[idx].w1_bm); } + if (data->slpz_gpio >= 0) { + gpio_set_value(data->slpz_gpio, 0); + gpio_free(data->slpz_gpio); + } + /* Free the memory */ kfree(data); return 0; diff --git a/include/linux/platform_data/ds2482.h b/include/linux/platform_data/ds2482.h new file mode 100644 index 000000000000..5a6879e2a09a --- /dev/null +++ b/include/linux/platform_data/ds2482.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2012 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 __PLATFORM_DATA_DS2482__ +#define __PLATFORM_DATA_DS2482__ + +struct ds2482_platform_data { + int slpz_gpio; +}; + +#endif /* __PLATFORM_DATA_DS2482__ */ From 238e3775690ba4111033a3d5c3e9cc298f0fc547 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 11 May 2012 09:00:40 -0700 Subject: [PATCH 174/358] USB: gadget: Add ACCESSORY_SET_AUDIO_MODE control request and ioctl The control request will be used by the host to enable/disable USB audio and the ioctl will be used by userspace to read the audio mode Change-Id: I81c38611b588451e80eacdccc417ca6e11c60cab Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 11 ++++++++++- include/linux/usb/f_accessory.h | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 04ceb0996560..07c29d9b9264 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -40,7 +40,7 @@ #define BULK_BUFFER_SIZE 16384 #define ACC_STRING_SIZE 256 -#define PROTOCOL_VERSION 1 +#define PROTOCOL_VERSION 2 /* String IDs */ #define INTERFACE_STRING_INDEX 0 @@ -78,6 +78,8 @@ struct acc_dev { /* set to 1 if we have a pending start request */ int start_requested; + int audio_mode; + /* synchronize access to our device file */ atomic_t open_excl; @@ -510,6 +512,8 @@ static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) break; case ACCESSORY_IS_START_REQUESTED: return dev->start_requested; + case ACCESSORY_GET_AUDIO_MODE: + return dev->audio_mode; } if (!src) return -EINVAL; @@ -586,6 +590,10 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, cdev->gadget->ep0->driver_data = dev; cdev->req->complete = acc_complete_set_string; value = w_length; + } else if (b_request == ACCESSORY_SET_AUDIO_MODE && + w_index == 0 && w_length == 0) { + dev->audio_mode = w_value; + value = 0; } } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_GET_PROTOCOL) { @@ -600,6 +608,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, memset(dev->uri, 0, sizeof(dev->uri)); memset(dev->serial, 0, sizeof(dev->serial)); dev->start_requested = 0; + dev->audio_mode = 0; } } diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index 5b2dcf9728e1..ddb2fd0e88fb 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -36,13 +36,15 @@ #define ACCESSORY_STRING_URI 4 #define ACCESSORY_STRING_SERIAL 5 -/* Control request for retrieving device's protocol version (currently 1) +/* Control request for retrieving device's protocol version * * requestType: USB_DIR_IN | USB_TYPE_VENDOR * request: ACCESSORY_GET_PROTOCOL * value: 0 * index: 0 * data version number (16 bits little endian) + * 1 for original accessory support + * 2 adds device to host audio support */ #define ACCESSORY_GET_PROTOCOL 51 @@ -70,6 +72,17 @@ */ #define ACCESSORY_START 53 +/* Control request for setting the audio mode. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_AUDIO_MODE + * value: 0 - no audio + * 1 - device to host, 44100 16-bit stereo PCM + * index: 0 + * data none + */ +#define ACCESSORY_SET_AUDIO_MODE 58 + /* ioctls for retrieving strings set by the host */ #define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) #define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) @@ -79,5 +92,7 @@ #define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) /* returns 1 if there is a start request pending */ #define ACCESSORY_IS_START_REQUESTED _IO('M', 7) +/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ +#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) #endif /* __LINUX_USB_F_ACCESSORY_H */ From 23d5195682b377a979351c0ac5eb5d94f202ea23 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 26 Mar 2012 11:03:55 -0700 Subject: [PATCH 175/358] USB: gadget: f_accessory: Add support for HID input devices Change-Id: I4f1452db32508382df52acdc47c0eb395ae328c7 Signed-off-by: Mike Lockwood --- drivers/usb/gadget/android.c | 5 + drivers/usb/gadget/f_accessory.c | 391 ++++++++++++++++++++++++++++++- include/linux/usb/f_accessory.h | 50 +++- 3 files changed, 437 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index ff60af768228..5f7c5bb0a888 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -1421,6 +1421,11 @@ static void android_disconnect(struct usb_gadget *gadget) unsigned long flags; composite_disconnect(gadget); + /* accessory HID support can be active while the + accessory function is not actually enabled, + so we need to inform it when we are disconnected. + */ + acc_disconnect(); spin_lock_irqsave(&cdev->lock, flags); dev->connected = 0; diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 07c29d9b9264..921db5a193d6 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -33,6 +33,8 @@ #include #include +#include +#include #include #include #include @@ -49,6 +51,20 @@ #define TX_REQ_MAX 4 #define RX_REQ_MAX 2 +struct acc_hid_dev { + struct list_head list; + struct hid_device *hid; + struct acc_dev *dev; + /* accessory defined ID */ + int id; + /* HID report descriptor */ + u8 *report_desc; + /* length of HID report descriptor */ + int report_desc_len; + /* number of bytes of report_desc we have received so far */ + int report_desc_offset; +}; + struct acc_dev { struct usb_function function; struct usb_composite_dev *cdev; @@ -89,7 +105,21 @@ struct acc_dev { wait_queue_head_t write_wq; struct usb_request *rx_req[RX_REQ_MAX]; int rx_done; - struct delayed_work work; + + /* delayed work for handling ACCESSORY_START */ + struct delayed_work start_work; + + /* worker for registering and unregistering hid devices */ + struct work_struct hid_work; + + /* list of active HID devices */ + struct list_head hid_list; + + /* list of new HID devices to register */ + struct list_head new_hid_list; + + /* list of dead HID devices to unregister */ + struct list_head dead_hid_list; }; static struct usb_interface_descriptor acc_interface_desc = { @@ -298,7 +328,161 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) } } -static int create_bulk_endpoints(struct acc_dev *dev, +static void acc_complete_set_hid_report_desc(struct usb_ep *ep, + struct usb_request *req) +{ + struct acc_hid_dev *hid = req->context; + struct acc_dev *dev = hid->dev; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_set_hid_report_desc, err %d\n", + req->status); + return; + } + + memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length); + hid->report_desc_offset += length; + if (hid->report_desc_offset == hid->report_desc_len) { + /* After we have received the entire report descriptor + * we schedule work to initialize the HID device + */ + schedule_work(&dev->hid_work); + } +} + +static void acc_complete_send_hid_event(struct usb_ep *ep, + struct usb_request *req) +{ + struct acc_hid_dev *hid = req->context; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_send_hid_event, err %d\n", req->status); + return; + } + + hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1); +} + +static int acc_hid_parse(struct hid_device *hid) +{ + struct acc_hid_dev *hdev = hid->driver_data; + + hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len); + return 0; +} + +static int acc_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void acc_hid_stop(struct hid_device *hid) +{ +} + +static int acc_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void acc_hid_close(struct hid_device *hid) +{ +} + +static struct hid_ll_driver acc_hid_ll_driver = { + .parse = acc_hid_parse, + .start = acc_hid_start, + .stop = acc_hid_stop, + .open = acc_hid_open, + .close = acc_hid_close, +}; + +static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, + int id, int desc_len) +{ + struct acc_hid_dev *hdev; + + hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC); + if (!hdev) + return NULL; + hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC); + if (!hdev->report_desc) { + kfree(hdev); + return NULL; + } + hdev->dev = dev; + hdev->id = id; + hdev->report_desc_len = desc_len; + + return hdev; +} + +static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id) +{ + struct acc_hid_dev *hid; + + list_for_each_entry(hid, list, list) { + if (hid->id == id) + return hid; + } + return NULL; +} + +static int acc_register_hid(struct acc_dev *dev, int id, int desc_length) +{ + struct acc_hid_dev *hid; + unsigned long flags; + + /* report descriptor length must be > 0 */ + if (desc_length <= 0) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + /* replace HID if one already exists with this ID */ + hid = acc_hid_get(&dev->hid_list, id); + if (!hid) + hid = acc_hid_get(&dev->new_hid_list, id); + if (hid) + list_move(&hid->list, &dev->dead_hid_list); + + hid = acc_hid_new(dev, id, desc_length); + if (!hid) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ENOMEM; + } + + list_add(&hid->list, &dev->new_hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + + /* schedule work to register the HID device */ + schedule_work(&dev->hid_work); + return 0; +} + +static int acc_unregister_hid(struct acc_dev *dev, int id) +{ + struct acc_hid_dev *hid; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->hid_list, id); + if (!hid) + hid = acc_hid_get(&dev->new_hid_list, id); + if (!hid) { + spin_unlock_irqrestore(&dev->lock, flags); + return -EINVAL; + } + + list_move(&hid->list, &dev->dead_hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + + schedule_work(&dev->hid_work); + return 0; +} + +static int __init create_bulk_endpoints(struct acc_dev *dev, struct usb_endpoint_descriptor *in_desc, struct usb_endpoint_descriptor *out_desc) { @@ -355,7 +539,7 @@ static int create_bulk_endpoints(struct acc_dev *dev, return 0; fail: - printk(KERN_ERR "acc_bind() could not allocate requests\n"); + pr_err("acc_bind() could not allocate requests\n"); while ((req = req_get(dev, &dev->tx_idle))) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -544,7 +728,7 @@ static int acc_release(struct inode *ip, struct file *fp) return 0; } -/* file operations for /dev/acc_usb */ +/* file operations for /dev/usb_accessory */ static const struct file_operations acc_fops = { .owner = THIS_MODULE, .read = acc_read, @@ -554,23 +738,47 @@ static const struct file_operations acc_fops = { .release = acc_release, }; +static int acc_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) + return ret; + return hid_hw_start(hdev, HID_CONNECT_DEFAULT); +} + static struct miscdevice acc_device = { .minor = MISC_DYNAMIC_MINOR, .name = "usb_accessory", .fops = &acc_fops, }; +static const struct hid_device_id acc_hid_table[] = { + { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, + { } +}; + +static struct hid_driver acc_hid_driver = { + .name = "USB accessory", + .id_table = acc_hid_table, + .probe = acc_hid_probe, +}; static int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { struct acc_dev *dev = _acc_dev; int value = -EOPNOTSUPP; + struct acc_hid_dev *hid; + int offset; u8 b_requestType = ctrl->bRequestType; u8 b_request = ctrl->bRequest; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); + unsigned long flags; /* printk(KERN_INFO "acc_ctrlrequest " @@ -583,7 +791,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, if (b_request == ACCESSORY_START) { dev->start_requested = 1; schedule_delayed_work( - &dev->work, msecs_to_jiffies(10)); + &dev->start_work, msecs_to_jiffies(10)); value = 0; } else if (b_request == ACCESSORY_SEND_STRING) { dev->string_index = w_index; @@ -594,6 +802,38 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, w_index == 0 && w_length == 0) { dev->audio_mode = w_value; value = 0; + } else if (b_request == ACCESSORY_REGISTER_HID) { + value = acc_register_hid(dev, w_value, w_index); + } else if (b_request == ACCESSORY_UNREGISTER_HID) { + value = acc_unregister_hid(dev, w_value); + } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) { + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->new_hid_list, w_value); + spin_unlock_irqrestore(&dev->lock, flags); + if (!hid) { + value = -EINVAL; + goto err; + } + offset = w_index; + if (offset != hid->report_desc_offset + || offset + w_length > hid->report_desc_len) { + value = -EINVAL; + goto err; + } + cdev->req->context = hid; + cdev->req->complete = acc_complete_set_hid_report_desc; + value = w_length; + } else if (b_request == ACCESSORY_SEND_HID_EVENT) { + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->hid_list, w_value); + spin_unlock_irqrestore(&dev->lock, flags); + if (!hid) { + value = -EINVAL; + goto err; + } + cdev->req->context = hid; + cdev->req->complete = acc_complete_send_hid_event; + value = w_length; } } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_GET_PROTOCOL) { @@ -621,6 +861,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, __func__); } +err: if (value == -EOPNOTSUPP) VDBG(cdev, "unknown class-specific control req " @@ -640,6 +881,10 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) DBG(cdev, "acc_function_bind dev: %p\n", dev); + ret = hid_register_driver(&acc_hid_driver); + if (ret) + return ret; + dev->start_requested = 0; /* allocate interface ID(s) */ @@ -668,6 +913,36 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; } +static void +kill_all_hid_devices(struct acc_dev *dev) +{ + struct acc_hid_dev *hid; + struct list_head *entry, *temp; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_for_each_safe(entry, temp, &dev->hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + list_add(&hid->list, &dev->dead_hid_list); + } + list_for_each_safe(entry, temp, &dev->new_hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + list_add(&hid->list, &dev->dead_hid_list); + } + spin_unlock_irqrestore(&dev->lock, flags); + + schedule_work(&dev->hid_work); +} + +static void +acc_hid_unbind(struct acc_dev *dev) +{ + hid_unregister_driver(&acc_hid_driver); + kill_all_hid_devices(dev); +} + static void acc_function_unbind(struct usb_configuration *c, struct usb_function *f) { @@ -679,14 +954,104 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) acc_request_free(dev->rx_req[i], dev->ep_out); + + acc_hid_unbind(dev); } -static void acc_work(struct work_struct *data) +static void acc_start_work(struct work_struct *data) { char *envp[2] = { "ACCESSORY=START", NULL }; kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); } +static int acc_hid_init(struct acc_hid_dev *hdev) +{ + struct hid_device *hid; + int ret; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &acc_hid_ll_driver; + hid->dev.parent = acc_device.this_device; + + hid->bus = BUS_USB; + hid->vendor = HID_ANY_ID; + hid->product = HID_ANY_ID; + hid->driver_data = hdev; + ret = hid_add_device(hid); + if (ret) { + pr_err("can't add hid device: %d\n", ret); + hid_destroy_device(hid); + return ret; + } + + hdev->hid = hid; + return 0; +} + +static void acc_hid_delete(struct acc_hid_dev *hid) +{ + kfree(hid->report_desc); + kfree(hid); +} + +static void acc_hid_work(struct work_struct *data) +{ + struct acc_dev *dev = _acc_dev; + struct list_head *entry, *temp; + struct acc_hid_dev *hid; + struct list_head new_list, dead_list; + unsigned long flags; + + INIT_LIST_HEAD(&new_list); + + spin_lock_irqsave(&dev->lock, flags); + + /* copy hids that are ready for initialization to new_list */ + list_for_each_safe(entry, temp, &dev->new_hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + if (hid->report_desc_offset == hid->report_desc_len) + list_move(&hid->list, &new_list); + } + + if (list_empty(&dev->dead_hid_list)) { + INIT_LIST_HEAD(&dead_list); + } else { + /* move all of dev->dead_hid_list to dead_list */ + dead_list.prev = dev->dead_hid_list.prev; + dead_list.next = dev->dead_hid_list.next; + dead_list.next->prev = &dead_list; + dead_list.prev->next = &dead_list; + INIT_LIST_HEAD(&dev->dead_hid_list); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + /* register new HID devices */ + list_for_each_safe(entry, temp, &new_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + if (acc_hid_init(hid)) { + pr_err("can't add HID device %p\n", hid); + acc_hid_delete(hid); + } else { + spin_lock_irqsave(&dev->lock, flags); + list_move(&hid->list, &dev->hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + } + } + + /* remove dead HID devices */ + list_for_each_safe(entry, temp, &dead_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + if (hid->hid) + hid_destroy_device(hid->hid); + acc_hid_delete(hid); + } +} + static int acc_function_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { @@ -780,7 +1145,11 @@ static int acc_setup(void) init_waitqueue_head(&dev->write_wq); atomic_set(&dev->open_excl, 0); INIT_LIST_HEAD(&dev->tx_idle); - INIT_DELAYED_WORK(&dev->work, acc_work); + INIT_LIST_HEAD(&dev->hid_list); + INIT_LIST_HEAD(&dev->new_hid_list); + INIT_LIST_HEAD(&dev->dead_hid_list); + INIT_DELAYED_WORK(&dev->start_work, acc_start_work); + INIT_WORK(&dev->hid_work, acc_hid_work); /* _acc_dev must be set before calling usb_gadget_register_driver */ _acc_dev = dev; @@ -793,10 +1162,16 @@ static int acc_setup(void) err: kfree(dev); - printk(KERN_ERR "USB accessory gadget driver failed to initialize\n"); + pr_err("USB accessory gadget driver failed to initialize\n"); return ret; } +static void acc_disconnect(void) +{ + /* unregister all HID devices if USB is disconnected */ + kill_all_hid_devices(_acc_dev); +} + static void acc_cleanup(void) { misc_deregister(&acc_device); diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index ddb2fd0e88fb..61ebe0aabc5b 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -44,7 +44,7 @@ * index: 0 * data version number (16 bits little endian) * 1 for original accessory support - * 2 adds device to host audio support + * 2 adds HID and device to host audio support */ #define ACCESSORY_GET_PROTOCOL 51 @@ -72,6 +72,54 @@ */ #define ACCESSORY_START 53 +/* Control request for registering a HID device. + * Upon registering, a unique ID is sent by the accessory in the + * value parameter. This ID will be used for future commands for + * the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID_DEVICE + * value: Accessory assigned ID for the HID device + * index: total length of the HID report descriptor + * data none + */ +#define ACCESSORY_REGISTER_HID 54 + +/* Control request for unregistering a HID device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID + * value: Accessory assigned ID for the HID device + * index: 0 + * data none + */ +#define ACCESSORY_UNREGISTER_HID 55 + +/* Control request for sending the HID report descriptor. + * If the HID descriptor is longer than the endpoint zero max packet size, + * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC + * commands. The data for the descriptor must be sent sequentially + * if multiple packets are needed. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_HID_REPORT_DESC + * value: Accessory assigned ID for the HID device + * index: offset of data in descriptor + * (needed when HID descriptor is too big for one packet) + * data the HID report descriptor + */ +#define ACCESSORY_SET_HID_REPORT_DESC 56 + +/* Control request for sending HID events. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_HID_EVENT + * value: Accessory assigned ID for the HID device + * index: 0 + * data the HID report for the event + */ +#define ACCESSORY_SEND_HID_EVENT 57 + /* Control request for setting the audio mode. * * requestType: USB_DIR_OUT | USB_TYPE_VENDOR From 384820bf96bb36e29744dda79b775e5d0a175a42 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 11 May 2012 09:01:08 -0700 Subject: [PATCH 176/358] USB: gadget: f_audio_source: New gadget driver for audio output This driver presents a standard USB audio class interface to the host and an ALSA PCM device to userspace Change-Id: If16b14a5ff27045f9cb2daaf1ae9195c5eeab7d0 Signed-off-by: Mike Lockwood --- drivers/usb/gadget/android.c | 63 +++ drivers/usb/gadget/f_audio_source.c | 828 ++++++++++++++++++++++++++++ 2 files changed, 891 insertions(+) create mode 100644 drivers/usb/gadget/f_audio_source.c diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 5f7c5bb0a888..32679eec28ed 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -43,6 +43,7 @@ #include "composite.c" #include "f_fs.c" +#include "f_audio_source.c" #include "f_mass_storage.c" #include "u_serial.c" #include "f_acm.c" @@ -917,6 +918,67 @@ static struct android_usb_function accessory_function = { .ctrlrequest = accessory_function_ctrlrequest, }; +static int audio_source_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + struct audio_source_config *config; + + config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL); + if (!config) + return -ENOMEM; + config->card = -1; + config->device = -1; + f->config = config; + return 0; +} + +static void audio_source_function_cleanup(struct android_usb_function *f) +{ + kfree(f->config); +} + +static int audio_source_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct audio_source_config *config = f->config; + + return audio_source_bind_config(c, config); +} + +static void audio_source_function_unbind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct audio_source_config *config = f->config; + + config->card = -1; + config->device = -1; +} + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct audio_source_config *config = f->config; + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", config->card, config->device); +} + +static DEVICE_ATTR(pcm, S_IRUGO | S_IWUSR, audio_source_pcm_show, NULL); + +static struct device_attribute *audio_source_function_attributes[] = { + &dev_attr_pcm, + NULL +}; + +static struct android_usb_function audio_source_function = { + .name = "audio_source", + .init = audio_source_function_init, + .cleanup = audio_source_function_cleanup, + .bind_config = audio_source_function_bind_config, + .unbind_config = audio_source_function_unbind_config, + .attributes = audio_source_function_attributes, +}; static struct android_usb_function *supported_functions[] = { &ffs_function, @@ -927,6 +989,7 @@ static struct android_usb_function *supported_functions[] = { &rndis_function, &mass_storage_function, &accessory_function, + &audio_source_function, NULL }; diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c new file mode 100644 index 000000000000..c757409edf94 --- /dev/null +++ b/drivers/usb/gadget/f_audio_source.c @@ -0,0 +1,828 @@ +/* + * Gadget Function Driver for USB audio source device + * + * Copyright (C) 2012 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 +#include +#include +#include +#include +#include + +#define SAMPLE_RATE 44100 +#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) + +#define IN_EP_MAX_PACKET_SIZE 384 + +/* Number of requests to allocate */ +#define IN_EP_REQ_COUNT 4 + +#define AUDIO_AC_INTERFACE 0 +#define AUDIO_AS_INTERFACE 1 +#define AUDIO_NUM_INTERFACES 2 + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, +}; + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); + +#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES) +/* 1 input terminal, 1 output terminal and 1 feature unit */ +#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ + + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \ + + UAC_DT_FEATURE_UNIT_SIZE(0)) +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_2 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_LENGTH, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_HEADER, + .bcdADC = __constant_cpu_to_le16(0x0100), + .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), + .bInCollection = AUDIO_NUM_INTERFACES, + .baInterfaceNr = { + [0] = AUDIO_AC_INTERFACE, + [1] = AUDIO_AS_INTERFACE, + } +}; + +#define INPUT_TERMINAL_ID 1 +static struct uac_input_terminal_descriptor input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = INPUT_TERMINAL_ID, + .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE, + .bAssocTerminal = 0, + .wChannelConfig = 0x3, +}; + +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); + +#define FEATURE_UNIT_ID 2 +static struct uac_feature_unit_descriptor_0 feature_unit_desc = { + .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FEATURE_UNIT, + .bUnitID = FEATURE_UNIT_ID, + .bSourceID = INPUT_TERMINAL_ID, + .bControlSize = 2, +}; + +#define OUTPUT_TERMINAL_ID 3 +static struct uac1_output_terminal_descriptor output_terminal_desc = { + .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = OUTPUT_TERMINAL_ID, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = FEATURE_UNIT_ID, + .bSourceID = FEATURE_UNIT_ID, +}; + +/* B.4.1 Standard AS Interface Descriptor */ +static struct usb_interface_descriptor as_interface_alt_0_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +static struct usb_interface_descriptor as_interface_alt_1_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +/* B.4.2 Class-Specific AS Interface Descriptor */ +static struct uac1_as_header_descriptor as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = INPUT_TERMINAL_ID, + .bDelay = 1, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, +}; + +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); + +static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, + .bSubframeSize = 2, + .bBitResolution = 16, + .bSamFreqType = 1, +}; + +/* Standard ISO IN Endpoint Descriptor for highspeed */ +static struct usb_endpoint_descriptor hs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_SYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), + .bInterval = 4, /* poll 1 per millisecond */ +}; + +/* Standard ISO IN Endpoint Descriptor for highspeed */ +static struct usb_endpoint_descriptor fs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_SYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), + .bInterval = 1, /* poll 1 per millisecond */ +}; + +/* Class-specific AS ISO OUT Endpoint Descriptor */ +static struct uac_iso_endpoint_descriptor as_iso_in_desc = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 1, + .bLockDelayUnits = 1, + .wLockDelay = __constant_cpu_to_le16(1), +}; + +static struct usb_descriptor_header *hs_audio_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&hs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct usb_descriptor_header *fs_audio_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&fs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct snd_pcm_hardware audio_hw_info = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = SAMPLE_RATE, + .rate_max = SAMPLE_RATE, + + .buffer_bytes_max = 1024 * 1024, + .period_bytes_min = 64, + .period_bytes_max = 512 * 1024, + .periods_min = 2, + .periods_max = 1024, +}; + +/*-------------------------------------------------------------------------*/ + +struct audio_source_config { + int card; + int device; +}; + +struct audio_dev { + struct usb_function func; + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + + struct list_head idle_reqs; + struct usb_ep *in_ep; + + spinlock_t lock; + + /* beginning, end and current position in our buffer */ + void *buffer_start; + void *buffer_end; + void *buffer_pos; + + /* byte size of a "period" */ + unsigned int period; + /* bytes sent since last call to snd_pcm_period_elapsed */ + unsigned int period_offset; + /* time we started playing */ + ktime_t start_time; + /* number of frames sent since start_time */ + s64 frames_sent; +}; + +static inline struct audio_dev *func_to_audio(struct usb_function *f) +{ + return container_of(f, struct audio_dev, func); +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + req->length = buffer_size; + return req; +} + +static void audio_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static void audio_req_put(struct audio_dev *audio, struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + list_add_tail(&req->list, &audio->idle_reqs); + spin_unlock_irqrestore(&audio->lock, flags); +} + +static struct usb_request *audio_req_get(struct audio_dev *audio) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&audio->lock, flags); + if (list_empty(&audio->idle_reqs)) { + req = 0; + } else { + req = list_first_entry(&audio->idle_reqs, struct usb_request, + list); + list_del(&req->list); + } + spin_unlock_irqrestore(&audio->lock, flags); + return req; +} + +/* send the appropriate number of packets to match our bitrate */ +static void audio_send(struct audio_dev *audio) +{ + struct snd_pcm_runtime *runtime; + struct usb_request *req; + int length, length1, length2, ret; + s64 msecs; + s64 frames; + ktime_t now; + + /* audio->substream will be null if we have been closed */ + if (!audio->substream) + return; + /* audio->buffer_pos will be null if we have been stopped */ + if (!audio->buffer_pos) + return; + + runtime = audio->substream->runtime; + + /* compute number of frames to send */ + now = ktime_get(); + msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); + do_div(msecs, 1000000); + frames = msecs * SAMPLE_RATE; + do_div(frames, 1000); + + /* Readjust our frames_sent if we fall too far behind. + * If we get too far behind it is better to drop some frames than + * to keep sending data too fast in an attempt to catch up. + */ + if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC) + audio->frames_sent = frames - FRAMES_PER_MSEC; + + frames -= audio->frames_sent; + + /* We need to send something to keep the pipeline going */ + if (frames <= 0) + frames = FRAMES_PER_MSEC; + + while (frames > 0) { + req = audio_req_get(audio); + if (!req) + break; + + length = frames_to_bytes(runtime, frames); + if (length > IN_EP_MAX_PACKET_SIZE) + length = IN_EP_MAX_PACKET_SIZE; + + if (audio->buffer_pos + length > audio->buffer_end) + length1 = audio->buffer_end - audio->buffer_pos; + else + length1 = length; + memcpy(req->buf, audio->buffer_pos, length1); + if (length1 < length) { + /* Wrap around and copy remaining length + * at beginning of buffer. + */ + length2 = length - length1; + memcpy(req->buf + length1, audio->buffer_start, + length2); + audio->buffer_pos = audio->buffer_start + length2; + } else { + audio->buffer_pos += length1; + if (audio->buffer_pos >= audio->buffer_end) + audio->buffer_pos = audio->buffer_start; + } + + req->length = length; + ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC); + if (ret < 0) { + pr_err("usb_ep_queue failed ret: %d\n", ret); + audio_req_put(audio, req); + break; + } + + frames -= bytes_to_frames(runtime, length); + audio->frames_sent += bytes_to_frames(runtime, length); + } +} + +static void audio_control_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* nothing to do here */ +} + +static void audio_data_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct audio_dev *audio = req->context; + + pr_debug("audio_data_complete req->status %d req->actual %d\n", + req->status, req->actual); + + audio_req_put(audio, req); + + if (!audio->buffer_start || req->status) + return; + + audio->period_offset += req->actual; + if (audio->period_offset >= audio->period) { + snd_pcm_period_elapsed(audio->substream); + audio->period_offset = 0; + } + audio_send(audio); +} + +static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + int value = -EOPNOTSUPP; + u16 ep = le16_to_cpu(ctrl->wIndex); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_SET_CUR: + case UAC_SET_MIN: + case UAC_SET_MAX: + case UAC_SET_RES: + value = len; + break; + default: + break; + } + + return value; +} + +static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 *buf = cdev->req->buf; + + pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) { + switch (ctrl->bRequest) { + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: + /* return our sample rate */ + buf[0] = (u8)SAMPLE_RATE; + buf[1] = (u8)(SAMPLE_RATE >> 8); + buf[2] = (u8)(SAMPLE_RATE >> 16); + value = 3; + break; + default: + break; + } + } + + return value; +} + +static int +audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything; interface + * activation uses set_alt(). + */ + switch (ctrl->bRequestType) { + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_set_endpoint_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_get_endpoint_req(f, ctrl); + break; + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + pr_debug("audio req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + req->complete = audio_control_complete; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + pr_err("audio response on err %d\n", value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct audio_dev *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep); + if (ret) + return ret; + + usb_ep_enable(audio->in_ep); + return 0; +} + +static void audio_disable(struct usb_function *f) +{ + struct audio_dev *audio = func_to_audio(f); + + pr_debug("audio_disable\n"); + usb_ep_disable(audio->in_ep); +} + +/*-------------------------------------------------------------------------*/ + +static void audio_build_desc(struct audio_dev *audio) +{ + u8 *sam_freq; + int rate; + + /* Set channel numbers */ + input_terminal_desc.bNrChannels = 2; + as_type_i_desc.bNrChannels = 2; + + /* Set sample rates */ + rate = SAMPLE_RATE; + sam_freq = as_type_i_desc.tSamFreq[0]; + memcpy(sam_freq, &rate, 3); +} + +/* audio function driver setup/binding */ +static int +audio_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct audio_dev *audio = func_to_audio(f); + int status; + struct usb_ep *ep; + struct usb_request *req; + int i; + + audio_build_desc(audio); + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + as_interface_alt_0_desc.bInterfaceNumber = status; + as_interface_alt_1_desc.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate our endpoint */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc); + if (!ep) + goto fail; + audio->in_ep = ep; + ep->driver_data = audio; /* claim */ + + if (gadget_is_dualspeed(c->cdev->gadget)) + hs_as_in_ep_desc.bEndpointAddress = + fs_as_in_ep_desc.bEndpointAddress; + + f->descriptors = fs_audio_desc; + f->hs_descriptors = hs_audio_desc; + + for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) { + req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE); + if (req) { + req->context = audio; + req->complete = audio_data_complete; + audio_req_put(audio, req); + } else + status = -ENOMEM; + } + +fail: + return status; +} + +static void +audio_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct audio_dev *audio = func_to_audio(f); + struct usb_request *req; + + while ((req = audio_req_get(audio))) + audio_request_free(req, audio->in_ep); + + snd_card_free_when_closed(audio->card); + audio->card = NULL; + audio->pcm = NULL; + audio->substream = NULL; + audio->in_ep = NULL; +} + +static void audio_pcm_playback_start(struct audio_dev *audio) +{ + audio->start_time = ktime_get(); + audio->frames_sent = 0; + audio_send(audio); +} + +static void audio_pcm_playback_stop(struct audio_dev *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + audio->buffer_start = 0; + audio->buffer_end = 0; + audio->buffer_pos = 0; + spin_unlock_irqrestore(&audio->lock, flags); +} + +static int audio_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = substream->private_data; + + runtime->private_data = audio; + runtime->hw = audio_hw_info; + snd_pcm_limit_hw_rates(runtime); + runtime->hw.channels_max = 2; + + audio->substream = substream; + return 0; +} + +static int audio_pcm_close(struct snd_pcm_substream *substream) +{ + struct audio_dev *audio = substream->private_data; + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + audio->substream = NULL; + spin_unlock_irqrestore(&audio->lock, flags); + + return 0; +} + +static int audio_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + + if (rate != SAMPLE_RATE) + return -EINVAL; + if (channels != 2) + return -EINVAL; + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(params)); +} + +static int audio_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int audio_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = runtime->private_data; + + audio->period = snd_pcm_lib_period_bytes(substream); + audio->period_offset = 0; + audio->buffer_start = runtime->dma_area; + audio->buffer_end = audio->buffer_start + + snd_pcm_lib_buffer_bytes(substream); + audio->buffer_pos = audio->buffer_start; + + return 0; +} + +static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = runtime->private_data; + ssize_t bytes = audio->buffer_pos - audio->buffer_start; + + /* return offset of next frame to fill in our buffer */ + return bytes_to_frames(runtime, bytes); +} + +static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct audio_dev *audio = substream->runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + audio_pcm_playback_start(audio); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + audio_pcm_playback_stop(audio); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static struct audio_dev _audio_dev = { + .func = { + .name = "audio_source", + .bind = audio_bind, + .unbind = audio_unbind, + .set_alt = audio_set_alt, + .setup = audio_setup, + .disable = audio_disable, + }, + .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), + .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), +}; + +static struct snd_pcm_ops audio_playback_ops = { + .open = audio_pcm_open, + .close = audio_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = audio_pcm_hw_params, + .hw_free = audio_pcm_hw_free, + .prepare = audio_pcm_prepare, + .trigger = audio_pcm_playback_trigger, + .pointer = audio_pcm_pointer, +}; + +int audio_source_bind_config(struct usb_configuration *c, + struct audio_source_config *config) +{ + struct audio_dev *audio; + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + config->card = -1; + config->device = -1; + + audio = &_audio_dev; + + err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err) + return err; + + snd_card_set_dev(card, &c->cdev->gadget->dev); + + err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm); + if (err) + goto pcm_fail; + pcm->private_data = audio; + pcm->info_flags = 0; + audio->pcm = pcm; + + strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name)); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + NULL, 0, 64 * 1024); + + strlcpy(card->driver, "audio_source", sizeof(card->driver)); + strlcpy(card->shortname, card->driver, sizeof(card->shortname)); + strlcpy(card->longname, "USB accessory audio source", + sizeof(card->longname)); + + err = snd_card_register(card); + if (err) + goto register_fail; + + err = usb_add_function(c, &audio->func); + if (err) + goto add_fail; + + config->card = pcm->card->number; + config->device = pcm->device; + audio->card = card; + return 0; + +add_fail: +register_fail: +pcm_fail: + snd_card_free(audio->card); + return err; +} From 3df9729f3fe3d9ce5a8ea16d8396520a7ef2c0e1 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 20 Sep 2012 16:34:10 -0700 Subject: [PATCH 177/358] net: PPPoPNS and PPPoLAC update to use PPP_MRU instead of PPP_MRU Some headers files were moved around and some defines renamed. Signed-off-by: JP Abgrall --- drivers/net/ppp/pppolac.c | 2 +- drivers/net/ppp/pppopns.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c index c94b8507d92b..a5d3d634fd9a 100644 --- a/drivers/net/ppp/pppolac.c +++ b/drivers/net/ppp/pppolac.c @@ -310,7 +310,7 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, po->chan.hdrlen = 12; po->chan.private = sk_udp; po->chan.ops = &pppolac_channel_ops; - po->chan.mtu = PPP_MTU - 80; + po->chan.mtu = PPP_MRU - 80; po->proto.lac.local = unaligned(&addr->local)->u32; po->proto.lac.remote = unaligned(&addr->remote)->u32; atomic_set(&po->proto.lac.sequencing, 1); diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c index fb8198447938..6016d29c0660 100644 --- a/drivers/net/ppp/pppopns.c +++ b/drivers/net/ppp/pppopns.c @@ -290,7 +290,7 @@ static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, po->chan.hdrlen = 14; po->chan.private = sk_raw; po->chan.ops = &pppopns_channel_ops; - po->chan.mtu = PPP_MTU - 80; + po->chan.mtu = PPP_MRU - 80; po->proto.pns.local = addr->local; po->proto.pns.remote = addr->remote; po->proto.pns.data_ready = sk_raw->sk_data_ready; From e7cf5e54f886d52cadadc7b2109cee0e10023c3b Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Wed, 19 Sep 2012 17:03:34 -0700 Subject: [PATCH 178/358] usb: gadget: f_fs: Fix enumeration in fullspeed mode ffs_func_eps_enable always take the highspeed usb_endpoint_descriptor if it is available. Choose the correct usb_endpoint_descriptor depending on the gadget speed instead. Change-Id: I5b8444cfdf1b6fe1eef25525fb7a0a0f617a353e Signed-off-by: Benoit Goby --- drivers/usb/gadget/f_fs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index 2b4334394076..663078c0dafe 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -1334,7 +1334,12 @@ static int ffs_func_eps_enable(struct ffs_function *func) spin_lock_irqsave(&func->ffs->eps_lock, flags); do { struct usb_endpoint_descriptor *ds; - ds = ep->descs[ep->descs[1] ? 1 : 0]; + int desc_idx = ffs->gadget->speed == USB_SPEED_HIGH ? 1 : 0; + ds = ep->descs[desc_idx]; + if (!ds) { + ret = -EINVAL; + break; + } ep->ep->driver_data = ep; ep->ep->desc = ds; From 8471567324069cc5e7735859ce9e7195a31fde0b Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 24 Sep 2012 18:03:58 -0700 Subject: [PATCH 179/358] cpufreq: interactive: always limit initial speed bump to hispeed First bump speed up to hispeed_freq whenever the current speed is below hispeed_freq, instead of only when the current speed is the minimum speed. The previous code made it too difficult to use hispeed_freq as a common intermediate speed on systems that frequently run at speeds between minimum and hispeed_freq. Change-Id: I04ec30bafabf5741e267ff289209b8c2d846824b Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 4b60b6ec7ed5..f85fbbd6cb7b 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -178,7 +178,8 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = load_since_change; if (cpu_load >= go_hispeed_load || boost_val) { - if (pcpu->target_freq <= pcpu->policy->min) { + if (pcpu->target_freq < hispeed_freq && + hispeed_freq < pcpu->policy->max) { new_freq = hispeed_freq; } else { new_freq = pcpu->policy->max * cpu_load / 100; From c89a21e48c901c98eeebd473ae4a0a190bfad2e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 25 Sep 2012 17:37:14 -0700 Subject: [PATCH 180/358] staging: android: lowmemorykiller: Add config option to support oom_adj values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conversion to use oom_score_adj instead of the deprecated oom_adj values breaks existing user-space code. Add a config option to convert oom_adj values written to oom_score_adj values if they appear to be valid oom_adj values. Change-Id: I68308125059b802ee2991feefb07e9703bc48549 Signed-off-by: Arve Hjønnevåg --- drivers/staging/android/Kconfig | 9 +++ drivers/staging/android/lowmemorykiller.c | 85 +++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index b91c758883bf..a3ecdb76554c 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -64,6 +64,15 @@ config ANDROID_LOW_MEMORY_KILLER ---help--- Registers processes to be killed when memory is low +config ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES + bool "Android Low Memory Killer: detect oom_adj values" + depends on ANDROID_LOW_MEMORY_KILLER + default y + ---help--- + Detect oom_adj values written to + /sys/module/lowmemorykiller/parameters/adj and convert them + to oom_score_adj values. + config ANDROID_INTF_ALARM_DEV bool "Android alarm driver" depends on RTC_CLASS diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 8b49b3dd0978..7157305f6c81 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -186,9 +186,94 @@ static void __exit lowmem_exit(void) unregister_shrinker(&lowmem_shrinker); } +#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES +static short lowmem_oom_adj_to_oom_score_adj(short oom_adj) +{ + if (oom_adj == OOM_ADJUST_MAX) + return OOM_SCORE_ADJ_MAX; + else + return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE; +} + +static void lowmem_autodetect_oom_adj_values(void) +{ + int i; + short oom_adj; + short oom_score_adj; + int array_size = ARRAY_SIZE(lowmem_adj); + + if (lowmem_adj_size < array_size) + array_size = lowmem_adj_size; + + if (array_size <= 0) + return; + + oom_adj = lowmem_adj[array_size - 1]; + if (oom_adj > OOM_ADJUST_MAX) + return; + + oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj); + if (oom_score_adj <= OOM_ADJUST_MAX) + return; + + lowmem_print(1, "lowmem_shrink: convert oom_adj to oom_score_adj:\n"); + for (i = 0; i < array_size; i++) { + oom_adj = lowmem_adj[i]; + oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj); + lowmem_adj[i] = oom_score_adj; + lowmem_print(1, "oom_adj %d => oom_score_adj %d\n", + oom_adj, oom_score_adj); + } +} + +static int lowmem_adj_array_set(const char *val, const struct kernel_param *kp) +{ + int ret; + + ret = param_array_ops.set(val, kp); + + /* HACK: Autodetect oom_adj values in lowmem_adj array */ + lowmem_autodetect_oom_adj_values(); + + return ret; +} + +static int lowmem_adj_array_get(char *buffer, const struct kernel_param *kp) +{ + return param_array_ops.get(buffer, kp); +} + +static void lowmem_adj_array_free(void *arg) +{ + param_array_ops.free(arg); +} + +static struct kernel_param_ops lowmem_adj_array_ops = { + .set = lowmem_adj_array_set, + .get = lowmem_adj_array_get, + .free = lowmem_adj_array_free, +}; + +static const struct kparam_array __param_arr_adj = { + .max = ARRAY_SIZE(lowmem_adj), + .num = &lowmem_adj_size, + .ops = ¶m_ops_short, + .elemsize = sizeof(lowmem_adj[0]), + .elem = lowmem_adj, +}; +#endif + module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); +#ifdef CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES +__module_param_call(MODULE_PARAM_PREFIX, adj, + &lowmem_adj_array_ops, + .arr = &__param_arr_adj, + S_IRUGO | S_IWUSR, -1); +__MODULE_PARM_TYPE(adj, "array of short"); +#else module_param_array_named(adj, lowmem_adj, short, &lowmem_adj_size, S_IRUGO | S_IWUSR); +#endif module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, S_IRUGO | S_IWUSR); module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); From f5c0e02eb6bb4327d6fedd5d99c1f7f3cc7204a9 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 3 Oct 2012 00:39:56 -0700 Subject: [PATCH 181/358] cpufreq: interactive: run at fraction of hispeed_freq when load is low When load is below go_hispeed_load, apply the percentage of CPU load to a max frequency of hispeed_freq instead of the max speed. This avoids jumping too quickly to hispeed_freq when it is a relatively low percentage of max speed. This also allows go_hispeed_load to be set to a high percentage relative to hispeed_freq (as a percentage of max speed, again useful when hispeed_freq is a low fraction of max speed), to cap larger loads at hispeed_freq. For example, a load of 60% will typically move to 60% of hispeed_freq, not 60% of max speed. This causes the governor to apply two different speed caps, depending on whether load is below or above go_hispeed_load. Also fix the type of hispeed_freq, which was u64, to match other speed data types (and avoid overhead and allow division). Change-Id: Ie2d0668be161c074aaad77db2037505431457b3a Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index f85fbbd6cb7b..16bd23be2495 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -63,7 +63,7 @@ static cpumask_t speedchange_cpumask; static spinlock_t speedchange_cpumask_lock; /* Hi speed to bump to from lo speed when load burst (default max) */ -static u64 hispeed_freq; +static unsigned int hispeed_freq; /* Go to hi speed when CPU load at or above this value. */ #define DEFAULT_GO_HISPEED_LOAD 85 @@ -198,7 +198,7 @@ static void cpufreq_interactive_timer(unsigned long data) } } } else { - new_freq = pcpu->policy->max * cpu_load / 100; + new_freq = hispeed_freq * cpu_load / 100; } if (new_freq <= hispeed_freq) @@ -465,7 +465,7 @@ static void cpufreq_interactive_boost(void) static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) { - return sprintf(buf, "%llu\n", hispeed_freq); + return sprintf(buf, "%u\n", hispeed_freq); } static ssize_t store_hispeed_freq(struct kobject *kobj, @@ -473,9 +473,9 @@ static ssize_t store_hispeed_freq(struct kobject *kobj, size_t count) { int ret; - u64 val; + long unsigned int val; - ret = strict_strtoull(buf, 0, &val); + ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; hispeed_freq = val; From 6ccb8c7c29a067c2b29adc73ad8eb6962e90da14 Mon Sep 17 00:00:00 2001 From: HongMin Son Date: Fri, 28 Sep 2012 14:03:31 +0900 Subject: [PATCH 182/358] power: android-battery: add charge timeouts and recharge logic Add recharge logic when voltage threshold reached. Add charge and recharge timeouts. Change-Id: I3ef3b926ce694115dde7f8056072bef63884a5d0 Signed-off-by: HongMin Son Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 118 ++++++++++++++++-- include/linux/platform_data/android_battery.h | 5 + 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index e3b1f5e87b28..16db8cff5095 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -60,6 +60,8 @@ struct android_bat_data { unsigned int batt_vcell; unsigned int batt_soc; unsigned int charging_status; + bool recharging; + unsigned long charging_start_time; struct workqueue_struct *monitor_wqueue; struct work_struct monitor_work; @@ -92,6 +94,8 @@ static enum power_supply_property android_power_props[] = { }; static void android_bat_update_data(struct android_bat_data *battery); +static int android_bat_enable_charging(struct android_bat_data *battery, + bool enable); static char *charge_source_str(int charge_source) { @@ -250,6 +254,21 @@ static void android_bat_update_data(struct android_bat_data *battery) android_bat_get_temp(battery); } +static void android_bat_set_charge_time(struct android_bat_data *battery, + bool enable) +{ + if (enable && !battery->charging_start_time) { + struct timespec cur_time; + + get_monotonic_boottime(&cur_time); + /* record start time for charge timeout timer */ + battery->charging_start_time = cur_time.tv_sec; + } else if (!enable) { + /* clear charge timeout timer */ + battery->charging_start_time = 0; + } +} + static int android_bat_enable_charging(struct android_bat_data *battery, bool enable) { @@ -268,12 +287,60 @@ static int android_bat_enable_charging(struct android_bat_data *battery, if (battery->pdata && battery->pdata->set_charging_enable) battery->pdata->set_charging_enable(enable); + android_bat_set_charge_time(battery, enable); pr_info("battery: enable=%d charger: %s\n", enable, charge_source_str(battery->charge_source)); - return 0; } +static bool android_bat_charge_timeout(struct android_bat_data *battery, + unsigned long timeout) +{ + struct timespec cur_time; + + if (!battery->charging_start_time) + return 0; + + get_monotonic_boottime(&cur_time); + pr_debug("%s: Start time: %ld, End time: %ld, current time: %ld\n", + __func__, battery->charging_start_time, + battery->charging_start_time + timeout, + cur_time.tv_sec); + return cur_time.tv_sec >= battery->charging_start_time + timeout; +} + +static void android_bat_charging_timer(struct android_bat_data *battery) +{ + if (!battery->charging_start_time && + battery->charging_status == POWER_SUPPLY_STATUS_CHARGING) { + android_bat_enable_charging(battery, true); + battery->recharging = true; + pr_debug("%s: charge status charging but timer is expired\n", + __func__); + } else if (battery->charging_start_time == 0) { + pr_debug("%s: charging_start_time never initialized\n", + __func__); + return; + } + + if (android_bat_charge_timeout( + battery, + battery->recharging ? battery->pdata->recharging_time : + battery->pdata->full_charging_time)) { + android_bat_enable_charging(battery, false); + if (battery->batt_vcell > + battery->pdata->recharging_voltage && + battery->batt_soc == 100) + battery->charging_status = + POWER_SUPPLY_STATUS_FULL; + battery->recharging = false; + battery->charging_start_time = 0; + pr_info("battery: charging timer expired\n"); + } + + return; +} + static void android_bat_charge_source_changed(struct android_bat_callbacks *ptr, int charge_source) { @@ -289,6 +356,18 @@ static void android_bat_charge_source_changed(struct android_bat_callbacks *ptr, queue_work(battery->monitor_wqueue, &battery->charger_work); } +static void android_bat_set_full_status(struct android_bat_callbacks *ptr) +{ + struct android_bat_data *battery = + container_of(ptr, struct android_bat_data, callbacks); + + pr_info("battery: battery full\n"); + battery->charging_status = POWER_SUPPLY_STATUS_FULL; + android_bat_enable_charging(battery, false); + battery->recharging = false; + power_supply_changed(&battery->psy_bat); +} + static void android_bat_charger_work(struct work_struct *work) { struct android_bat_data *battery = @@ -298,8 +377,9 @@ static void android_bat_charger_work(struct work_struct *work) case CHARGE_SOURCE_NONE: battery->charging_status = POWER_SUPPLY_STATUS_DISCHARGING; android_bat_enable_charging(battery, false); - if (battery->batt_health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) - battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; + battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; + battery->recharging = false; + battery->charging_start_time = 0; break; case CHARGE_SOURCE_USB: case CHARGE_SOURCE_AC: @@ -327,14 +407,23 @@ static void android_bat_monitor_set_alarm(struct android_bat_data *battery, static void android_bat_monitor_work(struct work_struct *work) { - struct android_bat_data *battery; - battery = container_of(work, struct android_bat_data, monitor_work); + struct android_bat_data *battery = + container_of(work, struct android_bat_data, monitor_work); + struct timespec cur_time; wake_lock(&battery->monitor_wake_lock); android_bat_update_data(battery); switch (battery->charging_status) { case POWER_SUPPLY_STATUS_FULL: + if (battery->batt_vcell < battery->pdata->recharging_voltage && + !battery->recharging) { + battery->recharging = true; + android_bat_enable_charging(battery, true); + pr_info("battery: start recharging, v=%d\n", + battery->batt_vcell/1000); + } + break; case POWER_SUPPLY_STATUS_DISCHARGING: break; case POWER_SUPPLY_STATUS_CHARGING: @@ -362,9 +451,10 @@ static void android_bat_monitor_work(struct work_struct *work) android_bat_enable_charging(battery, true); battery->charging_status = POWER_SUPPLY_STATUS_CHARGING; - } else + } else { battery->charging_status = POWER_SUPPLY_STATUS_DISCHARGING; + } } break; default: @@ -373,11 +463,16 @@ static void android_bat_monitor_work(struct work_struct *work) break; } - pr_info("battery: l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d type=%s\n", + android_bat_charging_timer(battery); + get_monotonic_boottime(&cur_time); + pr_info("battery: l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", battery->batt_soc, battery->batt_vcell/1000, battery->batt_current, battery->batt_temp < 0 ? "-" : "", abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), battery->batt_health, battery->charging_status, + battery->recharging ? "r" : "", + battery->charging_start_time ? + cur_time.tv_sec - battery->charging_start_time : 0, charge_source_str(battery->charge_source)); power_supply_changed(&battery->psy_bat); battery->last_poll = ktime_get_boottime(); @@ -400,13 +495,18 @@ static enum alarmtimer_restart android_bat_monitor_alarm( static int android_power_debug_dump(struct seq_file *s, void *unused) { struct android_bat_data *battery = s->private; + struct timespec cur_time; android_bat_update_data(battery); - seq_printf(s, "l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d type=%s\n", + get_monotonic_boottime(&cur_time); + seq_printf(s, "l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", battery->batt_soc, battery->batt_vcell/1000, battery->batt_current, battery->batt_temp < 0 ? "-" : "", abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), battery->batt_health, battery->charging_status, + battery->recharging ? "r" : "", + battery->charging_start_time ? + cur_time.tv_sec - battery->charging_start_time : 0, charge_source_str(battery->charge_source)); return 0; @@ -510,6 +610,8 @@ static int android_bat_probe(struct platform_device *pdev) battery->callbacks.charge_source_changed = android_bat_charge_source_changed; + battery->callbacks.battery_set_full = + android_bat_set_full_status; if (battery->pdata && battery->pdata->register_callbacks) battery->pdata->register_callbacks(&battery->callbacks); diff --git a/include/linux/platform_data/android_battery.h b/include/linux/platform_data/android_battery.h index a0749f5b9d3b..f6c8298fd885 100644 --- a/include/linux/platform_data/android_battery.h +++ b/include/linux/platform_data/android_battery.h @@ -20,6 +20,7 @@ enum { struct android_bat_callbacks { void (*charge_source_changed) (struct android_bat_callbacks *, int); + void (*battery_set_full)(struct android_bat_callbacks *); }; struct android_bat_platform_data { @@ -37,6 +38,10 @@ struct android_bat_platform_data { int temp_high_recovery; int temp_low_recovery; int temp_low_threshold; + + unsigned long full_charging_time; + unsigned long recharging_time; + unsigned int recharging_voltage; }; #endif From ac875ecbf3e136f66085d32c4f9e64342483da65 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 4 Oct 2012 15:09:52 -0700 Subject: [PATCH 183/358] power: android-battery: Add state locking Change-Id: I83abd61f0ee2541f5406258b2c07f76af7d2acb5 Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index 16db8cff5095..d6ff01adcd08 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -93,6 +94,8 @@ static enum power_supply_property android_power_props[] = { POWER_SUPPLY_PROP_ONLINE, }; +static DEFINE_MUTEX(android_bat_state_lock); + static void android_bat_update_data(struct android_bat_data *battery); static int android_bat_enable_charging(struct android_bat_data *battery, bool enable); @@ -226,6 +229,13 @@ static void android_bat_get_temp(struct android_bat_data *battery) battery->batt_temp = batt_temp; } +/* + * android_bat_state_lock not held, may call back into + * android_bat_charge_source_changed. Gathering data here can be + * non-atomic; updating our state based on the data may need to be + * atomic. + */ + static void android_bat_update_data(struct android_bat_data *battery) { int ret; @@ -344,15 +354,17 @@ static void android_bat_charging_timer(struct android_bat_data *battery) static void android_bat_charge_source_changed(struct android_bat_callbacks *ptr, int charge_source) { - struct android_bat_data *battery; + struct android_bat_data *battery = + container_of(ptr, struct android_bat_data, callbacks); - battery = container_of(ptr, struct android_bat_data, callbacks); wake_lock(&battery->charger_wake_lock); + mutex_lock(&android_bat_state_lock); battery->charge_source = charge_source; pr_info("battery: charge source type was changed: %s\n", charge_source_str(battery->charge_source)); + mutex_unlock(&android_bat_state_lock); queue_work(battery->monitor_wqueue, &battery->charger_work); } @@ -361,10 +373,12 @@ static void android_bat_set_full_status(struct android_bat_callbacks *ptr) struct android_bat_data *battery = container_of(ptr, struct android_bat_data, callbacks); + mutex_lock(&android_bat_state_lock); pr_info("battery: battery full\n"); battery->charging_status = POWER_SUPPLY_STATUS_FULL; android_bat_enable_charging(battery, false); battery->recharging = false; + mutex_unlock(&android_bat_state_lock); power_supply_changed(&battery->psy_bat); } @@ -373,6 +387,8 @@ static void android_bat_charger_work(struct work_struct *work) struct android_bat_data *battery = container_of(work, struct android_bat_data, charger_work); + mutex_lock(&android_bat_state_lock); + switch (battery->charge_source) { case CHARGE_SOURCE_NONE: battery->charging_status = POWER_SUPPLY_STATUS_DISCHARGING; @@ -391,10 +407,10 @@ static void android_bat_charger_work(struct work_struct *work) break; } + mutex_unlock(&android_bat_state_lock); + wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); power_supply_changed(&battery->psy_ac); power_supply_changed(&battery->psy_usb); - - wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); } @@ -413,6 +429,7 @@ static void android_bat_monitor_work(struct work_struct *work) wake_lock(&battery->monitor_wake_lock); android_bat_update_data(battery); + mutex_lock(&android_bat_state_lock); switch (battery->charging_status) { case POWER_SUPPLY_STATUS_FULL: @@ -474,6 +491,7 @@ static void android_bat_monitor_work(struct work_struct *work) battery->charging_start_time ? cur_time.tv_sec - battery->charging_start_time : 0, charge_source_str(battery->charge_source)); + mutex_unlock(&android_bat_state_lock); power_supply_changed(&battery->psy_bat); battery->last_poll = ktime_get_boottime(); android_bat_monitor_set_alarm(battery, FAST_POLL); @@ -499,6 +517,7 @@ static int android_power_debug_dump(struct seq_file *s, void *unused) android_bat_update_data(battery); get_monotonic_boottime(&cur_time); + mutex_lock(&android_bat_state_lock); seq_printf(s, "l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", battery->batt_soc, battery->batt_vcell/1000, battery->batt_current, battery->batt_temp < 0 ? "-" : "", @@ -508,7 +527,7 @@ static int android_power_debug_dump(struct seq_file *s, void *unused) battery->charging_start_time ? cur_time.tv_sec - battery->charging_start_time : 0, charge_source_str(battery->charge_source)); - + mutex_unlock(&android_bat_state_lock); return 0; } From 0d9c5bda96135ab0cf790873328638d32b1773ed Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 4 Oct 2012 16:11:53 -0700 Subject: [PATCH 184/358] power: android-battery: leave full/not-charging status when charger re-sensed If charger was connected and a charge source change event occurs such that the charger is still connected (or a different charge source connected), do not overwrite an existing state such as full or not-charging. Only switch from discharging to charging on a charge source change event. Change-Id: I1b841c8f3a92bf15074999e18b17d1d19ce028d7 Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index d6ff01adcd08..d7682d69e3ab 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -399,8 +399,18 @@ static void android_bat_charger_work(struct work_struct *work) break; case CHARGE_SOURCE_USB: case CHARGE_SOURCE_AC: - battery->charging_status = POWER_SUPPLY_STATUS_CHARGING; - android_bat_enable_charging(battery, true); + /* + * If charging status indicates a charger was already + * connected prior to this and a non-charging status is + * set, leave the status alone. + */ + if (battery->charging_status == + POWER_SUPPLY_STATUS_DISCHARGING || + battery->charging_status == POWER_SUPPLY_STATUS_UNKNOWN) { + battery->charging_status = POWER_SUPPLY_STATUS_CHARGING; + android_bat_enable_charging(battery, true); + } + break; default: pr_err("%s: Invalid charger type\n", __func__); From aebefbb4119a301fc1519ab5d7a93d5377a0346a Mon Sep 17 00:00:00 2001 From: HongMin Son Date: Fri, 5 Oct 2012 13:22:31 +0900 Subject: [PATCH 185/358] power: android-battery: Fix battery alarm timer not modified at suspend - Alarm Timer driver is suspended before battery driver, so alarm timer was not changed to slow poll speed at suspend. Use PM notifiers instead. - If charger is connected, keep 1 min alarm timer. Signed-off-by: HongMin Son Signed-off-by: Todd Poynor Change-Id: Id0f3bda5d0749fd90f1964073bc30c1a73fc317f --- drivers/power/android_battery.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index d7682d69e3ab..5001bba28f91 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -700,21 +700,24 @@ static int android_bat_suspend(struct device *dev) struct android_bat_data *battery = dev_get_drvdata(dev); cancel_work_sync(&battery->monitor_work); - android_bat_monitor_set_alarm(battery, SLOW_POLL); + android_bat_monitor_set_alarm( + battery, + battery->charge_source == CHARGE_SOURCE_NONE ? + SLOW_POLL : FAST_POLL); return 0; } -static int android_bat_resume(struct device *dev) +static void android_bat_resume(struct device *dev) { struct android_bat_data *battery = dev_get_drvdata(dev); android_bat_monitor_set_alarm(battery, FAST_POLL); - return 0; + return; } static const struct dev_pm_ops android_bat_pm_ops = { - .suspend = android_bat_suspend, - .resume = android_bat_resume, + .prepare = android_bat_suspend, + .complete = android_bat_resume, }; static struct platform_driver android_bat_driver = { From d50b2ff1fec2d4de728e764d6420763692a761b0 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Zavin Date: Fri, 5 Oct 2012 13:54:59 -0700 Subject: [PATCH 186/358] mm: vmscan: Add a debug file for shrinkers This patch adds a debugfs file called "shrinker" when read this calls all the shrinkers in the system with nr_to_scan set to zero and prints the result. These results are the number of objects the shrinkers have available and can thus be used an indication of the total memory that would be availble to the system if a shrink occurred. Change-Id: Ied0ee7caff3d2fc1cb4bb839aaafee81b5b0b143 Signed-off-by: Rebecca Schultz Zavin --- mm/vmscan.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/mm/vmscan.c b/mm/vmscan.c index a9c74b409681..9f4cf9910ce3 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -174,6 +175,39 @@ static unsigned long get_lru_size(struct lruvec *lruvec, enum lru_list lru) return zone_page_state(lruvec_zone(lruvec), NR_LRU_BASE + lru); } +struct dentry *debug_file; + +static int debug_shrinker_show(struct seq_file *s, void *unused) +{ + struct shrinker *shrinker; + struct shrink_control sc; + + sc.gfp_mask = -1; + sc.nr_to_scan = 0; + + down_read(&shrinker_rwsem); + list_for_each_entry(shrinker, &shrinker_list, list) { + int num_objs; + + num_objs = shrinker->count_objects(shrinker, &sc); + seq_printf(s, "%pf %d\n", shrinker->scan_objects, num_objs); + } + up_read(&shrinker_rwsem); + return 0; +} + +static int debug_shrinker_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_shrinker_show, inode->i_private); +} + +static const struct file_operations debug_shrinker_fops = { + .open = debug_shrinker_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* * Add a shrinker callback to be called from the vm. */ @@ -203,6 +237,15 @@ int register_shrinker(struct shrinker *shrinker) } EXPORT_SYMBOL(register_shrinker); +static int __init add_shrinker_debug(void) +{ + debugfs_create_file("shrinker", 0644, NULL, NULL, + &debug_shrinker_fops); + return 0; +} + +late_initcall(add_shrinker_debug); + /* * Remove one */ From cf33e1c75a163e9eec79b16f77cd17bbf0fef881 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 5 Oct 2012 02:04:45 -0700 Subject: [PATCH 187/358] power: android-battery: use freezable workqueue for monitor Alarmtimer may execute the monitor early in the resume sequence. Freeze the monitor while suspended, and unfreeze after the battery charger and fuel gauge, etc. drivers are resumed. Change-Id: Iefda1c642d4b2440169d90d2bee2888fc70e030a Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index 5001bba28f91..7befc14e3aa3 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -627,7 +627,7 @@ static int android_bat_probe(struct platform_device *pdev) } battery->monitor_wqueue = - create_singlethread_workqueue(dev_name(&pdev->dev)); + alloc_workqueue(dev_name(&pdev->dev), WQ_FREEZABLE, 1); if (!battery->monitor_wqueue) { dev_err(battery->dev, "%s: fail to create workqueue\n", __func__); From 675e02ceb881df75b84a1d0bda7c6d0d1404ebf0 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 9 Oct 2012 20:38:21 -0700 Subject: [PATCH 188/358] netfilter: xt_qtaguid: fix error exit that would keep a spinlock. qtudev_open() could return with a uid_tag_data_tree_lock held when an kzalloc(..., GFP_ATOMIC) would fail. Very unlikely to get triggered AND survive the mayhem of running out of mem. Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 14b003da1423..6b22563a924f 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2752,7 +2752,7 @@ static int qtudev_open(struct inode *inode, struct file *file) utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); if (IS_ERR_OR_NULL(utd_entry)) { res = PTR_ERR(utd_entry); - goto err; + goto err_unlock; } /* Look for existing PID based proc_data */ @@ -2794,8 +2794,8 @@ err_unlock_free_utd: rb_erase(&utd_entry->node, &uid_tag_data_tree); kfree(utd_entry); } +err_unlock: spin_unlock_bh(&uid_tag_data_tree_lock); -err: return res; } From 1a4b0fca6e93f6352861985ffb3adec2f3686ca1 Mon Sep 17 00:00:00 2001 From: HongMin Son Date: Thu, 11 Oct 2012 19:42:54 +0900 Subject: [PATCH 189/358] power: android-battery: Battery health check only when connected to charger Only check battery health when connected to a charger. Change-Id: I43e3f73ac1e5862f8e6dbaa19daa6d322f3bba20 Signed-off-by: HongMin Son --- drivers/power/android_battery.c | 47 ++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index 7befc14e3aa3..a5b5f65e019c 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -201,28 +201,39 @@ static void android_bat_get_temp(struct android_bat_data *battery) if (battery->pdata->get_temperature) battery->pdata->get_temperature(&batt_temp); - if (batt_temp >= battery->pdata->temp_high_threshold) { - if (health != POWER_SUPPLY_HEALTH_OVERHEAT && + if (battery->charge_source != CHARGE_SOURCE_NONE) { + if (batt_temp >= battery->pdata->temp_high_threshold) { + if (health != POWER_SUPPLY_HEALTH_OVERHEAT && health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { - pr_info("battery overheat (%d>=%d), charging unavailable\n", - batt_temp, battery->pdata->temp_high_threshold); - battery->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; - } - } else if (batt_temp <= battery->pdata->temp_high_recovery && + pr_info("battery overheat (%d>=%d), " \ + "charging unavailable\n", + batt_temp, + battery->pdata->temp_high_threshold); + battery->batt_health = + POWER_SUPPLY_HEALTH_OVERHEAT; + } + } else if (batt_temp <= battery->pdata->temp_high_recovery && batt_temp >= battery->pdata->temp_low_recovery) { - if (health == POWER_SUPPLY_HEALTH_OVERHEAT || + if (health == POWER_SUPPLY_HEALTH_OVERHEAT || health == POWER_SUPPLY_HEALTH_COLD) { - pr_info("battery recovery (%d,%d~%d), charging available\n", - batt_temp, battery->pdata->temp_low_recovery, - battery->pdata->temp_high_recovery); - battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; - } - } else if (batt_temp <= battery->pdata->temp_low_threshold) { - if (health != POWER_SUPPLY_HEALTH_COLD && + pr_info("battery recovery (%d,%d~%d)," \ + "charging available\n", + batt_temp, + battery->pdata->temp_low_recovery, + battery->pdata->temp_high_recovery); + battery->batt_health = + POWER_SUPPLY_HEALTH_GOOD; + } + } else if (batt_temp <= battery->pdata->temp_low_threshold) { + if (health != POWER_SUPPLY_HEALTH_COLD && health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { - pr_info("battery cold (%d <= %d), charging unavailable\n", - batt_temp, battery->pdata->temp_low_threshold); - battery->batt_health = POWER_SUPPLY_HEALTH_COLD; + pr_info("battery cold (%d <= %d)," \ + "charging unavailable\n", + batt_temp, + battery->pdata->temp_low_threshold); + battery->batt_health = + POWER_SUPPLY_HEALTH_COLD; + } } } From d795b84a53f6b21fb61114bb91b287f0ccee7bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 15 Oct 2012 20:58:20 -0700 Subject: [PATCH 190/358] staging:iio:events: Don't copy data to user-space with a locked spinlock. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iio_event_chrdev_read would return -EFAULT if the user-space page was not resident. Change-Id: I4ac5d3bc791bb503d014d9db576b9d6d522505b2 Signed-off-by: Arve Hjønnevåg --- drivers/iio/industrialio-event.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index c9c1419fe6e0..c31db87986d6 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -35,6 +35,7 @@ */ struct iio_event_interface { wait_queue_head_t wait; + struct mutex read_lock; DECLARE_KFIFO(det_events, struct iio_event_data, 16); struct list_head dev_attr_list; @@ -111,14 +112,16 @@ static ssize_t iio_event_chrdev_read(struct file *filep, if (count < sizeof(struct iio_event_data)) return -EINVAL; - spin_lock_irq(&ev_int->wait.lock); + if (mutex_lock_interruptible(&ev_int->read_lock)) + return -ERESTARTSYS; + if (kfifo_is_empty(&ev_int->det_events)) { if (filep->f_flags & O_NONBLOCK) { ret = -EAGAIN; goto error_unlock; } /* Blocking on device; waiting for something to be there */ - ret = wait_event_interruptible_locked_irq(ev_int->wait, + ret = wait_event_interruptible(ev_int->wait, !kfifo_is_empty(&ev_int->det_events) || indio_dev->info == NULL); if (ret) @@ -133,7 +136,7 @@ static ssize_t iio_event_chrdev_read(struct file *filep, ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied); error_unlock: - spin_unlock_irq(&ev_int->wait.lock); + mutex_unlock(&ev_int->read_lock); return ret ? ret : copied; } @@ -425,6 +428,7 @@ static void iio_setup_ev_int(struct iio_event_interface *ev_int) { INIT_KFIFO(ev_int->det_events); init_waitqueue_head(&ev_int->wait); + mutex_init(&ev_int->read_lock); } static const char *iio_event_group_name = "events"; @@ -488,6 +492,7 @@ int iio_device_register_eventset(struct iio_dev *indio_dev) error_free_setup_event_lines: iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list); + mutex_destroy(&indio_dev->event_interface->read_lock); kfree(indio_dev->event_interface); error_ret: @@ -514,5 +519,6 @@ void iio_device_unregister_eventset(struct iio_dev *indio_dev) return; iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list); kfree(indio_dev->event_interface->group.attrs); + mutex_destroy(&indio_dev->event_interface->read_lock); kfree(indio_dev->event_interface); } From ba99c57d34c595154aa954c62dc628c29ba89be2 Mon Sep 17 00:00:00 2001 From: Danke Xie Date: Tue, 16 Oct 2012 15:24:41 -0700 Subject: [PATCH 191/358] power: android-battery: Allow changing current in charging state When a new charge source is set, enable charging and set the charging current, unless we've paused charging due to battery full or battery health problems. Change-Id: I129e621c455a941e264dc431b4eb9db6a17c9f7b Signed-off-by: Danke Xie Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index a5b5f65e019c..dbba090a51e1 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -412,15 +412,24 @@ static void android_bat_charger_work(struct work_struct *work) case CHARGE_SOURCE_AC: /* * If charging status indicates a charger was already - * connected prior to this and a non-charging status is - * set, leave the status alone. + * connected prior to this and the status is something + * other than charging ("full" or "not-charging"), leave + * the status alone. */ if (battery->charging_status == POWER_SUPPLY_STATUS_DISCHARGING || - battery->charging_status == POWER_SUPPLY_STATUS_UNKNOWN) { + battery->charging_status == POWER_SUPPLY_STATUS_UNKNOWN) battery->charging_status = POWER_SUPPLY_STATUS_CHARGING; + + /* + * Don't re-enable charging if the battery is full and we + * are not actively re-charging it, or if "not-charging" + * status is set. + */ + if (!((battery->charging_status == POWER_SUPPLY_STATUS_FULL + && !battery->recharging) || battery->charging_status == + POWER_SUPPLY_STATUS_NOT_CHARGING)) android_bat_enable_charging(battery, true); - } break; default: From f6b2ed89606944c41aa29cbb1703131e191220fd Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Fri, 14 Sep 2012 14:36:34 -0700 Subject: [PATCH 192/358] sync: add Documentation/sync.txt Change-Id: Ic7f3a6d4622083be607b82ddd8d676609225bb8f Signed-off-by: Erik Gilling --- Documentation/sync.txt | 75 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Documentation/sync.txt diff --git a/Documentation/sync.txt b/Documentation/sync.txt new file mode 100644 index 000000000000..a2d05e7fa193 --- /dev/null +++ b/Documentation/sync.txt @@ -0,0 +1,75 @@ +Motivation: + +In complicated DMA pipelines such as graphics (multimedia, camera, gpu, display) +a consumer of a buffer needs to know when the producer has finished producing +it. Likewise the producer needs to know when the consumer is finished with the +buffer so it can reuse it. A particular buffer may be consumed by multiple +consumers which will retain the buffer for different amounts of time. In +addition, a consumer may consume multiple buffers atomically. +The sync framework adds an API which allows synchronization between the +producers and consumers in a generic way while also allowing platforms which +have shared hardware synchronization primitives to exploit them. + +Goals: + * provide a generic API for expressing synchronization dependencies + * allow drivers to exploit hardware synchronization between hardware + blocks + * provide a userspace API that allows a compositor to manage + dependencies. + * provide rich telemetry data to allow debugging slowdowns and stalls of + the graphics pipeline. + +Objects: + * sync_timeline + * sync_pt + * sync_fence + +sync_timeline: + +A sync_timeline is an abstract monotonically increasing counter. In general, +each driver/hardware block context will have one of these. They can be backed +by the appropriate hardware or rely on the generic sw_sync implementation. +Timelines are only ever created through their specific implementations +(i.e. sw_sync.) + +sync_pt: + +A sync_pt is an abstract value which marks a point on a sync_timeline. Sync_pts +have a single timeline parent. They have 3 states: active, signaled, and error. +They start in active state and transition, once, to either signaled (when the +timeline counter advances beyond the sync_pt’s value) or error state. + +sync_fence: + +Sync_fences are the primary primitives used by drivers to coordinate +synchronization of their buffers. They are a collection of sync_pts which may +or may not have the same timeline parent. A sync_pt can only exist in one fence +and the fence's list of sync_pts is immutable once created. Fences can be +waited on synchronously or asynchronously. Two fences can also be merged to +create a third fence containing a copy of the two fences’ sync_pts. Fences are +backed by file descriptors to allow userspace to coordinate the display pipeline +dependencies. + +Use: + +A driver implementing sync support should have a work submission function which: + * takes a fence argument specifying when to begin work + * asynchronously queues that work to kick off when the fence is signaled + * returns a fence to indicate when its work will be done. + * signals the returned fence once the work is completed. + +Consider an imaginary display driver that has the following API: +/* + * assumes buf is ready to be displayed. + * blocks until the buffer is on screen. + */ + void display_buffer(struct dma_buf *buf); + +The new API will become: +/* + * will display buf when fence is signaled. + * returns immediately with a fence that will signal when buf + * is no longer displayed. + */ +struct sync_fence* display_buffer(struct dma_buf *buf, + struct sync_fence *fence); From 7c6cb9e6108ec93750afbe9514ac8a52ac788d1a Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 31 Oct 2012 17:41:39 -0700 Subject: [PATCH 193/358] ARM: fiq_debugger: lock between tty and console writes debug_console_write calls debug_uart_flush, which will usually wait until the serial port fifo empties. If another thread is continuously calling fiq_tty_write, the fifo will constantly be refilled and debug_uart_flush might never return. Add a spinlock that is locked in debug_console_write and fiq_tty_write to ensure they can't run at the same time. This has an extra advantage of preventing lines from the console and tty from being mixed together. Also reduce the size returned by fiq_tty_write_room to keep the time spent with the spinlock held to a reasonable value. In addition, make sure fiq context can't loop forever by never calling debug_uart_flush when the console is enabled. Change-Id: I5712b01f740ca0c84f680d2032c9fa16b7656939 Signed-off-by: Colin Cross --- arch/arm/common/fiq_debugger.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index a12810b5fb68..946a31403ce0 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -86,6 +86,7 @@ struct fiq_debugger_state { char work_cmd[DEBUG_MAX]; #ifdef CONFIG_FIQ_DEBUGGER_CONSOLE + spinlock_t console_lock; struct console console; struct tty_struct *tty; int tty_open_count; @@ -708,8 +709,9 @@ static bool debug_fiq_exec(struct fiq_debugger_state *state, state->no_sleep = true; debug_printf(state, "disabling sleep\n"); } else if (!strcmp(cmd, "console")) { - state->console_enable = true; debug_printf(state, "console mode\n"); + debug_uart_flush(state); + state->console_enable = true; } else if (!strcmp(cmd, "cpu")) { debug_printf(state, "cpu %d\n", state->current_cpu); } else if (!strncmp(cmd, "cpu ", 4)) { @@ -896,7 +898,8 @@ static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state, } last_c = c; } - debug_uart_flush(state); + if (!state->console_enable) + debug_uart_flush(state); if (state->pdata->fiq_ack) state->pdata->fiq_ack(state->pdev, state->fiq); @@ -980,6 +983,7 @@ static void debug_console_write(struct console *co, const char *s, unsigned int count) { struct fiq_debugger_state *state; + unsigned long flags; state = container_of(co, struct fiq_debugger_state, console); @@ -987,12 +991,14 @@ static void debug_console_write(struct console *co, return; debug_uart_enable(state); + spin_lock_irqsave(&state->console_lock, flags); while (count--) { if (*s == '\n') debug_putc(state, '\r'); debug_putc(state, *s++); } debug_uart_flush(state); + spin_unlock_irqrestore(&state->console_lock, flags); debug_uart_disable(state); } @@ -1033,8 +1039,10 @@ int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) return count; debug_uart_enable(state); + spin_lock_irq(&state->console_lock); for (i = 0; i < count; i++) debug_putc(state, *buf++); + spin_unlock_irq(&state->console_lock); debug_uart_disable(state); return count; @@ -1042,7 +1050,7 @@ int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) int fiq_tty_write_room(struct tty_struct *tty) { - return 1024; + return 16; } #ifdef CONFIG_CONSOLE_POLL From 065a6ec32a9ce6bcc5afb62ef036441c3cb8a060 Mon Sep 17 00:00:00 2001 From: Mars Date: Sat, 3 Nov 2012 12:15:38 +0800 Subject: [PATCH 194/358] ARM: fiq_debugger: fix uninitialised spin_lock. Backtrace: [] (dump_backtrace+0x0/0x10c) from [] (dump_stack+0x1) r6:c07a489c r5:c0c9b9dc r4:00000002 r3:271aed3b [] (dump_stack+0x0/0x1c) from [] (__lock_acquire+0x93) [] (__lock_acquire+0x0/0xad4) from [] (lock_acquire+0) [] (lock_acquire+0x0/0xa4) from [] (_raw_spin_lock_ir) [] (_raw_spin_lock_irq+0x0/0x5c) from [] (fiq_tty_wri) r5:e30f0000 r4:e36f0c00 [] (fiq_tty_write+0x0/0x80) from [] (n_tty_write+0x18) r8:e370fc40 r7:e378a000 r6:e3572d1c r5:e36f0c00 r4:00000002 r3:c005293c [] (n_tty_write+0x0/0x440) from [] (tty_write+0x100/0) [] (tty_write+0x0/0x2a8) from [] (vfs_write+0xa4/0x14) [] (vfs_write+0x0/0x148) from [] (sys_write+0x40/0x78) r8:00000002 r7:4076d2c4 r6:e370fc40 r5:00000000 r4:00000000 [] (sys_write+0x0/0x78) from [] (ret_fast_syscall+0x0) r8:c0041908 r7:00000004 r6:00000002 r5:00000000 r4:4007cbe0 [ccross: moved spin_lock_init into existing #ifdef] Change-Id: If400d084eb20433c126ea1dd027a6be7f2ebb1f6 Signed-off-by: Mars Signed-off-by: Colin Cross --- arch/arm/common/fiq_debugger.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index 946a31403ce0..053680b6c326 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -1340,6 +1340,7 @@ static int fiq_debugger_probe(struct platform_device *pdev) handle_wakeup(state); #if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) + spin_lock_init(&state->console_lock); state->console = fiq_debugger_console; state->console.index = pdev->id; if (!console_set_on_cmdline) From 36247c2d3b43b94be6b1687be033ab10260ca574 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 5 Nov 2012 13:09:03 -0800 Subject: [PATCH 195/358] cpufreq: interactive: pin timers to associated CPU Helps avoid waking up other CPUs to react to activity on the local CPU. Change-Id: Ife272aaa7916894a437705d44521b1a1693fbe8e Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 106 +++++++------------------- 1 file changed, 27 insertions(+), 79 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 16bd23be2495..e53eae221499 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -42,8 +42,6 @@ struct cpufreq_interactive_cpuinfo { int timer_idlecancel; u64 time_in_idle; u64 idle_exit_time; - u64 timer_run_time; - int idling; u64 target_set_time; u64 target_set_time_in_idle; struct cpufreq_policy *policy; @@ -109,6 +107,7 @@ struct cpufreq_governor cpufreq_gov_interactive = { static void cpufreq_interactive_timer(unsigned long data) { + u64 now; unsigned int delta_idle; unsigned int delta_time; int cpu_load; @@ -127,26 +126,11 @@ static void cpufreq_interactive_timer(unsigned long data) if (!pcpu->governor_enabled) goto exit; - /* - * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time, - * this lets idle exit know the current idle time sample has - * been processed, and idle exit can generate a new sample and - * re-arm the timer. This prevents a concurrent idle - * exit on that CPU from writing a new set of info at the same time - * the timer function runs (the timer function can't use that info - * until more time passes). - */ time_in_idle = pcpu->time_in_idle; idle_exit_time = pcpu->idle_exit_time; - now_idle = get_cpu_idle_time_us(data, &pcpu->timer_run_time); - smp_wmb(); - - /* If we raced with cancelling a timer, skip. */ - if (!idle_exit_time) - goto exit; - + now_idle = get_cpu_idle_time_us(data, &now); delta_idle = (unsigned int)(now_idle - time_in_idle); - delta_time = (unsigned int)(pcpu->timer_run_time - idle_exit_time); + delta_time = (unsigned int)(now - idle_exit_time); /* * If timer ran less than 1ms after short-term sample started, retry. @@ -160,8 +144,7 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = 100 * (delta_time - delta_idle) / delta_time; delta_idle = (unsigned int)(now_idle - pcpu->target_set_time_in_idle); - delta_time = (unsigned int)(pcpu->timer_run_time - - pcpu->target_set_time); + delta_time = (unsigned int)(now - pcpu->target_set_time); if ((delta_time == 0) || (delta_idle > delta_time)) load_since_change = 0; @@ -189,7 +172,7 @@ static void cpufreq_interactive_timer(unsigned long data) if (pcpu->target_freq == hispeed_freq && new_freq > hispeed_freq && - pcpu->timer_run_time - pcpu->hispeed_validate_time + now - pcpu->hispeed_validate_time < above_hispeed_delay_val) { trace_cpufreq_interactive_notyet(data, cpu_load, pcpu->target_freq, @@ -202,7 +185,7 @@ static void cpufreq_interactive_timer(unsigned long data) } if (new_freq <= hispeed_freq) - pcpu->hispeed_validate_time = pcpu->timer_run_time; + pcpu->hispeed_validate_time = now; if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_H, @@ -219,8 +202,7 @@ static void cpufreq_interactive_timer(unsigned long data) * floor frequency for the minimum sample time since last validated. */ if (new_freq < pcpu->floor_freq) { - if (pcpu->timer_run_time - pcpu->floor_validate_time - < min_sample_time) { + if (now - pcpu->floor_validate_time < min_sample_time) { trace_cpufreq_interactive_notyet(data, cpu_load, pcpu->target_freq, new_freq); goto rearm; @@ -228,7 +210,7 @@ static void cpufreq_interactive_timer(unsigned long data) } pcpu->floor_freq = new_freq; - pcpu->floor_validate_time = pcpu->timer_run_time; + pcpu->floor_validate_time = now; if (pcpu->target_freq == new_freq) { trace_cpufreq_interactive_already(data, cpu_load, @@ -239,7 +221,7 @@ static void cpufreq_interactive_timer(unsigned long data) trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, new_freq); pcpu->target_set_time_in_idle = now_idle; - pcpu->target_set_time = pcpu->timer_run_time; + pcpu->target_set_time = now; pcpu->target_freq = new_freq; spin_lock_irqsave(&speedchange_cpumask_lock, flags); @@ -258,23 +240,16 @@ rearm_if_notmax: rearm: if (!timer_pending(&pcpu->cpu_timer)) { /* - * If already at min: if that CPU is idle, don't set timer. - * Else cancel the timer if that CPU goes idle. We don't - * need to re-evaluate speed until the next idle exit. + * If already at min, cancel the timer if that CPU goes idle. + * We don't need to re-evaluate speed until the next idle exit. */ - if (pcpu->target_freq == pcpu->policy->min) { - smp_rmb(); - - if (pcpu->idling) - goto exit; - + if (pcpu->target_freq == pcpu->policy->min) pcpu->timer_idlecancel = 1; - } pcpu->time_in_idle = get_cpu_idle_time_us( data, &pcpu->idle_exit_time); - mod_timer(&pcpu->cpu_timer, - jiffies + usecs_to_jiffies(timer_rate)); + mod_timer_pinned(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); } exit: @@ -290,8 +265,6 @@ static void cpufreq_interactive_idle_start(void) if (!pcpu->governor_enabled) return; - pcpu->idling = 1; - smp_wmb(); pending = timer_pending(&pcpu->cpu_timer); if (pcpu->target_freq != pcpu->policy->min) { @@ -308,8 +281,9 @@ static void cpufreq_interactive_idle_start(void) pcpu->time_in_idle = get_cpu_idle_time_us( smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, - jiffies + usecs_to_jiffies(timer_rate)); + mod_timer_pinned( + &pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); } #endif } else { @@ -321,12 +295,6 @@ static void cpufreq_interactive_idle_start(void) */ if (pending && pcpu->timer_idlecancel) { del_timer(&pcpu->cpu_timer); - /* - * Ensure last timer run time is after current idle - * sample start time, so next idle exit will always - * start a new idle sampling period. - */ - pcpu->idle_exit_time = 0; pcpu->timer_idlecancel = 0; } } @@ -341,29 +309,15 @@ static void cpufreq_interactive_idle_end(void) if (!pcpu->governor_enabled) return; - pcpu->idling = 0; - smp_wmb(); - - /* - * Arm the timer for 1-2 ticks later if not already, and if the timer - * function has already processed the previous load sampling - * interval. (If the timer is not pending but has not processed - * the previous interval, it is probably racing with us on another - * CPU. Let it compute load based on the previous sample and then - * re-arm the timer for another interval when it's done, rather - * than updating the interval start time to be "now", which doesn't - * give the timer function enough time to make a decision on this - * run.) - */ - if (timer_pending(&pcpu->cpu_timer) == 0 && - pcpu->timer_run_time >= pcpu->idle_exit_time && - pcpu->governor_enabled) { + /* Arm the timer for 1-2 ticks later if not already. */ + if (!timer_pending(&pcpu->cpu_timer)) { pcpu->time_in_idle = get_cpu_idle_time_us(smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, - jiffies + usecs_to_jiffies(timer_rate)); + mod_timer_pinned( + &pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); } } @@ -673,6 +627,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, freq_table = cpufreq_frequency_get_table(policy->cpu); + if (!hispeed_freq) + hispeed_freq = policy->max; for_each_cpu(j, policy->cpus) { pcpu = &per_cpu(cpuinfo, j); @@ -689,11 +645,11 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->target_set_time; pcpu->governor_enabled = 1; smp_wmb(); + pcpu->cpu_timer.expires = + jiffies + usecs_to_jiffies(timer_rate); + add_timer_on(&pcpu->cpu_timer, j); } - if (!hispeed_freq) - hispeed_freq = policy->max; - /* * Do not register the idle hook and create sysfs * entries if we have already done so. @@ -715,14 +671,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->governor_enabled = 0; smp_wmb(); del_timer_sync(&pcpu->cpu_timer); - - /* - * Reset idle exit time since we may cancel the timer - * before it can run after the last idle exit time, - * to avoid tripping the check in idle exit for a timer - * that is trying to run. - */ - pcpu->idle_exit_time = 0; } if (atomic_dec_return(&active_count) > 0) From 59e5c704bfe982c33b0145f13963e5bcff5bbc37 Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Thu, 1 Nov 2012 09:59:52 +0800 Subject: [PATCH 196/358] cpufreq: interactive: use deferrable timer by default Avoid wakeups only to handle the governor timer when the system is otherwise idle. For platforms where the power cost of remaining in idle at higher CPU speed may outweigh the cost of a governor wakeup from idle to lower the speed, set parameter cpufreq_interactive.governidle=1. Change-Id: Id6c43eb35caecf9b0574fcdd5b769711bc7e6de6 Signed-off-by: LianWei WANG Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index e53eae221499..71887cabfadb 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,11 @@ static unsigned long above_hispeed_delay_val; static int boost_val; +static bool governidle; +module_param(governidle, bool, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(governidle, + "Set to 1 to wake up CPUs from idle to reduce speed (default 0)"); + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -240,10 +246,11 @@ rearm_if_notmax: rearm: if (!timer_pending(&pcpu->cpu_timer)) { /* - * If already at min, cancel the timer if that CPU goes idle. - * We don't need to re-evaluate speed until the next idle exit. + * If governing speed in idle and already at min, cancel the + * timer if that CPU goes idle. We don't need to re-evaluate + * speed until the next idle exit. */ - if (pcpu->target_freq == pcpu->policy->min) + if (governidle && pcpu->target_freq == pcpu->policy->min) pcpu->timer_idlecancel = 1; pcpu->time_in_idle = get_cpu_idle_time_us( @@ -268,7 +275,6 @@ static void cpufreq_interactive_idle_start(void) pending = timer_pending(&pcpu->cpu_timer); if (pcpu->target_freq != pcpu->policy->min) { -#ifdef CONFIG_SMP /* * Entering idle while not at lowest speed. On some * platforms this can hold the other CPU(s) at that speed @@ -285,8 +291,7 @@ static void cpufreq_interactive_idle_start(void) &pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); } -#endif - } else { + } else if (governidle) { /* * If at min speed and entering idle after load has * already been evaluated, and a timer has been set just in @@ -708,7 +713,10 @@ static int __init cpufreq_interactive_init(void) /* Initalize per-cpu timers */ for_each_possible_cpu(i) { pcpu = &per_cpu(cpuinfo, i); - init_timer(&pcpu->cpu_timer); + if (governidle) + init_timer(&pcpu->cpu_timer); + else + init_timer_deferrable(&pcpu->cpu_timer); pcpu->cpu_timer.function = cpufreq_interactive_timer; pcpu->cpu_timer.data = i; } From 77c484502f41543f47bd71476daff642636bf0af Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 8 Oct 2012 20:14:34 -0700 Subject: [PATCH 197/358] cpufreq: interactive: kick timer on idle exit past expiry The deferrable timer list isn't checked on all idle exits, such as when hi-res timers expire or ISRs schedule workers. If the idle loop is exited and it's past time to run the governor load polling timer, run it immediately. This ensures we handle load spikes caused by actvity that does not run the normal timer list. Rename the field that timestamps the "time_in_idle" value to be more accurate. Change-Id: Ied590ecbefc83c9a9ec5eb9e31903557f6fa1614 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 43 +++++++++++++-------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 71887cabfadb..81537692903e 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -42,7 +42,7 @@ struct cpufreq_interactive_cpuinfo { struct timer_list cpu_timer; int timer_idlecancel; u64 time_in_idle; - u64 idle_exit_time; + u64 time_in_idle_timestamp; u64 target_set_time; u64 target_set_time_in_idle; struct cpufreq_policy *policy; @@ -111,6 +111,16 @@ struct cpufreq_governor cpufreq_gov_interactive = { .owner = THIS_MODULE, }; +static void cpufreq_interactive_timer_resched( + struct cpufreq_interactive_cpuinfo *pcpu) +{ + mod_timer_pinned(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); + pcpu->time_in_idle = + get_cpu_idle_time_us(smp_processor_id(), + &pcpu->time_in_idle_timestamp); +} + static void cpufreq_interactive_timer(unsigned long data) { u64 now; @@ -118,8 +128,6 @@ static void cpufreq_interactive_timer(unsigned long data) unsigned int delta_time; int cpu_load; int load_since_change; - u64 time_in_idle; - u64 idle_exit_time; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, data); u64 now_idle; @@ -132,11 +140,9 @@ static void cpufreq_interactive_timer(unsigned long data) if (!pcpu->governor_enabled) goto exit; - time_in_idle = pcpu->time_in_idle; - idle_exit_time = pcpu->idle_exit_time; now_idle = get_cpu_idle_time_us(data, &now); - delta_idle = (unsigned int)(now_idle - time_in_idle); - delta_time = (unsigned int)(now - idle_exit_time); + delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); + delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); /* * If timer ran less than 1ms after short-term sample started, retry. @@ -253,10 +259,7 @@ rearm: if (governidle && pcpu->target_freq == pcpu->policy->min) pcpu->timer_idlecancel = 1; - pcpu->time_in_idle = get_cpu_idle_time_us( - data, &pcpu->idle_exit_time); - mod_timer_pinned(&pcpu->cpu_timer, - jiffies + usecs_to_jiffies(timer_rate)); + cpufreq_interactive_timer_resched(pcpu); } exit: @@ -284,12 +287,8 @@ static void cpufreq_interactive_idle_start(void) * the CPUFreq driver. */ if (!pending) { - pcpu->time_in_idle = get_cpu_idle_time_us( - smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer_pinned( - &pcpu->cpu_timer, - jiffies + usecs_to_jiffies(timer_rate)); + cpufreq_interactive_timer_resched(pcpu); } } else if (governidle) { /* @@ -316,15 +315,13 @@ static void cpufreq_interactive_idle_end(void) /* Arm the timer for 1-2 ticks later if not already. */ if (!timer_pending(&pcpu->cpu_timer)) { - pcpu->time_in_idle = - get_cpu_idle_time_us(smp_processor_id(), - &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer_pinned( - &pcpu->cpu_timer, - jiffies + usecs_to_jiffies(timer_rate)); + cpufreq_interactive_timer_resched(pcpu); + } else if (!governidle && + time_after_eq(jiffies, pcpu->cpu_timer.expires)) { + del_timer(&pcpu->cpu_timer); + cpufreq_interactive_timer(smp_processor_id()); } - } static int cpufreq_interactive_speedchange_task(void *data) From 1258da61dc31a7ac2bf12af34a77c70871d5dc20 Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Mon, 19 Nov 2012 11:44:51 -0800 Subject: [PATCH 198/358] netfilter: qtaguid: Don't BUG_ON if create_if_tag_stat fails If create_if_tag_stat fails to allocate memory (GFP_ATOMIC) the following will happen: qtaguid: iface_stat: tag stat alloc failed ... kernel BUG at xt_qtaguid.c:1482! Signed-off-by: Pontus Fuchs --- net/netfilter/xt_qtaguid.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 6b22563a924f..603bdd206990 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1461,6 +1461,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. */ new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); + if (!new_tag_stat) + goto unlock; uid_tag_counters = &new_tag_stat->counters; } else { uid_tag_counters = &tag_stat_entry->counters; @@ -1469,6 +1471,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, if (acct_tag) { /* Create the child {acct_tag, uid_tag} and hook up parent. */ new_tag_stat = create_if_tag_stat(iface_entry, tag); + if (!new_tag_stat) + goto unlock; new_tag_stat->parent_counters = uid_tag_counters; } else { /* @@ -1482,6 +1486,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, BUG_ON(!new_tag_stat); } tag_stat_update(new_tag_stat, direction, proto, bytes); +unlock: spin_unlock_bh(&iface_entry->tag_stat_list_lock); } From 182f4363544a18a6a11d1dd2a2a6b45929ceba3c Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 28 Nov 2012 13:03:40 -0800 Subject: [PATCH 199/358] usb: gadget: accessory: Fix section mismatch (again) create_bulk_endpoints should not be __init since it is called when accessory is enabled. Change-Id: Iac6e9f29d53c93760e926efd8e7603432632acb4 Signed-off-by: Todd Poynor --- drivers/usb/gadget/f_accessory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 921db5a193d6..a244265c1143 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -482,7 +482,7 @@ static int acc_unregister_hid(struct acc_dev *dev, int id) return 0; } -static int __init create_bulk_endpoints(struct acc_dev *dev, +static int create_bulk_endpoints(struct acc_dev *dev, struct usb_endpoint_descriptor *in_desc, struct usb_endpoint_descriptor *out_desc) { From f8e903fe01ce61bf10edce4a88ca313204751bf8 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 28 Nov 2012 17:56:09 -0800 Subject: [PATCH 200/358] cpufreq: interactive: trace actual speed in target speed decisions Tracing adds actual speed since this is expected to be key to the choice of target speed. Change-Id: Iec936102d0010c4e9dfa143c38a9fd0d551189c3 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 18 ++++++----- include/trace/events/cpufreq_interactive.h | 36 +++++++++++++--------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 81537692903e..2b4aad97385c 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -186,9 +186,9 @@ static void cpufreq_interactive_timer(unsigned long data) new_freq > hispeed_freq && now - pcpu->hispeed_validate_time < above_hispeed_delay_val) { - trace_cpufreq_interactive_notyet(data, cpu_load, - pcpu->target_freq, - new_freq); + trace_cpufreq_interactive_notyet( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); goto rearm; } } @@ -215,8 +215,9 @@ static void cpufreq_interactive_timer(unsigned long data) */ if (new_freq < pcpu->floor_freq) { if (now - pcpu->floor_validate_time < min_sample_time) { - trace_cpufreq_interactive_notyet(data, cpu_load, - pcpu->target_freq, new_freq); + trace_cpufreq_interactive_notyet( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); goto rearm; } } @@ -225,13 +226,14 @@ static void cpufreq_interactive_timer(unsigned long data) pcpu->floor_validate_time = now; if (pcpu->target_freq == new_freq) { - trace_cpufreq_interactive_already(data, cpu_load, - pcpu->target_freq, new_freq); + trace_cpufreq_interactive_already( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); goto rearm_if_notmax; } trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, - new_freq); + pcpu->policy->cur, new_freq); pcpu->target_set_time_in_idle = now_idle; pcpu->target_set_time = now; diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index ecec7970b871..951e6ca12da8 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -36,44 +36,50 @@ DEFINE_EVENT(set, cpufreq_interactive_setspeed, DECLARE_EVENT_CLASS(loadeval, TP_PROTO(unsigned long cpu_id, unsigned long load, - unsigned long curfreq, unsigned long targfreq), - TP_ARGS(cpu_id, load, curfreq, targfreq), + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg), TP_STRUCT__entry( __field(unsigned long, cpu_id ) __field(unsigned long, load ) - __field(unsigned long, curfreq ) - __field(unsigned long, targfreq ) + __field(unsigned long, curtarg ) + __field(unsigned long, curactual ) + __field(unsigned long, newtarg ) ), TP_fast_assign( __entry->cpu_id = cpu_id; __entry->load = load; - __entry->curfreq = curfreq; - __entry->targfreq = targfreq; + __entry->curtarg = curtarg; + __entry->curactual = curactual; + __entry->newtarg = newtarg; ), - TP_printk("cpu=%lu load=%lu cur=%lu targ=%lu", - __entry->cpu_id, __entry->load, __entry->curfreq, - __entry->targfreq) + TP_printk("cpu=%lu load=%lu cur=%lu actual=%lu targ=%lu", + __entry->cpu_id, __entry->load, __entry->curtarg, + __entry->curactual, __entry->newtarg) ); DEFINE_EVENT(loadeval, cpufreq_interactive_target, TP_PROTO(unsigned long cpu_id, unsigned long load, - unsigned long curfreq, unsigned long targfreq), - TP_ARGS(cpu_id, load, curfreq, targfreq) + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ); DEFINE_EVENT(loadeval, cpufreq_interactive_already, TP_PROTO(unsigned long cpu_id, unsigned long load, - unsigned long curfreq, unsigned long targfreq), - TP_ARGS(cpu_id, load, curfreq, targfreq) + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ); DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, TP_PROTO(unsigned long cpu_id, unsigned long load, - unsigned long curfreq, unsigned long targfreq), - TP_ARGS(cpu_id, load, curfreq, targfreq) + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ); TRACE_EVENT(cpufreq_interactive_boost, From 9e077b4b5d461664a4be94d72876a5dd3c62df40 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 28 Nov 2012 17:58:17 -0800 Subject: [PATCH 201/358] cpufreq: interactive: change speed according to current speed and target load Add a target_load attribute that specifies how aggressively the governor is to adjust speed to meet the observed load. New target speed is calculated as the current actual speed (may be higher than target speed on SMP) times the CPU load (as a fraction) divided by target load (fraction). cpufreq_frequency_table_target() call use CPUFREQ_RELATION_L to set the next higher speed rather than next lower speed. Change-Id: If432451da82f5fed12e15c9421d7d27792376150 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 35 ++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 2b4aad97385c..c4293c5741f7 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -68,6 +68,10 @@ static unsigned int hispeed_freq; #define DEFAULT_GO_HISPEED_LOAD 85 static unsigned long go_hispeed_load; +/* Target load. Lower values result in higher CPU speeds. */ +#define DEFAULT_TARGET_LOAD 90 +static unsigned long target_load = DEFAULT_TARGET_LOAD; + /* * The minimum amount of time to spend at a frequency before we can ramp down. */ @@ -177,7 +181,7 @@ static void cpufreq_interactive_timer(unsigned long data) hispeed_freq < pcpu->policy->max) { new_freq = hispeed_freq; } else { - new_freq = pcpu->policy->max * cpu_load / 100; + new_freq = pcpu->policy->cur * cpu_load / target_load; if (new_freq < hispeed_freq) new_freq = hispeed_freq; @@ -193,14 +197,14 @@ static void cpufreq_interactive_timer(unsigned long data) } } } else { - new_freq = hispeed_freq * cpu_load / 100; + new_freq = pcpu->policy->cur * cpu_load / target_load; } if (new_freq <= hispeed_freq) pcpu->hispeed_validate_time = now; if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, - new_freq, CPUFREQ_RELATION_H, + new_freq, CPUFREQ_RELATION_L, &index)) { pr_warn_once("timer %d: cpufreq_frequency_table_target error\n", (int) data); @@ -420,6 +424,30 @@ static void cpufreq_interactive_boost(void) wake_up_process(speedchange_task); } +static ssize_t show_target_load( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", target_load); +} + +static ssize_t store_target_load( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + target_load = val; + return count; +} + +static struct global_attr target_load_attr = + __ATTR(target_load, S_IRUGO | S_IWUSR, + show_target_load, store_target_load); + static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -581,6 +609,7 @@ static struct global_attr boostpulse = __ATTR(boostpulse, 0200, NULL, store_boostpulse); static struct attribute *interactive_attributes[] = { + &target_load_attr.attr, &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, &above_hispeed_delay.attr, From ce51d432cbcb77e4fba4665c0f7e3721cd6a854e Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 8 Nov 2012 15:06:55 -0800 Subject: [PATCH 202/358] cpufreq: interactive: apply above_hispeed_delay to each step above hispeed Apply above_hispeed_delay whenever increasing speed to a new speed above hispeed (not just the first step above hispeed). Change-Id: Ibb7add7db47f2a4306a9458c4e1ebabb60698636 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 36 ++++++++++----------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index c4293c5741f7..17c42cc12dc8 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -176,32 +176,22 @@ static void cpufreq_interactive_timer(unsigned long data) if (load_since_change > cpu_load) cpu_load = load_since_change; - if (cpu_load >= go_hispeed_load || boost_val) { - if (pcpu->target_freq < hispeed_freq && - hispeed_freq < pcpu->policy->max) { - new_freq = hispeed_freq; - } else { - new_freq = pcpu->policy->cur * cpu_load / target_load; - - if (new_freq < hispeed_freq) - new_freq = hispeed_freq; - - if (pcpu->target_freq == hispeed_freq && - new_freq > hispeed_freq && - now - pcpu->hispeed_validate_time - < above_hispeed_delay_val) { - trace_cpufreq_interactive_notyet( - data, cpu_load, pcpu->target_freq, - pcpu->policy->cur, new_freq); - goto rearm; - } - } - } else { + if ((cpu_load >= go_hispeed_load || boost_val) && + pcpu->target_freq < hispeed_freq) + new_freq = hispeed_freq; + else new_freq = pcpu->policy->cur * cpu_load / target_load; + + if (pcpu->target_freq >= hispeed_freq && + new_freq > pcpu->target_freq && + now - pcpu->hispeed_validate_time < above_hispeed_delay_val) { + trace_cpufreq_interactive_notyet( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + goto rearm; } - if (new_freq <= hispeed_freq) - pcpu->hispeed_validate_time = now; + pcpu->hispeed_validate_time = now; if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_L, From 4922a4050e85d07d51484d92b6d7ba4919423b32 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 14 Nov 2012 11:41:21 -0800 Subject: [PATCH 203/358] cpufreq: interactive: allow arbitrary speed / target load mappings Accept a string of target loads and speeds at which to apply the target loads, per the documentation update in this patch. For example, "85 1000000:90 1700000:99" targets CPU load 85% below speed 1GHz, 90% at or above 1GHz, until 1.7GHz and above, at which load 99% is targeted. Attempt to avoid oscillations by evaluating the current speed weighted by current load against each new choice of speed, choosing a higher speed if the current load requires a higher speed. Change-Id: Ie3300206047c84eca5a26b0b63ea512e5207550e Signed-off-by: Todd Poynor --- Documentation/cpu-freq/governors.txt | 17 +++ drivers/cpufreq/cpufreq_interactive.c | 186 ++++++++++++++++++++++++-- 2 files changed, 189 insertions(+), 14 deletions(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index f02f8d465863..d44901eedcf8 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -244,6 +244,23 @@ short-term load since idle exit to determine the cpu speed to ramp to. The tuneable values for this governor are: +target_loads: CPU load values used to adjust speed to influence the +current CPU load toward that value. In general, the lower the target +load, the more often the governor will raise CPU speeds to bring load +below the target. The format is a single target load, optionally +followed by pairs of CPU speeds and CPU loads to target at or above +those speeds. Colons can be used between the speeds and associated +target loads for readability. For example: + + 85 1000000:90 1700000:99 + +targets CPU load 85% below speed 1GHz, 90% at or above 1GHz, until +1.7GHz and above, at which load 99% is targeted. If speeds are +specified these must appear in ascending order. Higher target load +values are typically specified for higher speeds, that is, target load +values also usually appear in an ascending order. The default is +target load 90% for all speeds. + min_sample_time: The minimum amount of time to spend at the current frequency before ramping down. This is to ensure that the governor has seen enough historic cpu load data to determine the appropriate diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 17c42cc12dc8..6ea77a0d6b80 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -70,7 +70,10 @@ static unsigned long go_hispeed_load; /* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 -static unsigned long target_load = DEFAULT_TARGET_LOAD; +static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; +static spinlock_t target_loads_lock; +static unsigned int *target_loads = default_target_loads; +static int ntarget_loads = ARRAY_SIZE(default_target_loads); /* * The minimum amount of time to spend at a frequency before we can ramp down. @@ -125,6 +128,110 @@ static void cpufreq_interactive_timer_resched( &pcpu->time_in_idle_timestamp); } +static unsigned int freq_to_targetload(unsigned int freq) +{ + int i; + unsigned int ret; + + spin_lock(&target_loads_lock); + + for (i = 0; i < ntarget_loads - 1 && freq >= target_loads[i+1]; i += 2) + ; + + ret = target_loads[i]; + spin_unlock(&target_loads_lock); + return ret; +} + +/* + * If increasing frequencies never map to a lower target load then + * choose_freq() will find the minimum frequency that does not exceed its + * target load given the current load. + */ + +static unsigned int choose_freq( + struct cpufreq_interactive_cpuinfo *pcpu, unsigned int curload) +{ + unsigned int freq = pcpu->policy->cur; + unsigned int loadadjfreq = freq * curload; + unsigned int prevfreq, freqmin, freqmax; + unsigned int tl; + int index; + + freqmin = 0; + freqmax = UINT_MAX; + + do { + prevfreq = freq; + tl = freq_to_targetload(freq); + + /* + * Find the lowest frequency where the computed load is less + * than or equal to the target load. + */ + + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, loadadjfreq / tl, + CPUFREQ_RELATION_L, &index); + freq = pcpu->freq_table[index].frequency; + + if (freq > prevfreq) { + /* The previous frequency is too low. */ + freqmin = prevfreq; + + if (freq >= freqmax) { + /* + * Find the highest frequency that is less + * than freqmax. + */ + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmax - 1, CPUFREQ_RELATION_H, + &index); + freq = pcpu->freq_table[index].frequency; + + if (freq == freqmin) { + /* + * The first frequency below freqmax + * has already been found to be too + * low. freqmax is the lowest speed + * we found that is fast enough. + */ + freq = freqmax; + break; + } + } + } else if (freq < prevfreq) { + /* The previous frequency is high enough. */ + freqmax = prevfreq; + + if (freq <= freqmin) { + /* + * Find the lowest frequency that is higher + * than freqmin. + */ + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmin + 1, CPUFREQ_RELATION_L, + &index); + freq = pcpu->freq_table[index].frequency; + + /* + * If freqmax is the first frequency above + * freqmin then we have already found that + * this speed is fast enough. + */ + if (freq == freqmax) + break; + } + } + + /* If same frequency chosen as previous then done. */ + } while (freq != prevfreq); + + return freq; +} + static void cpufreq_interactive_timer(unsigned long data) { u64 now; @@ -180,7 +287,7 @@ static void cpufreq_interactive_timer(unsigned long data) pcpu->target_freq < hispeed_freq) new_freq = hispeed_freq; else - new_freq = pcpu->policy->cur * cpu_load / target_load; + new_freq = choose_freq(pcpu, cpu_load); if (pcpu->target_freq >= hispeed_freq && new_freq > pcpu->target_freq && @@ -414,29 +521,79 @@ static void cpufreq_interactive_boost(void) wake_up_process(speedchange_task); } -static ssize_t show_target_load( +static ssize_t show_target_loads( struct kobject *kobj, struct attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", target_load); + int i; + ssize_t ret = 0; + + spin_lock(&target_loads_lock); + + for (i = 0; i < ntarget_loads; i++) + ret += sprintf(buf + ret, "%u%s", target_loads[i], + i & 0x1 ? ":" : " "); + + ret += sprintf(buf + ret, "\n"); + spin_unlock(&target_loads_lock); + return ret; } -static ssize_t store_target_load( +static ssize_t store_target_loads( struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { int ret; - unsigned long val; + const char *cp; + unsigned int *new_target_loads = NULL; + int ntokens = 1; + int i; - ret = strict_strtoul(buf, 0, &val); - if (ret < 0) - return ret; - target_load = val; + cp = buf; + while ((cp = strpbrk(cp + 1, " :"))) + ntokens++; + + if (!(ntokens & 0x1)) + goto err_inval; + + new_target_loads = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); + if (!new_target_loads) { + ret = -ENOMEM; + goto err; + } + + cp = buf; + i = 0; + while (i < ntokens) { + if (sscanf(cp, "%u", &new_target_loads[i++]) != 1) + goto err_inval; + + cp = strpbrk(cp, " :"); + if (!cp) + break; + cp++; + } + + if (i != ntokens) + goto err_inval; + + spin_lock(&target_loads_lock); + if (target_loads != default_target_loads) + kfree(target_loads); + target_loads = new_target_loads; + ntarget_loads = ntokens; + spin_unlock(&target_loads_lock); return count; + +err_inval: + ret = -EINVAL; +err: + kfree(new_target_loads); + return ret; } -static struct global_attr target_load_attr = - __ATTR(target_load, S_IRUGO | S_IWUSR, - show_target_load, store_target_load); +static struct global_attr target_loads_attr = + __ATTR(target_loads, S_IRUGO | S_IWUSR, + show_target_loads, store_target_loads); static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) @@ -599,7 +756,7 @@ static struct global_attr boostpulse = __ATTR(boostpulse, 0200, NULL, store_boostpulse); static struct attribute *interactive_attributes[] = { - &target_load_attr.attr, + &target_loads_attr.attr, &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, &above_hispeed_delay.attr, @@ -739,6 +896,7 @@ static int __init cpufreq_interactive_init(void) pcpu->cpu_timer.data = i; } + spin_lock_init(&target_loads_lock); spin_lock_init(&speedchange_cpumask_lock); speedchange_task = kthread_create(cpufreq_interactive_speedchange_task, NULL, From 9bd5ea20da387b84a900b9fe2b012f53539333d7 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 7 Dec 2012 20:08:45 -0800 Subject: [PATCH 204/358] cpufreq: interactive: remove load since last speed change The longer-term load since last speed change isn't terribly useful, may delay recognition of dropping load, and would need forthcoming changes to adjust load for changing CPU speeds. Drop it. Change-Id: Ic3cbb0542cc3484617031787e03ed9bdd632dec1 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 34 ++++----------------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 6ea77a0d6b80..3d8e7b4360a5 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -43,8 +43,6 @@ struct cpufreq_interactive_cpuinfo { int timer_idlecancel; u64 time_in_idle; u64 time_in_idle_timestamp; - u64 target_set_time; - u64 target_set_time_in_idle; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; @@ -238,7 +236,6 @@ static void cpufreq_interactive_timer(unsigned long data) unsigned int delta_idle; unsigned int delta_time; int cpu_load; - int load_since_change; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, data); u64 now_idle; @@ -266,23 +263,6 @@ static void cpufreq_interactive_timer(unsigned long data) else cpu_load = 100 * (delta_time - delta_idle) / delta_time; - delta_idle = (unsigned int)(now_idle - pcpu->target_set_time_in_idle); - delta_time = (unsigned int)(now - pcpu->target_set_time); - - if ((delta_time == 0) || (delta_idle > delta_time)) - load_since_change = 0; - else - load_since_change = - 100 * (delta_time - delta_idle) / delta_time; - - /* - * Choose greater of short-term load (since last idle timer - * started or timer function re-armed itself) or long-term load - * (since last frequency change). - */ - if (load_since_change > cpu_load) - cpu_load = load_since_change; - if ((cpu_load >= go_hispeed_load || boost_val) && pcpu->target_freq < hispeed_freq) new_freq = hispeed_freq; @@ -335,8 +315,6 @@ static void cpufreq_interactive_timer(unsigned long data) trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); - pcpu->target_set_time_in_idle = now_idle; - pcpu->target_set_time = now; pcpu->target_freq = new_freq; spin_lock_irqsave(&speedchange_cpumask_lock, flags); @@ -500,9 +478,8 @@ static void cpufreq_interactive_boost(void) if (pcpu->target_freq < hispeed_freq) { pcpu->target_freq = hispeed_freq; cpumask_set_cpu(i, &speedchange_cpumask); - pcpu->target_set_time_in_idle = - get_cpu_idle_time_us(i, &pcpu->target_set_time); - pcpu->hispeed_validate_time = pcpu->target_set_time; + pcpu->hispeed_validate_time = + ktime_to_us(ktime_get()); anyboost = 1; } @@ -815,14 +792,11 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->policy = policy; pcpu->target_freq = policy->cur; pcpu->freq_table = freq_table; - pcpu->target_set_time_in_idle = - get_cpu_idle_time_us(j, - &pcpu->target_set_time); pcpu->floor_freq = pcpu->target_freq; pcpu->floor_validate_time = - pcpu->target_set_time; + ktime_to_us(ktime_get()); pcpu->hispeed_validate_time = - pcpu->target_set_time; + pcpu->floor_validate_time; pcpu->governor_enabled = 1; smp_wmb(); pcpu->cpu_timer.expires = From 62ca69e55859b019bbe174241965c2d3090bfcd2 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 11 Dec 2012 16:05:03 -0800 Subject: [PATCH 205/358] cpufreq: interactive: adjust load for changes in speed Add notifier for speed transitions. Keep a count of CPU active microseconds times current frequency, converted to a percentage relative to the current frequency when load is evaluated. Change-Id: I5c27adb11081c50490219784ca57cc46e97fc28c Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 87 ++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 16 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 3d8e7b4360a5..d0d51ee30727 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -41,8 +41,11 @@ static atomic_t active_count = ATOMIC_INIT(0); struct cpufreq_interactive_cpuinfo { struct timer_list cpu_timer; int timer_idlecancel; + spinlock_t load_lock; /* protects the next 4 fields */ u64 time_in_idle; u64 time_in_idle_timestamp; + u64 cputime_speedadj; + u64 cputime_speedadj_timestamp; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; @@ -121,9 +124,13 @@ static void cpufreq_interactive_timer_resched( { mod_timer_pinned(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); + spin_lock(&pcpu->load_lock); pcpu->time_in_idle = get_cpu_idle_time_us(smp_processor_id(), &pcpu->time_in_idle_timestamp); + pcpu->cputime_speedadj = 0; + pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; + spin_unlock(&pcpu->load_lock); } static unsigned int freq_to_targetload(unsigned int freq) @@ -148,10 +155,9 @@ static unsigned int freq_to_targetload(unsigned int freq) */ static unsigned int choose_freq( - struct cpufreq_interactive_cpuinfo *pcpu, unsigned int curload) + struct cpufreq_interactive_cpuinfo *pcpu, unsigned int loadadjfreq) { unsigned int freq = pcpu->policy->cur; - unsigned int loadadjfreq = freq * curload; unsigned int prevfreq, freqmin, freqmax; unsigned int tl; int index; @@ -230,16 +236,36 @@ static unsigned int choose_freq( return freq; } +static u64 update_load(int cpu) +{ + struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); + u64 now; + u64 now_idle; + unsigned int delta_idle; + unsigned int delta_time; + u64 active_time; + + now_idle = get_cpu_idle_time_us(cpu, &now); + delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); + delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); + active_time = delta_time - delta_idle; + pcpu->cputime_speedadj += active_time * pcpu->policy->cur; + + pcpu->time_in_idle = now_idle; + pcpu->time_in_idle_timestamp = now; + return now; +} + static void cpufreq_interactive_timer(unsigned long data) { u64 now; - unsigned int delta_idle; unsigned int delta_time; + u64 cputime_speedadj; int cpu_load; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, data); - u64 now_idle; unsigned int new_freq; + unsigned int loadadjfreq; unsigned int index; unsigned long flags; @@ -248,26 +274,24 @@ static void cpufreq_interactive_timer(unsigned long data) if (!pcpu->governor_enabled) goto exit; - now_idle = get_cpu_idle_time_us(data, &now); - delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); - delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); + spin_lock(&pcpu->load_lock); + now = update_load(data); + delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp); + cputime_speedadj = pcpu->cputime_speedadj; + spin_unlock(&pcpu->load_lock); - /* - * If timer ran less than 1ms after short-term sample started, retry. - */ - if (delta_time < 1000) + if (WARN_ON_ONCE(!delta_time)) goto rearm; - if (delta_idle > delta_time) - cpu_load = 0; - else - cpu_load = 100 * (delta_time - delta_idle) / delta_time; + do_div(cputime_speedadj, delta_time); + loadadjfreq = (unsigned int)cputime_speedadj * 100; + cpu_load = loadadjfreq / pcpu->target_freq; if ((cpu_load >= go_hispeed_load || boost_val) && pcpu->target_freq < hispeed_freq) new_freq = hispeed_freq; else - new_freq = choose_freq(pcpu, cpu_load); + new_freq = choose_freq(pcpu, loadadjfreq); if (pcpu->target_freq >= hispeed_freq && new_freq > pcpu->target_freq && @@ -498,6 +522,32 @@ static void cpufreq_interactive_boost(void) wake_up_process(speedchange_task); } +static int cpufreq_interactive_notifier( + struct notifier_block *nb, unsigned long val, void *data) +{ + struct cpufreq_freqs *freq = data; + struct cpufreq_interactive_cpuinfo *pcpu; + int cpu; + + if (val == CPUFREQ_POSTCHANGE) { + pcpu = &per_cpu(cpuinfo, freq->cpu); + + for_each_cpu(cpu, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, cpu); + spin_lock(&pjcpu->load_lock); + update_load(cpu); + spin_unlock(&pjcpu->load_lock); + } + } + + return 0; +} + +static struct notifier_block cpufreq_notifier_block = { + .notifier_call = cpufreq_interactive_notifier, +}; + static ssize_t show_target_loads( struct kobject *kobj, struct attribute *attr, char *buf) { @@ -817,6 +867,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, return rc; idle_notifier_register(&cpufreq_interactive_idle_nb); + cpufreq_register_notifier( + &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); break; case CPUFREQ_GOV_STOP: @@ -830,6 +882,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (atomic_dec_return(&active_count) > 0) return 0; + cpufreq_unregister_notifier( + &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); idle_notifier_unregister(&cpufreq_interactive_idle_nb); sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); @@ -868,6 +922,7 @@ static int __init cpufreq_interactive_init(void) init_timer_deferrable(&pcpu->cpu_timer); pcpu->cpu_timer.function = cpufreq_interactive_timer; pcpu->cpu_timer.data = i; + spin_lock_init(&pcpu->load_lock); } spin_lock_init(&target_loads_lock); From de091367ead15b6e95dd1d0743a18f0da5a07ee5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 14 Dec 2012 17:31:19 -0800 Subject: [PATCH 206/358] cpufreq: interactive: specify duration of CPU speed boost pulse Sysfs attribute boostpulse_duration specifies the duration of boosting CPU speed in response to bootpulse events. Duration is specified in usecs, default 80ms. Change-Id: Ifd41625574891a44f1787a4e85d1e7b4f2afb52b Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 52 +++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index d0d51ee30727..8b44a82aa146 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -95,11 +95,12 @@ static unsigned long timer_rate; #define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE static unsigned long above_hispeed_delay_val; -/* - * Non-zero means longer-term speed boost active. - */ - +/* Non-zero means indefinite speed boost active */ static int boost_val; +/* Duration of a boot pulse in usecs */ +static int boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; +/* End time of boost pulse in ktime converted to usecs */ +static u64 boostpulse_endtime; static bool governidle; module_param(governidle, bool, S_IWUSR | S_IRUGO); @@ -268,6 +269,7 @@ static void cpufreq_interactive_timer(unsigned long data) unsigned int loadadjfreq; unsigned int index; unsigned long flags; + bool boosted; smp_rmb(); @@ -286,8 +288,9 @@ static void cpufreq_interactive_timer(unsigned long data) do_div(cputime_speedadj, delta_time); loadadjfreq = (unsigned int)cputime_speedadj * 100; cpu_load = loadadjfreq / pcpu->target_freq; + boosted = boost_val || now < boostpulse_endtime; - if ((cpu_load >= go_hispeed_load || boost_val) && + if ((cpu_load >= go_hispeed_load || boosted) && pcpu->target_freq < hispeed_freq) new_freq = hispeed_freq; else @@ -327,8 +330,18 @@ static void cpufreq_interactive_timer(unsigned long data) } } - pcpu->floor_freq = new_freq; - pcpu->floor_validate_time = now; + /* + * Update the timestamp for checking whether speed has been held at + * or above the selected frequency for a minimum of min_sample_time, + * if not boosted to hispeed_freq. If boosted to hispeed_freq then we + * allow the speed to drop as soon as the boostpulse duration expires + * (or the indefinite boost is turned off). + */ + + if (!boosted || new_freq > hispeed_freq) { + pcpu->floor_freq = new_freq; + pcpu->floor_validate_time = now; + } if (pcpu->target_freq == new_freq) { trace_cpufreq_interactive_already( @@ -774,6 +787,7 @@ static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, if (ret < 0) return ret; + boostpulse_endtime = ktime_to_us(ktime_get()) + boostpulse_duration_val; trace_cpufreq_interactive_boost("pulse"); cpufreq_interactive_boost(); return count; @@ -782,6 +796,29 @@ static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, static struct global_attr boostpulse = __ATTR(boostpulse, 0200, NULL, store_boostpulse); +static ssize_t show_boostpulse_duration( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", boostpulse_duration_val); +} + +static ssize_t store_boostpulse_duration( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + boostpulse_duration_val = val; + return count; +} + +define_one_global_rw(boostpulse_duration); + static struct attribute *interactive_attributes[] = { &target_loads_attr.attr, &hispeed_freq_attr.attr, @@ -791,6 +828,7 @@ static struct attribute *interactive_attributes[] = { &timer_rate_attr.attr, &boost.attr, &boostpulse.attr, + &boostpulse_duration.attr, NULL, }; From e43009fd646bb519fe5d5f0988c7e8c952dd658d Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 11 Jan 2013 13:51:48 -0800 Subject: [PATCH 207/358] hardlockup: detect hard lockups without NMIs using secondary cpus Emulate NMIs on systems where they are not available by using timer interrupts on other cpus. Each cpu will use its softlockup hrtimer to check that the next cpu is processing hrtimer interrupts by verifying that a counter is increasing. This patch is useful on systems where the hardlockup detector is not available due to a lack of NMIs, for example most ARM SoCs. Without this patch any cpu stuck with interrupts disabled can cause a hardware watchdog reset with no debugging information, but with this patch the kernel can detect the lockup and panic, which can result in useful debugging info. Signed-off-by: Colin Cross --- include/linux/nmi.h | 5 +- kernel/watchdog.c | 123 ++++++++++++++++++++++++++++++++++++++++++-- lib/Kconfig.debug | 14 ++++- 3 files changed, 135 insertions(+), 7 deletions(-) diff --git a/include/linux/nmi.h b/include/linux/nmi.h index 6a45fb583ff1..084b4c5a80d2 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -14,8 +14,11 @@ * may be used to reset the timeout - for code which intentionally * disables interrupts for a long time. This call is stateless. */ -#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) +#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR_NMI) #include +#endif + +#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR) extern void touch_nmi_watchdog(void); #else static inline void touch_nmi_watchdog(void) diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 4431610f049a..68fbd3f169f1 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -45,6 +45,11 @@ static DEFINE_PER_CPU(unsigned long, soft_lockup_hrtimer_cnt); static DEFINE_PER_CPU(bool, hard_watchdog_warn); static DEFINE_PER_CPU(bool, watchdog_nmi_touch); static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved); +#endif +#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU +static cpumask_t __read_mostly watchdog_cpus; +#endif +#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI static DEFINE_PER_CPU(struct perf_event *, watchdog_ev); #endif @@ -178,7 +183,7 @@ void touch_softlockup_watchdog_sync(void) __raw_get_cpu_var(watchdog_touch_ts) = 0; } -#ifdef CONFIG_HARDLOCKUP_DETECTOR +#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI /* watchdog detector functions */ static int is_hardlockup(void) { @@ -192,6 +197,76 @@ static int is_hardlockup(void) } #endif +#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU +static unsigned int watchdog_next_cpu(unsigned int cpu) +{ + cpumask_t cpus = watchdog_cpus; + unsigned int next_cpu; + + next_cpu = cpumask_next(cpu, &cpus); + if (next_cpu >= nr_cpu_ids) + next_cpu = cpumask_first(&cpus); + + if (next_cpu == cpu) + return nr_cpu_ids; + + return next_cpu; +} + +static int is_hardlockup_other_cpu(unsigned int cpu) +{ + unsigned long hrint = per_cpu(hrtimer_interrupts, cpu); + + if (per_cpu(hrtimer_interrupts_saved, cpu) == hrint) + return 1; + + per_cpu(hrtimer_interrupts_saved, cpu) = hrint; + return 0; +} + +static void watchdog_check_hardlockup_other_cpu(void) +{ + unsigned int next_cpu; + + /* + * Test for hardlockups every 3 samples. The sample period is + * watchdog_thresh * 2 / 5, so 3 samples gets us back to slightly over + * watchdog_thresh (over by 20%). + */ + if (__this_cpu_read(hrtimer_interrupts) % 3 != 0) + return; + + /* check for a hardlockup on the next cpu */ + next_cpu = watchdog_next_cpu(smp_processor_id()); + if (next_cpu >= nr_cpu_ids) + return; + + smp_rmb(); + + if (per_cpu(watchdog_nmi_touch, next_cpu) == true) { + per_cpu(watchdog_nmi_touch, next_cpu) = false; + return; + } + + if (is_hardlockup_other_cpu(next_cpu)) { + /* only warn once */ + if (per_cpu(hard_watchdog_warn, next_cpu) == true) + return; + + if (hardlockup_panic) + panic("Watchdog detected hard LOCKUP on cpu %u", next_cpu); + else + WARN(1, "Watchdog detected hard LOCKUP on cpu %u", next_cpu); + + per_cpu(hard_watchdog_warn, next_cpu) = true; + } else { + per_cpu(hard_watchdog_warn, next_cpu) = false; + } +} +#else +static inline void watchdog_check_hardlockup_other_cpu(void) { return; } +#endif + static int is_softlockup(unsigned long touch_ts) { unsigned long now = get_timestamp(); @@ -203,7 +278,7 @@ static int is_softlockup(unsigned long touch_ts) return 0; } -#ifdef CONFIG_HARDLOCKUP_DETECTOR +#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI static struct perf_event_attr wd_hw_attr = { .type = PERF_TYPE_HARDWARE, @@ -251,7 +326,7 @@ static void watchdog_overflow_callback(struct perf_event *event, __this_cpu_write(hard_watchdog_warn, false); return; } -#endif /* CONFIG_HARDLOCKUP_DETECTOR */ +#endif /* CONFIG_HARDLOCKUP_DETECTOR_NMI */ static void watchdog_interrupt_count(void) { @@ -271,6 +346,9 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) /* kick the hardlockup detector */ watchdog_interrupt_count(); + /* test for hardlockups on the next cpu */ + watchdog_check_hardlockup_other_cpu(); + /* kick the softlockup detector */ wake_up_process(__this_cpu_read(softlockup_watchdog)); @@ -395,7 +473,7 @@ static void watchdog(unsigned int cpu) __touch_watchdog(); } -#ifdef CONFIG_HARDLOCKUP_DETECTOR +#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI /* * People like the simple clean cpu node info on boot. * Reduce the watchdog noise by only printing messages @@ -471,9 +549,44 @@ static void watchdog_nmi_disable(unsigned int cpu) return; } #else +#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU +static int watchdog_nmi_enable(unsigned int cpu) +{ + /* + * The new cpu will be marked online before the first hrtimer interrupt + * runs on it. If another cpu tests for a hardlockup on the new cpu + * before it has run its first hrtimer, it will get a false positive. + * Touch the watchdog on the new cpu to delay the first check for at + * least 3 sampling periods to guarantee one hrtimer has run on the new + * cpu. + */ + per_cpu(watchdog_nmi_touch, cpu) = true; + smp_wmb(); + cpumask_set_cpu(cpu, &watchdog_cpus); + return 0; +} + +static void watchdog_nmi_disable(unsigned int cpu) +{ + unsigned int next_cpu = watchdog_next_cpu(cpu); + + /* + * Offlining this cpu will cause the cpu before this one to start + * checking the one after this one. If this cpu just finished checking + * the next cpu and updating hrtimer_interrupts_saved, and then the + * previous cpu checks it within one sample period, it will trigger a + * false positive. Touch the watchdog on the next cpu to prevent it. + */ + if (next_cpu < nr_cpu_ids) + per_cpu(watchdog_nmi_touch, next_cpu) = true; + smp_wmb(); + cpumask_clear_cpu(cpu, &watchdog_cpus); +} +#else static int watchdog_nmi_enable(unsigned int cpu) { return 0; } static void watchdog_nmi_disable(unsigned int cpu) { return; } -#endif /* CONFIG_HARDLOCKUP_DETECTOR */ +#endif /* CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU */ +#endif /* CONFIG_HARDLOCKUP_DETECTOR_NMI */ static struct smp_hotplug_thread watchdog_threads = { .store = &softlockup_watchdog, diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 5e2e2344b246..68837d7cf65d 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -635,15 +635,27 @@ config LOCKUP_DETECTOR The overhead should be minimal. A periodic hrtimer runs to generate interrupts and kick the watchdog task every 4 seconds. An NMI is generated every 10 seconds or so to check for hardlockups. + If NMIs are not available on the platform, every 12 seconds the + hrtimer interrupt on one cpu will be used to check for hardlockups + on the next cpu. The frequency of hrtimer and NMI events and the soft and hard lockup thresholds can be controlled through the sysctl watchdog_thresh. -config HARDLOCKUP_DETECTOR +config HARDLOCKUP_DETECTOR_NMI def_bool y depends on LOCKUP_DETECTOR && !HAVE_NMI_WATCHDOG depends on PERF_EVENTS && HAVE_PERF_EVENTS_NMI +config HARDLOCKUP_DETECTOR_OTHER_CPU + def_bool y + depends on LOCKUP_DETECTOR && SMP + depends on !HARDLOCKUP_DETECTOR_NMI && !HAVE_NMI_WATCHDOG + +config HARDLOCKUP_DETECTOR + def_bool y + depends on HARDLOCKUP_DETECTOR_NMI || HARDLOCKUP_DETECTOR_OTHER_CPU + config BOOTPARAM_HARDLOCKUP_PANIC bool "Panic (Reboot) On Hard Lockups" depends on HARDLOCKUP_DETECTOR From 43d56514f2b7e64f9c20bce299c54144e96ce8e5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 18 Dec 2012 17:50:10 -0800 Subject: [PATCH 208/358] cpufreq: interactive: add timer slack to limit idle at speed > min Always use deferrable timer for load sampling. Set a non-deferrable timer to an additional slack time to allow prior to waking up from idle to drop speed when not at minimum speed. Slack value -1 avoids wakeups to drop speed. Default is 80ms. Remove the governidle module param and its timer management in idle. For platforms on which holding speed above mimum in idle costs power, use the new timer slack to select how long to wait before waking up to drop speed. Change-Id: I270b3980667e2c70a68e5bff534124b4411dbad5 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 101 ++++++++++++++++---------- 1 file changed, 61 insertions(+), 40 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 8b44a82aa146..690be16aef8a 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -40,7 +40,7 @@ static atomic_t active_count = ATOMIC_INIT(0); struct cpufreq_interactive_cpuinfo { struct timer_list cpu_timer; - int timer_idlecancel; + struct timer_list cpu_slack_timer; spinlock_t load_lock; /* protects the next 4 fields */ u64 time_in_idle; u64 time_in_idle_timestamp; @@ -102,10 +102,12 @@ static int boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; /* End time of boost pulse in ktime converted to usecs */ static u64 boostpulse_endtime; -static bool governidle; -module_param(governidle, bool, S_IWUSR | S_IRUGO); -MODULE_PARM_DESC(governidle, - "Set to 1 to wake up CPUs from idle to reduce speed (default 0)"); +/* + * Max additional time to wait in idle, beyond timer_rate, at speeds above + * minimum before wakeup to reduce speed, or -1 if unnecessary. + */ +#define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE) +static int timer_slack_val = DEFAULT_TIMER_SLACK; static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -123,8 +125,14 @@ struct cpufreq_governor cpufreq_gov_interactive = { static void cpufreq_interactive_timer_resched( struct cpufreq_interactive_cpuinfo *pcpu) { - mod_timer_pinned(&pcpu->cpu_timer, - jiffies + usecs_to_jiffies(timer_rate)); + unsigned long expires = jiffies + usecs_to_jiffies(timer_rate); + + mod_timer_pinned(&pcpu->cpu_timer, expires); + if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { + expires += usecs_to_jiffies(timer_slack_val); + mod_timer_pinned(&pcpu->cpu_slack_timer, expires); + } + spin_lock(&pcpu->load_lock); pcpu->time_in_idle = get_cpu_idle_time_us(smp_processor_id(), @@ -368,17 +376,8 @@ rearm_if_notmax: goto exit; rearm: - if (!timer_pending(&pcpu->cpu_timer)) { - /* - * If governing speed in idle and already at min, cancel the - * timer if that CPU goes idle. We don't need to re-evaluate - * speed until the next idle exit. - */ - if (governidle && pcpu->target_freq == pcpu->policy->min) - pcpu->timer_idlecancel = 1; - + if (!timer_pending(&pcpu->cpu_timer)) cpufreq_interactive_timer_resched(pcpu); - } exit: return; @@ -404,21 +403,8 @@ static void cpufreq_interactive_idle_start(void) * min indefinitely. This should probably be a quirk of * the CPUFreq driver. */ - if (!pending) { - pcpu->timer_idlecancel = 0; + if (!pending) cpufreq_interactive_timer_resched(pcpu); - } - } else if (governidle) { - /* - * If at min speed and entering idle after load has - * already been evaluated, and a timer has been set just in - * case the CPU suddenly goes busy, cancel that timer. The - * CPU didn't go busy; we'll recheck things upon idle exit. - */ - if (pending && pcpu->timer_idlecancel) { - del_timer(&pcpu->cpu_timer); - pcpu->timer_idlecancel = 0; - } } } @@ -433,11 +419,10 @@ static void cpufreq_interactive_idle_end(void) /* Arm the timer for 1-2 ticks later if not already. */ if (!timer_pending(&pcpu->cpu_timer)) { - pcpu->timer_idlecancel = 0; cpufreq_interactive_timer_resched(pcpu); - } else if (!governidle && - time_after_eq(jiffies, pcpu->cpu_timer.expires)) { + } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) { del_timer(&pcpu->cpu_timer); + del_timer(&pcpu->cpu_slack_timer); cpufreq_interactive_timer(smp_processor_id()); } } @@ -747,6 +732,29 @@ static ssize_t store_timer_rate(struct kobject *kobj, static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, show_timer_rate, store_timer_rate); +static ssize_t show_timer_slack( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", timer_slack_val); +} + +static ssize_t store_timer_slack( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + timer_slack_val = val; + return count; +} + +define_one_global_rw(timer_slack); + static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -826,6 +834,7 @@ static struct attribute *interactive_attributes[] = { &above_hispeed_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, + &timer_slack.attr, &boost.attr, &boostpulse.attr, &boostpulse_duration.attr, @@ -876,6 +885,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, hispeed_freq = policy->max; for_each_cpu(j, policy->cpus) { + unsigned long expires; + pcpu = &per_cpu(cpuinfo, j); pcpu->policy = policy; pcpu->target_freq = policy->cur; @@ -887,9 +898,15 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->floor_validate_time; pcpu->governor_enabled = 1; smp_wmb(); - pcpu->cpu_timer.expires = - jiffies + usecs_to_jiffies(timer_rate); + expires = jiffies + usecs_to_jiffies(timer_rate); + pcpu->cpu_timer.expires = expires; add_timer_on(&pcpu->cpu_timer, j); + + if (timer_slack_val >= 0) { + expires += usecs_to_jiffies(timer_slack_val); + pcpu->cpu_slack_timer.expires = expires; + add_timer_on(&pcpu->cpu_slack_timer, j); + } } /* @@ -915,6 +932,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->governor_enabled = 0; smp_wmb(); del_timer_sync(&pcpu->cpu_timer); + del_timer_sync(&pcpu->cpu_slack_timer); } if (atomic_dec_return(&active_count) > 0) @@ -940,6 +958,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, return 0; } +static void cpufreq_interactive_nop_timer(unsigned long data) +{ +} + static int __init cpufreq_interactive_init(void) { unsigned int i; @@ -954,12 +976,11 @@ static int __init cpufreq_interactive_init(void) /* Initalize per-cpu timers */ for_each_possible_cpu(i) { pcpu = &per_cpu(cpuinfo, i); - if (governidle) - init_timer(&pcpu->cpu_timer); - else - init_timer_deferrable(&pcpu->cpu_timer); + init_timer_deferrable(&pcpu->cpu_timer); pcpu->cpu_timer.function = cpufreq_interactive_timer; pcpu->cpu_timer.data = i; + init_timer(&pcpu->cpu_slack_timer); + pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer; spin_lock_init(&pcpu->load_lock); } From effad965ead94ebbc98498eedec6af7a83cf6b6c Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 19 Dec 2012 16:06:48 -0800 Subject: [PATCH 209/358] cpufreq: interactive: fix boosting logic 35a84de cpufreq: interactive: apply above_hispeed_delay to each step above hispeed caused the speed choice logic to osciallate between boosting and not boosting. Add back code to ensure speed does not drop below boost frequency while boosting. Change-Id: Id420068480fcc7f5c4989ff523e2a8d22e2f4db2 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 690be16aef8a..f8e9ee9f7137 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -298,11 +298,18 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = loadadjfreq / pcpu->target_freq; boosted = boost_val || now < boostpulse_endtime; - if ((cpu_load >= go_hispeed_load || boosted) && - pcpu->target_freq < hispeed_freq) - new_freq = hispeed_freq; - else + if (cpu_load >= go_hispeed_load || boosted) { + if (pcpu->target_freq < hispeed_freq) { + new_freq = hispeed_freq; + } else { + new_freq = choose_freq(pcpu, loadadjfreq); + + if (new_freq < hispeed_freq) + new_freq = hispeed_freq; + } + } else { new_freq = choose_freq(pcpu, loadadjfreq); + } if (pcpu->target_freq >= hispeed_freq && new_freq > pcpu->target_freq && From 24d67a5821777ffbc528d6b49b15402d0f3482c1 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 18 Dec 2012 17:50:44 -0800 Subject: [PATCH 210/358] cpufreq: interactive: fix racy timer stopping When stopping the governor, del_timer_sync() can race against an invocation of the idle notifier callback, which has the potential to reactivate the timer. To fix this issue, a read-write semaphore is used. Multiple readers are allowed as long as pcpu->governor_enabled is true. However it can be moved to false only after taking a write semaphore which would wait for any on-going asynchronous activities to complete and prevent any more of those activities to be initiated. [toddpoynor@google.com: cosmetic and commit text changes] Change-Id: Ib51165a735d73dcf964a06754c48bdc1913e13d0 Signed-off-by: Nicolas Pitre --- drivers/cpufreq/cpufreq_interactive.c | 38 ++++++++++++++++++++------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index f8e9ee9f7137..74f56093d2f3 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -29,7 +29,6 @@ #include #include #include -#include #include #include @@ -52,6 +51,7 @@ struct cpufreq_interactive_cpuinfo { unsigned int floor_freq; u64 floor_validate_time; u64 hispeed_validate_time; + struct rw_semaphore enable_sem; int governor_enabled; }; @@ -279,8 +279,8 @@ static void cpufreq_interactive_timer(unsigned long data) unsigned long flags; bool boosted; - smp_rmb(); - + if (!down_read_trylock(&pcpu->enable_sem)) + return; if (!pcpu->governor_enabled) goto exit; @@ -387,6 +387,7 @@ rearm: cpufreq_interactive_timer_resched(pcpu); exit: + up_read(&pcpu->enable_sem); return; } @@ -396,8 +397,12 @@ static void cpufreq_interactive_idle_start(void) &per_cpu(cpuinfo, smp_processor_id()); int pending; - if (!pcpu->governor_enabled) + if (!down_read_trylock(&pcpu->enable_sem)) return; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + return; + } pending = timer_pending(&pcpu->cpu_timer); @@ -414,6 +419,7 @@ static void cpufreq_interactive_idle_start(void) cpufreq_interactive_timer_resched(pcpu); } + up_read(&pcpu->enable_sem); } static void cpufreq_interactive_idle_end(void) @@ -421,8 +427,12 @@ static void cpufreq_interactive_idle_end(void) struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, smp_processor_id()); - if (!pcpu->governor_enabled) + if (!down_read_trylock(&pcpu->enable_sem)) return; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + return; + } /* Arm the timer for 1-2 ticks later if not already. */ if (!timer_pending(&pcpu->cpu_timer)) { @@ -432,6 +442,8 @@ static void cpufreq_interactive_idle_end(void) del_timer(&pcpu->cpu_slack_timer); cpufreq_interactive_timer(smp_processor_id()); } + + up_read(&pcpu->enable_sem); } static int cpufreq_interactive_speedchange_task(void *data) @@ -466,10 +478,12 @@ static int cpufreq_interactive_speedchange_task(void *data) unsigned int max_freq = 0; pcpu = &per_cpu(cpuinfo, cpu); - smp_rmb(); - - if (!pcpu->governor_enabled) + if (!down_read_trylock(&pcpu->enable_sem)) continue; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + continue; + } for_each_cpu(j, pcpu->policy->cpus) { struct cpufreq_interactive_cpuinfo *pjcpu = @@ -486,6 +500,8 @@ static int cpufreq_interactive_speedchange_task(void *data) trace_cpufreq_interactive_setspeed(cpu, pcpu->target_freq, pcpu->policy->cur); + + up_read(&pcpu->enable_sem); } } @@ -936,10 +952,11 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, case CPUFREQ_GOV_STOP: for_each_cpu(j, policy->cpus) { pcpu = &per_cpu(cpuinfo, j); + down_write(&pcpu->enable_sem); pcpu->governor_enabled = 0; - smp_wmb(); del_timer_sync(&pcpu->cpu_timer); del_timer_sync(&pcpu->cpu_slack_timer); + up_write(&pcpu->enable_sem); } if (atomic_dec_return(&active_count) > 0) @@ -989,6 +1006,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); + init_rwsem(&pcpu->enable_sem); } spin_lock_init(&target_loads_lock); From 258432fdd61e1664752affa0352a643ac2c1e572 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 20 Dec 2012 15:51:00 -0800 Subject: [PATCH 211/358] cpufreq: interactive: fix race on timer restart on governor start Starting the governor, or restarting on a hotplugged-in CPU, can race with the timer start in idle, triggering a BUG on timer already pending. Start the timer before setting the enable flag, and use enable_sem to protect the sequence (and ensure correct order of the update to the enable flag). Delete any existing timer for safety. Change-Id: Ife77cf9fe099e8fd8543224cbf148c6722c2ffb0 Reported-by: Francisco Franco Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 74f56093d2f3..d22d162c9f27 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -919,17 +919,17 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, ktime_to_us(ktime_get()); pcpu->hispeed_validate_time = pcpu->floor_validate_time; - pcpu->governor_enabled = 1; - smp_wmb(); + down_write(&pcpu->enable_sem); expires = jiffies + usecs_to_jiffies(timer_rate); pcpu->cpu_timer.expires = expires; add_timer_on(&pcpu->cpu_timer, j); - if (timer_slack_val >= 0) { expires += usecs_to_jiffies(timer_slack_val); pcpu->cpu_slack_timer.expires = expires; add_timer_on(&pcpu->cpu_slack_timer, j); } + pcpu->governor_enabled = 1; + up_write(&pcpu->enable_sem); } /* From a592898b3cc5533080ac61790e67e646bb76b91d Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 21 Dec 2012 15:13:01 -0800 Subject: [PATCH 212/358] cpufreq: interactive: default go_hispeed_load 99%, doc updates Update default go_hispeed_load from 85% to 99%. Recent changes to the governor now use a default target_load of 90%. go_hispeed_load should not be lower than the target load for hispeed_freq, which could lead to oscillating speed decisions. Other recent changes reduce the need to dampen speed jumps on load spikes, while input event boosts from userspace are the preferred method for anticipating load spikes with UI impacts. General update to the documentation to reflect recent changes. Change-Id: I1b92f3091f42c04b10503cd1169a943b5dfd6faf Signed-off-by: Todd Poynor --- Documentation/cpu-freq/governors.txt | 62 +++++++++++++-------------- drivers/cpufreq/cpufreq_interactive.c | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index d44901eedcf8..3732ec162873 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -224,23 +224,8 @@ sampling rate. The CPUfreq governor "interactive" is designed for latency-sensitive, interactive workloads. This governor sets the CPU speed depending on -usage, similar to "ondemand" and "conservative" governors. However, -the governor is more aggressive about scaling the CPU speed up in -response to CPU-intensive activity. - -Sampling the CPU load every X ms can lead to under-powering the CPU -for X ms, leading to dropped frames, stuttering UI, etc. Instead of -sampling the cpu at a specified rate, the interactive governor will -check whether to scale the cpu frequency up soon after coming out of -idle. When the cpu comes out of idle, a timer is configured to fire -within 1-2 ticks. If the cpu is very busy between exiting idle and -when the timer fires then we assume the cpu is underpowered and ramp -to MAX speed. - -If the cpu was not sufficiently busy to immediately ramp to MAX speed, -then governor evaluates the cpu load since the last speed adjustment, -choosing the highest value between that longer-term load or the -short-term load since idle exit to determine the cpu speed to ramp to. +usage, similar to "ondemand" and "conservative" governors, but with a +different set of configurable behaviors. The tuneable values for this governor are: @@ -262,36 +247,51 @@ values also usually appear in an ascending order. The default is target load 90% for all speeds. min_sample_time: The minimum amount of time to spend at the current -frequency before ramping down. This is to ensure that the governor has -seen enough historic cpu load data to determine the appropriate -workload. Default is 80000 uS. +frequency before ramping down. Default is 80000 uS. hispeed_freq: An intermediate "hi speed" at which to initially ramp when CPU load hits the value specified in go_hispeed_load. If load stays high for the amount of time specified in above_hispeed_delay, -then speed may be bumped higher. Default is maximum speed. +then speed may be bumped higher. Default is the maximum speed +allowed by the policy at governor initialization time. -go_hispeed_load: The CPU load at which to ramp to the intermediate "hi -speed". Default is 85%. +go_hispeed_load: The CPU load at which to ramp to hispeed_freq. +Default is 99%. -above_hispeed_delay: Once speed is set to hispeed_freq, wait for this -long before bumping speed higher in response to continued high load. +above_hispeed_delay: When speed is at or above hispeed_freq, wait for +this long before raising speed in response to continued high load. Default is 20000 uS. -timer_rate: Sample rate for reevaluating cpu load when the system is -not idle. Default is 20000 uS. +timer_rate: Sample rate for reevaluating CPU load when the CPU is not +idle. A deferrable timer is used, such that the CPU will not be woken +from idle to service this timer until something else needs to run. +(The maximum time to allow deferring this timer when not running at +minimum speed is configurable via timer_slack.) Default is 20000 uS. -input_boost: If non-zero, boost speed of all CPUs to hispeed_freq on -touchscreen activity. Default is 0. +timer_slack: Maximum additional time to defer handling the governor +sampling timer beyond timer_rate when running at speeds above the +minimum. For platforms that consume additional power at idle when +CPUs are running at speeds greater than minimum, this places an upper +bound on how long the timer will be deferred prior to re-evaluating +load and dropping speed. For example, if timer_rate is 20000uS and +timer_slack is 10000uS then timers will be deferred for up to 30msec +when not at lowest speed. A value of -1 means defer timers +indefinitely at all speeds. Default is 80000 uS. boost: If non-zero, immediately boost speed of all CPUs to at least hispeed_freq until zero is written to this attribute. If zero, allow CPU speeds to drop below hispeed_freq according to load as usual. +Default is zero. -boostpulse: Immediately boost speed of all CPUs to hispeed_freq for -min_sample_time, after which speeds are allowed to drop below +boostpulse: On each write, immediately boost speed of all CPUs to +hispeed_freq for at least the period of time specified by +boostpulse_duration, after which speeds are allowed to drop below hispeed_freq according to load as usual. +boostpulse_duration: Length of time to hold CPU speed at hispeed_freq +on a write to boostpulse, before allowing speed to drop according to +load as usual. Default is 80000 uS. + 3. The Governor Interface in the CPUfreq Core ============================================= diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index d22d162c9f27..fdc2a4798389 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -66,7 +66,7 @@ static spinlock_t speedchange_cpumask_lock; static unsigned int hispeed_freq; /* Go to hi speed when CPU load at or above this value. */ -#define DEFAULT_GO_HISPEED_LOAD 85 +#define DEFAULT_GO_HISPEED_LOAD 99 static unsigned long go_hispeed_load; /* Target load. Lower values result in higher CPU speeds. */ From 2c7cba908160ae39fa7af923478baf2049b1f744 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 21 Dec 2012 15:32:21 -0800 Subject: [PATCH 213/358] cpufreq: interactive: init default values at compile time Change-Id: Ia4966e949a6c24c34fdbd4a6e522cd7c37e4108e Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index fdc2a4798389..c8358a3b5454 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -67,7 +67,7 @@ static unsigned int hispeed_freq; /* Go to hi speed when CPU load at or above this value. */ #define DEFAULT_GO_HISPEED_LOAD 99 -static unsigned long go_hispeed_load; +static unsigned long go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; /* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 @@ -80,20 +80,20 @@ static int ntarget_loads = ARRAY_SIZE(default_target_loads); * The minimum amount of time to spend at a frequency before we can ramp down. */ #define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) -static unsigned long min_sample_time; +static unsigned long min_sample_time = DEFAULT_MIN_SAMPLE_TIME; /* * The sample rate of the timer used to increase frequency */ #define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) -static unsigned long timer_rate; +static unsigned long timer_rate = DEFAULT_TIMER_RATE; /* * Wait this long before raising speed above hispeed, by default a single * timer interval. */ #define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE -static unsigned long above_hispeed_delay_val; +static unsigned long above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY; /* Non-zero means indefinite speed boost active */ static int boost_val; @@ -992,11 +992,6 @@ static int __init cpufreq_interactive_init(void) struct cpufreq_interactive_cpuinfo *pcpu; struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; - min_sample_time = DEFAULT_MIN_SAMPLE_TIME; - above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY; - timer_rate = DEFAULT_TIMER_RATE; - /* Initalize per-cpu timers */ for_each_possible_cpu(i) { pcpu = &per_cpu(cpuinfo, i); From ffb3fed744f3b17c48fb3f1c217e7dc980b813e5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Sun, 23 Dec 2012 12:28:49 -0800 Subject: [PATCH 214/358] cpufreq: interactive: don't handle transition notification if not enabled If multiple governors are in use then avoid processing frequency transition notifications for CPUs on which the interactive governor is not enabled. Change-Id: Ibd75255b921d887501a64774a8c4f62302f2d4e4 Reported-by: Francisco Franco Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index c8358a3b5454..51c34bf9c173 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -552,6 +552,12 @@ static int cpufreq_interactive_notifier( if (val == CPUFREQ_POSTCHANGE) { pcpu = &per_cpu(cpuinfo, freq->cpu); + if (!down_read_trylock(&pcpu->enable_sem)) + return 0; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + return 0; + } for_each_cpu(cpu, pcpu->policy->cpus) { struct cpufreq_interactive_cpuinfo *pjcpu = @@ -560,8 +566,9 @@ static int cpufreq_interactive_notifier( update_load(cpu); spin_unlock(&pjcpu->load_lock); } - } + up_read(&pcpu->enable_sem); + } return 0; } From 6f56aae0167b5da7b080edbad36c25114f635eda Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 2 Jan 2013 13:14:00 -0800 Subject: [PATCH 215/358] cpufreq: interactive: fix deadlock on spinlock in timer Need to use irqsave/restore spinlock calls to avoid a deadlock in calls from the timer. Change-Id: I15b6b590045ba1447e34ca7b5ff342723e53a605 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 29 ++++++++++++++++----------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 51c34bf9c173..e7f26aae186b 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -126,6 +126,7 @@ static void cpufreq_interactive_timer_resched( struct cpufreq_interactive_cpuinfo *pcpu) { unsigned long expires = jiffies + usecs_to_jiffies(timer_rate); + unsigned long flags; mod_timer_pinned(&pcpu->cpu_timer, expires); if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { @@ -133,27 +134,28 @@ static void cpufreq_interactive_timer_resched( mod_timer_pinned(&pcpu->cpu_slack_timer, expires); } - spin_lock(&pcpu->load_lock); + spin_lock_irqsave(&pcpu->load_lock, flags); pcpu->time_in_idle = get_cpu_idle_time_us(smp_processor_id(), &pcpu->time_in_idle_timestamp); pcpu->cputime_speedadj = 0; pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; - spin_unlock(&pcpu->load_lock); + spin_unlock_irqrestore(&pcpu->load_lock, flags); } static unsigned int freq_to_targetload(unsigned int freq) { int i; unsigned int ret; + unsigned long flags; - spin_lock(&target_loads_lock); + spin_lock_irqsave(&target_loads_lock, flags); for (i = 0; i < ntarget_loads - 1 && freq >= target_loads[i+1]; i += 2) ; ret = target_loads[i]; - spin_unlock(&target_loads_lock); + spin_unlock_irqrestore(&target_loads_lock, flags); return ret; } @@ -284,11 +286,11 @@ static void cpufreq_interactive_timer(unsigned long data) if (!pcpu->governor_enabled) goto exit; - spin_lock(&pcpu->load_lock); + spin_lock_irqsave(&pcpu->load_lock, flags); now = update_load(data); delta_time = (unsigned int)(now - pcpu->cputime_speedadj_timestamp); cputime_speedadj = pcpu->cputime_speedadj; - spin_unlock(&pcpu->load_lock); + spin_unlock_irqrestore(&pcpu->load_lock, flags); if (WARN_ON_ONCE(!delta_time)) goto rearm; @@ -549,6 +551,7 @@ static int cpufreq_interactive_notifier( struct cpufreq_freqs *freq = data; struct cpufreq_interactive_cpuinfo *pcpu; int cpu; + unsigned long flags; if (val == CPUFREQ_POSTCHANGE) { pcpu = &per_cpu(cpuinfo, freq->cpu); @@ -562,9 +565,9 @@ static int cpufreq_interactive_notifier( for_each_cpu(cpu, pcpu->policy->cpus) { struct cpufreq_interactive_cpuinfo *pjcpu = &per_cpu(cpuinfo, cpu); - spin_lock(&pjcpu->load_lock); + spin_lock_irqsave(&pjcpu->load_lock, flags); update_load(cpu); - spin_unlock(&pjcpu->load_lock); + spin_unlock_irqrestore(&pjcpu->load_lock, flags); } up_read(&pcpu->enable_sem); @@ -581,15 +584,16 @@ static ssize_t show_target_loads( { int i; ssize_t ret = 0; + unsigned long flags; - spin_lock(&target_loads_lock); + spin_lock_irqsave(&target_loads_lock, flags); for (i = 0; i < ntarget_loads; i++) ret += sprintf(buf + ret, "%u%s", target_loads[i], i & 0x1 ? ":" : " "); ret += sprintf(buf + ret, "\n"); - spin_unlock(&target_loads_lock); + spin_unlock_irqrestore(&target_loads_lock, flags); return ret; } @@ -602,6 +606,7 @@ static ssize_t store_target_loads( unsigned int *new_target_loads = NULL; int ntokens = 1; int i; + unsigned long flags; cp = buf; while ((cp = strpbrk(cp + 1, " :"))) @@ -631,12 +636,12 @@ static ssize_t store_target_loads( if (i != ntokens) goto err_inval; - spin_lock(&target_loads_lock); + spin_lock_irqsave(&target_loads_lock, flags); if (target_loads != default_target_loads) kfree(target_loads); target_loads = new_target_loads; ntarget_loads = ntokens; - spin_unlock(&target_loads_lock); + spin_unlock_irqrestore(&target_loads_lock, flags); return count; err_inval: From 5ad9b6a099b5c7e4084bdbe4fa21ca825918e13e Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 4 Jan 2013 18:18:36 -0800 Subject: [PATCH 216/358] netfilter: xt_qtaguid: remove AID_* dependency for access control qtaguid limits what can be done with /ctrl and /stats based on group membership. This changes removes AID_NET_BW_STATS and AID_NET_BW_ACCT, and picks up the groups from the gid of the matching proc entry files. Signed-off-by: JP Abgrall Change-Id: I42e477adde78a12ed5eb58fbc0b277cdaadb6f94 --- net/netfilter/xt_qtaguid.c | 51 +++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 603bdd206990..923f1bdd02e8 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -53,25 +53,22 @@ static unsigned int proc_stats_perms = S_IRUGO; module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); static struct proc_dir_entry *xt_qtaguid_ctrl_file; -#ifdef CONFIG_ANDROID_PARANOID_NETWORK + +/* Everybody can write. But proc_ctrl_write_limited is true by default which + * limits what can be controlled. See the can_*() functions. + */ static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; -#else -static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUSR; -#endif module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -#include -static gid_t proc_stats_readall_gid = AID_NET_BW_STATS; -static gid_t proc_ctrl_write_gid = AID_NET_BW_ACCT; -#else -/* 0 means, don't limit anybody */ -static gid_t proc_stats_readall_gid; -static gid_t proc_ctrl_write_gid; -#endif -module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, +/* Limited by default, so the gid of the ctrl and stats proc entries + * will limit what can be done. See the can_*() functions. + */ +static bool proc_stats_readall_limited = true; +static bool proc_ctrl_write_limited = true; + +module_param_named(stats_readall_limited, proc_stats_readall_limited, bool, S_IRUGO | S_IWUSR); -module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, +module_param_named(ctrl_write_limited, proc_ctrl_write_limited, bool, S_IRUGO | S_IWUSR); /* @@ -242,8 +239,9 @@ static struct qtaguid_event_counts qtu_events; static bool can_manipulate_uids(void) { /* root pwnd */ - return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) - || in_egroup_p(proc_ctrl_write_gid); + return in_egroup_p(xt_qtaguid_ctrl_file->gid) + || unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_limited) + || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); } static bool can_impersonate_uid(uid_t uid) @@ -254,9 +252,10 @@ static bool can_impersonate_uid(uid_t uid) static bool can_read_other_uid_stats(uid_t uid) { /* root pwnd */ - return unlikely(!current_fsuid()) || uid == current_fsuid() - || unlikely(!proc_stats_readall_gid) - || in_egroup_p(proc_stats_readall_gid); + return in_egroup_p(xt_qtaguid_stats_file->gid) + || unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_limited) + || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); } static inline void dc_add_byte_packets(struct data_counters *counters, int set, @@ -2302,11 +2301,12 @@ static int ctrl_cmd_tag(const char *input) } CT_DEBUG("qtaguid: ctrl_tag(%s): " "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " - "in_group=%d in_egroup=%d\n", + "ctrl.gid=%u in_group()=%d in_egroup()=%d\n", input, current->pid, current->tgid, current_uid(), current_euid(), current_fsuid(), - in_group_p(proc_ctrl_write_gid), - in_egroup_p(proc_ctrl_write_gid)); + xt_qtaguid_ctrl_file->gid, + in_group_p(xt_qtaguid_ctrl_file->gid), + in_egroup_p(xt_qtaguid_ctrl_file->gid)); if (argc < 4) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { @@ -2598,10 +2598,11 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) && !can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " - "from pid=%u tgid=%u uid=%u\n", + "from pid=%u tgid=%u uid=%u stats.gid=%u\n", ppi->iface_entry->ifname, get_atag_from_tag(tag), stat_uid, - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, current_fsuid(), + xt_qtaguid_stats_file->gid); return 0; } if (ppi->item_index++ < ppi->items_to_skip) From 1d2bf4fdb0b17219a1dd4844f843b15429baaa08 Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Mon, 7 Jan 2013 14:15:51 +0800 Subject: [PATCH 217/358] cpufreq: interactive: fix race on governor start/stop There is race condition when both two cpu do CPUFREQ_GOV_STOP and one cpu do CPUFREQ_GOV_START soon. The sysfs_remove_group is not done yet on one cpu, but sysfs_create_group is called on another cpu, which cause governor start failed and then kernel panic in timer callback because the policy and cpu mask are all kfree in cpufreq driver. Replace atomic with mutex to lock the whole START/STOP sequence. Change-Id: I3762b3d44315ae021b8275aca84f5ea9147cc540 Signed-off-by: Lianwei Wang --- drivers/cpufreq/cpufreq_interactive.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index e7f26aae186b..3447e58831d1 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -35,7 +35,7 @@ #define CREATE_TRACE_POINTS #include -static atomic_t active_count = ATOMIC_INIT(0); +static int active_count; struct cpufreq_interactive_cpuinfo { struct timer_list cpu_timer; @@ -61,6 +61,7 @@ static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); static struct task_struct *speedchange_task; static cpumask_t speedchange_cpumask; static spinlock_t speedchange_cpumask_lock; +static struct mutex gov_lock; /* Hi speed to bump to from lo speed when load burst (default max) */ static unsigned int hispeed_freq; @@ -914,6 +915,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (!cpu_online(policy->cpu)) return -EINVAL; + mutex_lock(&gov_lock); + freq_table = cpufreq_frequency_get_table(policy->cpu); if (!hispeed_freq) @@ -948,20 +951,26 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, * Do not register the idle hook and create sysfs * entries if we have already done so. */ - if (atomic_inc_return(&active_count) > 1) + if (++active_count > 1) { + mutex_unlock(&gov_lock); return 0; + } rc = sysfs_create_group(cpufreq_global_kobject, &interactive_attr_group); - if (rc) + if (rc) { + mutex_unlock(&gov_lock); return rc; + } idle_notifier_register(&cpufreq_interactive_idle_nb); cpufreq_register_notifier( &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + mutex_unlock(&gov_lock); break; case CPUFREQ_GOV_STOP: + mutex_lock(&gov_lock); for_each_cpu(j, policy->cpus) { pcpu = &per_cpu(cpuinfo, j); down_write(&pcpu->enable_sem); @@ -971,14 +980,17 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, up_write(&pcpu->enable_sem); } - if (atomic_dec_return(&active_count) > 0) + if (--active_count > 0) { + mutex_unlock(&gov_lock); return 0; + } cpufreq_unregister_notifier( &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); idle_notifier_unregister(&cpufreq_interactive_idle_nb); sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); + mutex_unlock(&gov_lock); break; @@ -1018,6 +1030,7 @@ static int __init cpufreq_interactive_init(void) spin_lock_init(&target_loads_lock); spin_lock_init(&speedchange_cpumask_lock); + mutex_init(&gov_lock); speedchange_task = kthread_create(cpufreq_interactive_speedchange_task, NULL, "cfinteractive"); From 01625c39ca0304f3d57ccbd0a531471d79f1741c Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 28 Jan 2013 16:50:44 -0800 Subject: [PATCH 218/358] netfilter: xt_qtaguid: extend iface stat to report protocols In the past the iface_stat_fmt would only show global bytes/packets for the skb-based numbers. For stall detection in userspace, distinguishing tcp vs other protocols makes it easier. Now we report ifname total_skb_rx_bytes total_skb_rx_packets total_skb_tx_bytes total_skb_tx_packets {rx,tx}_{tcp,udp,ohter}_{bytes,packets} Bug: 6818637 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 90 +++++++++++++++++------------ net/netfilter/xt_qtaguid_internal.h | 21 ++++++- net/netfilter/xt_qtaguid_print.c | 14 +++-- 3 files changed, 82 insertions(+), 43 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 923f1bdd02e8..92e5f80bd8fa 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -268,24 +268,6 @@ static inline void dc_add_byte_packets(struct data_counters *counters, int set, counters->bpc[set][direction][ifs_proto].packets += packets; } -static inline uint64_t dc_sum_bytes(struct data_counters *counters, - int set, - enum ifs_tx_rx direction) -{ - return counters->bpc[set][direction][IFS_TCP].bytes - + counters->bpc[set][direction][IFS_UDP].bytes - + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; -} - -static inline uint64_t dc_sum_packets(struct data_counters *counters, - int set, - enum ifs_tx_rx direction) -{ - return counters->bpc[set][direction][IFS_TCP].packets - + counters->bpc[set][direction][IFS_UDP].packets - + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; -} - static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) { struct rb_node *node = root->rb_node; @@ -787,6 +769,53 @@ done: return iface_entry; } +/* This is for fmt2 only */ +static int pp_iface_stat_line(bool header, char *outp, + int char_count, struct iface_stat *iface_entry) +{ + int len; + if (header) { + len = snprintf(outp, char_count, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n" + ); + } else { + struct data_counters *cnts; + int cnt_set = 0; /* We only use one set for the device */ + cnts = &iface_entry->totals_via_skb; + len = snprintf( + outp, char_count, + "%s " + "%llu %llu %llu %llu %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + } + return len; +} + static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, off_t items_to_skip, int char_count, int *eof, void *data) @@ -816,11 +845,7 @@ static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, return 0; if (fmt == 2 && item_index++ >= items_to_skip) { - len = snprintf(outp, char_count, - "ifname " - "total_skb_rx_bytes total_skb_rx_packets " - "total_skb_tx_bytes total_skb_tx_packets\n" - ); + len = pp_iface_stat_line(true, outp, char_count, NULL); if (len >= char_count) { *outp = '\0'; return outp - page; @@ -865,16 +890,8 @@ static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, stats->tx_bytes, stats->tx_packets ); } else { - len = snprintf( - outp, char_count, - "%s " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->totals_via_skb[IFS_RX].bytes, - iface_entry->totals_via_skb[IFS_RX].packets, - iface_entry->totals_via_skb[IFS_TX].bytes, - iface_entry->totals_via_skb[IFS_TX].packets - ); + len = pp_iface_stat_line(false, outp, char_count, + iface_entry); } if (len >= char_count) { spin_unlock_bh(&iface_stat_list_lock); @@ -1304,6 +1321,7 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, const struct net_device *el_dev; enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; int bytes = skb->len; + int proto; if (!skb->dev) { MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); @@ -1329,7 +1347,7 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, par->hooknum, __func__); BUG(); } else { - int proto = ipx_proto(skb, par); + proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", par->hooknum, el_dev->name, el_dev->type, par->family, proto); @@ -1347,8 +1365,8 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, el_dev->name, entry); - entry->totals_via_skb[direction].bytes += bytes; - entry->totals_via_skb[direction].packets++; + data_counters_update(&entry->totals_via_skb, 0, direction, proto, + bytes); spin_unlock_bh(&iface_stat_list_lock); } diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index d79f8383abf4..6dc14a9c6889 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -179,6 +179,25 @@ struct data_counters { struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; }; +static inline uint64_t dc_sum_bytes(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].bytes + + counters->bpc[set][direction][IFS_UDP].bytes + + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; +} + +static inline uint64_t dc_sum_packets(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].packets + + counters->bpc[set][direction][IFS_UDP].packets + + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; +} + + /* Generic X based nodes used as a base for rb_tree ops */ struct tag_node { struct rb_node node; @@ -203,7 +222,7 @@ struct iface_stat { struct net_device *net_dev; struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; - struct byte_packet_counters totals_via_skb[IFS_MAX_DIRECTIONS]; + struct data_counters totals_via_skb; /* * We keep the last_known, because some devices reset their counters * just before NETDEV_UP, while some will reset just before diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 8cbd8e42bcc4..f6a00a3520ed 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -177,9 +177,10 @@ char *pp_tag_stat(struct tag_stat *ts) char *pp_iface_stat(struct iface_stat *is) { char *res; - if (!is) + if (!is) { res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); - else + } else { + struct data_counters *cnts = &is->totals_via_skb; res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" "list=list_head{...}, " "ifname=%s, " @@ -206,10 +207,10 @@ char *pp_iface_stat(struct iface_stat *is) is->totals_via_dev[IFS_RX].packets, is->totals_via_dev[IFS_TX].bytes, is->totals_via_dev[IFS_TX].packets, - is->totals_via_skb[IFS_RX].bytes, - is->totals_via_skb[IFS_RX].packets, - is->totals_via_skb[IFS_TX].bytes, - is->totals_via_skb[IFS_TX].packets, + dc_sum_bytes(cnts, 0, IFS_RX), + dc_sum_packets(cnts, 0, IFS_RX), + dc_sum_bytes(cnts, 0, IFS_TX), + dc_sum_packets(cnts, 0, IFS_TX), is->last_known_valid, is->last_known[IFS_RX].bytes, is->last_known[IFS_RX].packets, @@ -218,6 +219,7 @@ char *pp_iface_stat(struct iface_stat *is) is->active, is->net_dev, is->proc_ptr); + } _bug_on_err_or_null(res); return res; } From aae81b17c25e058a3afd9cec3b0e8384fa6846fb Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 6 Feb 2013 17:40:07 -0800 Subject: [PATCH 219/358] netfilter: xt_qtaguid: Allow tracking loopback In the past it would always ignore interfaces with loopback addresses. Now we just treat them like any other. This also helps with writing tests that check for the presence of the qtaguid module. Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 92e5f80bd8fa..992a6e044902 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1108,18 +1108,13 @@ static void iface_stat_create(struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - bool activate = !ipv4_is_loopback(ipaddr); IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", ifname, entry); iface_check_stats_reset_and_adjust(net_dev, entry); - _iface_stat_set_active(entry, net_dev, activate); + _iface_stat_set_active(entry, net_dev, true); IF_DEBUG("qtaguid: %s(%s): " "tracking now %d on ip=%pI4\n", __func__, - entry->ifname, activate, &ipaddr); - goto done_unlock_put; - } else if (ipv4_is_loopback(ipaddr)) { - IF_DEBUG("qtaguid: iface_stat: create(%s): " - "ignore loopback dev. ip=%pI4\n", ifname, &ipaddr); + entry->ifname, true, &ipaddr); goto done_unlock_put; } @@ -1170,19 +1165,13 @@ static void iface_stat_create_ipv6(struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - bool activate = !(addr_type & IPV6_ADDR_LOOPBACK); IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ifname, entry); iface_check_stats_reset_and_adjust(net_dev, entry); - _iface_stat_set_active(entry, net_dev, activate); + _iface_stat_set_active(entry, net_dev, true); IF_DEBUG("qtaguid: %s(%s): " "tracking now %d on ip=%pI6c\n", __func__, - entry->ifname, activate, &ifa->addr); - goto done_unlock_put; - } else if (addr_type & IPV6_ADDR_LOOPBACK) { - IF_DEBUG("qtaguid: %s(%s): " - "ignore loopback dev. ip=%pI6c\n", __func__, - ifname, &ifa->addr); + entry->ifname, true, &ifa->addr); goto done_unlock_put; } From 93261306c1001e9e5e689407d4c772870073473a Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 8 Jan 2013 18:46:41 -0800 Subject: [PATCH 220/358] power: android-battery: remove ac and usb supplies Should no longer need to fix up AC/USB online state via these. Change-Id: I48d2ef0fbefee58cb47eafc11d9a44759920df7a Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 76 --------------------------------- 1 file changed, 76 deletions(-) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index dbba090a51e1..6e8b4054c8d7 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -47,8 +47,6 @@ struct android_bat_data { struct device *dev; struct power_supply psy_bat; - struct power_supply psy_usb; - struct power_supply psy_ac; struct wake_lock monitor_wake_lock; struct wake_lock charger_wake_lock; @@ -74,10 +72,6 @@ struct android_bat_data { struct dentry *debugfs_entry; }; -static char *supply_list[] = { - "android-battery", -}; - static enum power_supply_property android_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, @@ -90,10 +84,6 @@ static enum power_supply_property android_battery_props[] = { POWER_SUPPLY_PROP_CURRENT_NOW, }; -static enum power_supply_property android_power_props[] = { - POWER_SUPPLY_PROP_ONLINE, -}; - static DEFINE_MUTEX(android_bat_state_lock); static void android_bat_update_data(struct android_bat_data *battery); @@ -163,36 +153,6 @@ static int android_bat_get_property(struct power_supply *ps, return 0; } -static int android_usb_get_property(struct power_supply *ps, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct android_bat_data *battery = container_of(ps, - struct android_bat_data, psy_usb); - - if (psp != POWER_SUPPLY_PROP_ONLINE) - return -EINVAL; - - val->intval = (battery->charge_source == CHARGE_SOURCE_USB); - - return 0; -} - -static int android_ac_get_property(struct power_supply *ps, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct android_bat_data *battery = container_of(ps, - struct android_bat_data, psy_ac); - - if (psp != POWER_SUPPLY_PROP_ONLINE) - return -EINVAL; - - val->intval = (battery->charge_source == CHARGE_SOURCE_AC); - - return 0; -} - static void android_bat_get_temp(struct android_bat_data *battery) { int batt_temp = 42; /* 4.2C */ @@ -439,8 +399,6 @@ static void android_bat_charger_work(struct work_struct *work) mutex_unlock(&android_bat_state_lock); wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); - power_supply_changed(&battery->psy_ac); - power_supply_changed(&battery->psy_usb); } @@ -601,22 +559,6 @@ static int android_bat_probe(struct platform_device *pdev) battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props), battery->psy_bat.get_property = android_bat_get_property, - battery->psy_usb.name = "android-usb", - battery->psy_usb.type = POWER_SUPPLY_TYPE_USB, - battery->psy_usb.supplied_to = supply_list, - battery->psy_usb.num_supplicants = ARRAY_SIZE(supply_list), - battery->psy_usb.properties = android_power_props, - battery->psy_usb.num_properties = ARRAY_SIZE(android_power_props), - battery->psy_usb.get_property = android_usb_get_property, - - battery->psy_ac.name = "android-ac", - battery->psy_ac.type = POWER_SUPPLY_TYPE_MAINS, - battery->psy_ac.supplied_to = supply_list, - battery->psy_ac.num_supplicants = ARRAY_SIZE(supply_list), - battery->psy_ac.properties = android_power_props, - battery->psy_ac.num_properties = ARRAY_SIZE(android_power_props), - battery->psy_ac.get_property = android_ac_get_property; - battery->batt_vcell = -1; battery->batt_soc = -1; @@ -632,20 +574,6 @@ static int android_bat_probe(struct platform_device *pdev) goto err_psy_bat_reg; } - ret = power_supply_register(&pdev->dev, &battery->psy_usb); - if (ret) { - dev_err(battery->dev, "%s: failed to register psy_usb\n", - __func__); - goto err_psy_usb_reg; - } - - ret = power_supply_register(&pdev->dev, &battery->psy_ac); - if (ret) { - dev_err(battery->dev, "%s: failed to register psy_ac\n", - __func__); - goto err_psy_ac_reg; - } - battery->monitor_wqueue = alloc_workqueue(dev_name(&pdev->dev), WQ_FREEZABLE, 1); if (!battery->monitor_wqueue) { @@ -686,10 +614,6 @@ static int android_bat_probe(struct platform_device *pdev) return 0; err_wq: - power_supply_unregister(&battery->psy_ac); -err_psy_ac_reg: - power_supply_unregister(&battery->psy_usb); -err_psy_usb_reg: power_supply_unregister(&battery->psy_bat); err_psy_bat_reg: wake_lock_destroy(&battery->monitor_wake_lock); From 9eb0ba853a185a4063e411e5e8a1b566986ed244 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 12 Feb 2013 20:47:48 -0800 Subject: [PATCH 221/358] power: android-battery: push uevent whenever charge source changes Ensure userspace reads an up-to-date value for charging status whenever the charge source is updated. Avoid races where stale charging state may be reflected in userspace until the next battery state poll. Change-Id: Ia744db48584e9e9abf442710c279af9e3a25c079 Signed-off-by: Todd Poynor --- drivers/power/android_battery.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index 6e8b4054c8d7..8d45ff0f3677 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -399,6 +399,7 @@ static void android_bat_charger_work(struct work_struct *work) mutex_unlock(&android_bat_state_lock); wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); + power_supply_changed(&battery->psy_bat); } From 271a2521a14a7c775e6810af916c1018f188598d Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Thu, 21 Feb 2013 17:55:28 -0800 Subject: [PATCH 222/358] trace/events: add gpu trace events Change-Id: I0607b9c776acf61cb796b8572cf8cfb8b2dc1377 Signed-off-by: Jamie Gennis --- include/trace/events/gpu.h | 142 +++++++++++++++++++++++++++++++++++++ kernel/trace/Kconfig | 3 + kernel/trace/Makefile | 1 + kernel/trace/gpu-traces.c | 23 ++++++ 4 files changed, 169 insertions(+) create mode 100644 include/trace/events/gpu.h create mode 100644 kernel/trace/gpu-traces.c diff --git a/include/trace/events/gpu.h b/include/trace/events/gpu.h new file mode 100644 index 000000000000..09efa71d66c6 --- /dev/null +++ b/include/trace/events/gpu.h @@ -0,0 +1,142 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM gpu + +#if !defined(_TRACE_GPU_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_GPU_H + +#include +#include + +#define show_secs_from_ns(ns) \ + ({ \ + u64 t = ns + (NSEC_PER_USEC / 2); \ + do_div(t, NSEC_PER_SEC); \ + }) + +#define show_usecs_from_ns(ns) \ + ({ \ + u64 t = ns + (NSEC_PER_USEC / 2) ; \ + u32 rem; \ + do_div(t, NSEC_PER_USEC); \ + rem = do_div(t, USEC_PER_SEC); \ + }) + +/* + * The gpu_sched_switch event indicates that a switch from one GPU context to + * another occurred on one of the GPU hardware blocks. + * + * The gpu_name argument identifies the GPU hardware block. Each independently + * scheduled GPU hardware block should have a different name. This may be used + * in different ways for different GPUs. For example, if a GPU includes + * multiple processing cores it may use names "GPU 0", "GPU 1", etc. If a GPU + * includes a separately scheduled 2D and 3D hardware block, it might use the + * names "2D" and "3D". + * + * The timestamp argument is the timestamp at which the switch occurred on the + * GPU. These timestamps are in units of nanoseconds and must use + * approximately the same time as sched_clock, though they need not come from + * any CPU clock. The timestamps for a single hardware block must be + * monotonically nondecreasing. This means that if a variable compensation + * offset is used to translate from some other clock to the sched_clock, then + * care must be taken when increasing that offset, and doing so may result in + * multiple events with the same timestamp. + * + * The next_ctx_id argument identifies the next context that was running on + * the GPU hardware block. A value of 0 indicates that the hardware block + * will be idle. + * + * The next_prio argument indicates the priority of the next context at the + * time of the event. The exact numeric values may mean different things for + * different GPUs, but they should follow the rule that lower values indicate a + * higher priority. + * + * The next_job_id argument identifies the batch of work that the GPU will be + * working on. This should correspond to a job_id that was previously traced + * as a gpu_job_enqueue event when the batch of work was created. + */ +TRACE_EVENT(gpu_sched_switch, + + TP_PROTO(const char *gpu_name, u64 timestamp, + u32 next_ctx_id, s32 next_prio, u32 next_job_id), + + TP_ARGS(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id), + + TP_STRUCT__entry( + __string( gpu_name, gpu_name ) + __field( u64, timestamp ) + __field( u32, next_ctx_id ) + __field( s32, next_prio ) + __field( u32, next_job_id ) + ), + + TP_fast_assign( + __assign_str(gpu_name, gpu_name); + __entry->timestamp = timestamp; + __entry->next_ctx_id = next_ctx_id; + __entry->next_prio = next_prio; + __entry->next_job_id = next_job_id; + ), + + TP_printk("gpu_name=%s ts=%5llu.%06lu next_ctx_id=%lu next_prio=%ld " + "next_job_id=%lu", + __get_str(gpu_name), + (unsigned long long)show_secs_from_ns(__entry->timestamp), + (unsigned long)show_usecs_from_ns(__entry->timestamp), + (unsigned long)__entry->next_ctx_id, + (long)__entry->next_prio, + (unsigned long)__entry->next_job_id) +); + +/* + * The gpu_job_enqueue event indicates that a batch of work has been queued up + * to be processed by the GPU. This event is not intended to indicate that + * the batch of work has been submitted to the GPU hardware, but rather that + * it has been submitted to the GPU kernel driver. + * + * This event should be traced on the thread that initiated the work being + * queued. For example, if a batch of work is submitted to the kernel by a + * userland thread, the event should be traced on that thread. + * + * The ctx_id field identifies the GPU context in which the batch of work + * being queued is to be run. + * + * The job_id field identifies the batch of work being queued within the given + * GPU context. The first batch of work submitted for a given GPU context + * should have a job_id of 0, and each subsequent batch of work should + * increment the job_id by 1. + * + * The type field identifies the type of the job being enqueued. The job + * types may be different for different GPU hardware. For example, a GPU may + * differentiate between "2D", "3D", and "compute" jobs. + */ +TRACE_EVENT(gpu_job_enqueue, + + TP_PROTO(u32 ctx_id, u32 job_id, const char *type), + + TP_ARGS(ctx_id, job_id, type), + + TP_STRUCT__entry( + __field( u32, ctx_id ) + __field( u32, job_id ) + __string( type, type ) + ), + + TP_fast_assign( + __entry->ctx_id = ctx_id; + __entry->job_id = job_id; + __assign_str(type, type); + ), + + TP_printk("ctx_id=%lu job_id=%lu type=%s", + (unsigned long)__entry->ctx_id, + (unsigned long)__entry->job_id, + __get_str(type)) +); + +#undef show_secs_from_ns +#undef show_usecs_from_ns + +#endif /* _TRACE_GPU_H */ + +/* This part must be outside protection */ +#include diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 015f85aaca08..e24c188cbbcc 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -82,6 +82,9 @@ config EVENT_TRACING select CONTEXT_SWITCH_TRACER bool +config GPU_TRACEPOINTS + bool + config CONTEXT_SWITCH_TRACER bool diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 1378e84fbe39..30013ca8ba3d 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -61,5 +61,6 @@ obj-$(CONFIG_KGDB_KDB) += trace_kdb.o endif obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o obj-$(CONFIG_UPROBE_EVENT) += trace_uprobe.o +obj-$(CONFIG_GPU_TRACEPOINTS) += gpu-traces.o libftrace-y := ftrace.o diff --git a/kernel/trace/gpu-traces.c b/kernel/trace/gpu-traces.c new file mode 100644 index 000000000000..a4b3f00faee3 --- /dev/null +++ b/kernel/trace/gpu-traces.c @@ -0,0 +1,23 @@ +/* + * GPU tracepoints + * + * Copyright (C) 2013 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 + +#define CREATE_TRACE_POINTS +#include + +EXPORT_TRACEPOINT_SYMBOL(gpu_sched_switch); +EXPORT_TRACEPOINT_SYMBOL(gpu_job_enqueue); From 281322fe70ce8276db3e65a06f0a9f5e9d3a667c Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 5 Mar 2013 14:25:36 -0800 Subject: [PATCH 223/358] input: misc: keychord: log when keychord triggered log keychord id at info level just before waking up processes. Signed-off-by: JP Abgrall --- drivers/input/misc/keychord.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c index 3ffab6da411b..a5ea27ad0e16 100644 --- a/drivers/input/misc/keychord.c +++ b/drivers/input/misc/keychord.c @@ -126,8 +126,12 @@ static void keychord_event(struct input_handle *handle, unsigned int type, done: spin_unlock_irqrestore(&kdev->lock, flags); - if (got_chord) + if (got_chord) { + pr_info("keychord: got keychord id %d. Any tasks: %d\n", + keychord->id, + !list_empty_careful(&kdev->waitq.task_list)); wake_up_interruptible(&kdev->waitq); + } } static int keychord_connect(struct input_handler *handler, From 6639e3d91a05bafa2a85c24c211c43fcaa1b17c5 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 5 Nov 2012 08:15:34 -0500 Subject: [PATCH 224/358] Add security hooks to binder and implement the hooks for SELinux. Add security hooks to the binder and implement the hooks for SELinux. The security hooks enable security modules such as SELinux to implement controls over binder IPC. The security hooks include support for controlling what process can become the binder context manager (binder_set_context_mgr), controlling the ability of a process to invoke a binder transaction/IPC to another process (binder_transaction), controlling the ability a process to transfer a binder reference to another process (binder_transfer_binder), and controlling the ability of a process to transfer an open file to another process (binder_transfer_file). This support is used by SE Android, http://selinuxproject.org/page/SEAndroid. Change-Id: I9a64a87825df2e60b9c51400377af4a9cd1c4049 Signed-off-by: Stephen Smalley --- drivers/staging/android/binder.c | 21 +++++++++ include/linux/security.h | 29 +++++++++++++ security/capability.c | 24 +++++++++++ security/security.c | 20 +++++++++ security/selinux/hooks.c | 66 +++++++++++++++++++++++++++++ security/selinux/include/classmap.h | 1 + 6 files changed, 161 insertions(+) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 1432d956769c..0327a6327b8f 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "binder.h" #include "binder_trace.h" @@ -1383,6 +1384,10 @@ static void binder_transaction(struct binder_proc *proc, return_error = BR_DEAD_REPLY; goto err_dead_binder; } + if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) { + return_error = BR_FAILED_REPLY; + goto err_invalid_target_handle; + } if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { struct binder_transaction *tmp; tmp = thread->transaction_stack; @@ -1525,6 +1530,10 @@ static void binder_transaction(struct binder_proc *proc, fp->cookie, node->cookie); goto err_binder_get_ref_for_node_failed; } + if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { + return_error = BR_FAILED_REPLY; + goto err_binder_get_ref_for_node_failed; + } ref = binder_get_ref_for_node(target_proc, node); if (ref == NULL) { return_error = BR_FAILED_REPLY; @@ -1554,6 +1563,10 @@ static void binder_transaction(struct binder_proc *proc, return_error = BR_FAILED_REPLY; goto err_binder_get_ref_failed; } + if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { + return_error = BR_FAILED_REPLY; + goto err_binder_get_ref_failed; + } if (ref->node->proc == target_proc) { if (fp->type == BINDER_TYPE_HANDLE) fp->type = BINDER_TYPE_BINDER; @@ -1610,6 +1623,11 @@ static void binder_transaction(struct binder_proc *proc, return_error = BR_FAILED_REPLY; goto err_fget_failed; } + if (security_binder_transfer_file(proc->tsk, target_proc->tsk, file) < 0) { + fput(file); + return_error = BR_FAILED_REPLY; + goto err_get_unused_fd_failed; + } target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); if (target_fd < 0) { fput(file); @@ -2627,6 +2645,9 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = -EBUSY; goto err; } + ret = security_binder_set_context_mgr(proc->tsk); + if (ret < 0) + goto err; if (uid_valid(binder_context_mgr_uid)) { if (!uid_eq(binder_context_mgr_uid, current->cred->euid)) { pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n", diff --git a/include/linux/security.h b/include/linux/security.h index 5623a7f965b7..6d812a65c6d3 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1431,6 +1431,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) struct security_operations { char name[SECURITY_NAME_MAX + 1]; + int (*binder_set_context_mgr) (struct task_struct *mgr); + int (*binder_transaction) (struct task_struct *from, struct task_struct *to); + int (*binder_transfer_binder) (struct task_struct *from, struct task_struct *to); + int (*binder_transfer_file) (struct task_struct *from, struct task_struct *to, struct file *file); + int (*ptrace_access_check) (struct task_struct *child, unsigned int mode); int (*ptrace_traceme) (struct task_struct *parent); int (*capget) (struct task_struct *target, @@ -1728,6 +1733,10 @@ extern void __init security_fixup_ops(struct security_operations *ops); /* Security operations */ +int security_binder_set_context_mgr(struct task_struct *mgr); +int security_binder_transaction(struct task_struct *from, struct task_struct *to); +int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to); +int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file); int security_ptrace_access_check(struct task_struct *child, unsigned int mode); int security_ptrace_traceme(struct task_struct *parent); int security_capget(struct task_struct *target, @@ -1914,6 +1923,26 @@ static inline int security_init(void) return 0; } +static inline int security_binder_set_context_mgr(struct task_struct *mgr) +{ + return 0; +} + +static inline int security_binder_transaction(struct task_struct *from, struct task_struct *to) +{ + return 0; +} + +static inline int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to) +{ + return 0; +} + +static inline int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) +{ + return 0; +} + static inline int security_ptrace_access_check(struct task_struct *child, unsigned int mode) { diff --git a/security/capability.c b/security/capability.c index 8b4f24ae4338..715ea2213297 100644 --- a/security/capability.c +++ b/security/capability.c @@ -12,6 +12,26 @@ #include +static int cap_binder_set_context_mgr(struct task_struct *mgr) +{ + return 0; +} + +static int cap_binder_transaction(struct task_struct *from, struct task_struct *to) +{ + return 0; +} + +static int cap_binder_transfer_binder(struct task_struct *from, struct task_struct *to) +{ + return 0; +} + +static int cap_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) +{ + return 0; +} + static int cap_syslog(int type) { return 0; @@ -924,6 +944,10 @@ static void cap_audit_rule_free(void *lsmrule) void __init security_fixup_ops(struct security_operations *ops) { + set_to_cap_if_null(ops, binder_set_context_mgr); + set_to_cap_if_null(ops, binder_transaction); + set_to_cap_if_null(ops, binder_transfer_binder); + set_to_cap_if_null(ops, binder_transfer_file); set_to_cap_if_null(ops, ptrace_access_check); set_to_cap_if_null(ops, ptrace_traceme); set_to_cap_if_null(ops, capget); diff --git a/security/security.c b/security/security.c index 15b6928592ef..5d173927e28a 100644 --- a/security/security.c +++ b/security/security.c @@ -135,6 +135,26 @@ int __init register_security(struct security_operations *ops) /* Security operations */ +int security_binder_set_context_mgr(struct task_struct *mgr) +{ + return security_ops->binder_set_context_mgr(mgr); +} + +int security_binder_transaction(struct task_struct *from, struct task_struct *to) +{ + return security_ops->binder_transaction(from, to); +} + +int security_binder_transfer_binder(struct task_struct *from, struct task_struct *to) +{ + return security_ops->binder_transfer_binder(from, to); +} + +int security_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) +{ + return security_ops->binder_transfer_file(from, to, file); +} + int security_ptrace_access_check(struct task_struct *child, unsigned int mode) { #ifdef CONFIG_SECURITY_YAMA_STACKED diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4b34847208cc..228d9577a3f1 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1906,6 +1906,67 @@ static inline u32 open_file_to_av(struct file *file) /* Hook functions begin here. */ +static int selinux_binder_set_context_mgr(struct task_struct *mgr) +{ + u32 mysid = current_sid(); + u32 mgrsid = task_sid(mgr); + + return avc_has_perm(mysid, mgrsid, SECCLASS_BINDER, BINDER__SET_CONTEXT_MGR, NULL); +} + +static int selinux_binder_transaction(struct task_struct *from, struct task_struct *to) +{ + u32 mysid = current_sid(); + u32 fromsid = task_sid(from); + u32 tosid = task_sid(to); + int rc; + + if (mysid != fromsid) { + rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER, BINDER__IMPERSONATE, NULL); + if (rc) + return rc; + } + + return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__CALL, NULL); +} + +static int selinux_binder_transfer_binder(struct task_struct *from, struct task_struct *to) +{ + u32 fromsid = task_sid(from); + u32 tosid = task_sid(to); + return avc_has_perm(fromsid, tosid, SECCLASS_BINDER, BINDER__TRANSFER, NULL); +} + +static int selinux_binder_transfer_file(struct task_struct *from, struct task_struct *to, struct file *file) +{ + u32 sid = task_sid(to); + struct file_security_struct *fsec = file->f_security; + struct inode *inode = file->f_path.dentry->d_inode; + struct inode_security_struct *isec = inode->i_security; + struct common_audit_data ad; + struct selinux_audit_data sad = {0,}; + int rc; + + COMMON_AUDIT_DATA_INIT(&ad, PATH); + ad.u.path = file->f_path; + ad.selinux_audit_data = &sad; + + if (sid != fsec->sid) { + rc = avc_has_perm(sid, fsec->sid, + SECCLASS_FD, + FD__USE, + &ad); + if (rc) + return rc; + } + + if (unlikely(IS_PRIVATE(inode))) + return 0; + + return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), + &ad); +} + static int selinux_ptrace_access_check(struct task_struct *child, unsigned int mode) { @@ -5743,6 +5804,11 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) static struct security_operations selinux_ops = { .name = "selinux", + .binder_set_context_mgr = selinux_binder_set_context_mgr, + .binder_transaction = selinux_binder_transaction, + .binder_transfer_binder = selinux_binder_transfer_binder, + .binder_transfer_file = selinux_binder_transfer_file, + .ptrace_access_check = selinux_ptrace_access_check, .ptrace_traceme = selinux_ptrace_traceme, .capget = selinux_capget, diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 14d04e63b1f0..c32ff7bde81a 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -151,5 +151,6 @@ struct security_class_mapping secclass_map[] = { { "kernel_service", { "use_as_override", "create_files_as", NULL } }, { "tun_socket", { COMMON_SOCK_PERMS, "attach_queue", NULL } }, + { "binder", { "impersonate", "call", "set_context_mgr", "transfer", NULL } }, { NULL } }; From 697ba9d8a8a6906607269f47ba31ff0d43fe0296 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Wed, 21 Nov 2012 15:04:25 -0800 Subject: [PATCH 225/358] trace: Add an option to show tgids in trace output The tgids are tracked along side the saved_cmdlines tracking, and can be included in trace output by enabling the 'print-tgid' trace option. This is useful when doing post-processing of the trace data, as it allows events to be grouped by tgid. Change-Id: I52ed04c3a8ca7fddbb868b792ce5d21ceb76250e Signed-off-by: Jamie Gennis --- kernel/trace/trace.c | 104 ++++++++++++++++++++++++++++++++++-- kernel/trace/trace.h | 2 + kernel/trace/trace_output.c | 18 ++++++- 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 815c878f409b..9d03010726a3 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -786,6 +786,7 @@ static const char *trace_options[] = { "irq-info", "markers", "function-trace", + "print-tgid", NULL }; @@ -1298,6 +1299,7 @@ void tracing_reset_all_online_cpus(void) static unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1]; static unsigned map_cmdline_to_pid[SAVED_CMDLINES]; static char saved_cmdlines[SAVED_CMDLINES][TASK_COMM_LEN]; +static unsigned saved_tgids[SAVED_CMDLINES]; static int cmdline_idx; static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED; @@ -1486,6 +1488,7 @@ static void trace_save_cmdline(struct task_struct *tsk) } memcpy(&saved_cmdlines[idx], tsk->comm, TASK_COMM_LEN); + saved_tgids[idx] = tsk->tgid; arch_spin_unlock(&trace_cmdline_lock); } @@ -1521,6 +1524,25 @@ void trace_find_cmdline(int pid, char comm[]) preempt_enable(); } +int trace_find_tgid(int pid) +{ + unsigned map; + int tgid; + + preempt_disable(); + arch_spin_lock(&trace_cmdline_lock); + map = map_pid_to_cmdline[pid]; + if (map != NO_CMDLINE_MAP) + tgid = saved_tgids[map]; + else + tgid = -1; + + arch_spin_unlock(&trace_cmdline_lock); + preempt_enable(); + + return tgid; +} + void tracing_record_cmdline(struct task_struct *tsk) { if (atomic_read(&trace_record_cmdline_disabled) || !tracing_is_on()) @@ -2469,6 +2491,13 @@ static void print_func_help_header(struct trace_buffer *buf, struct seq_file *m) seq_puts(m, "# | | | | |\n"); } +static void print_func_help_header_tgid(struct trace_buffer *buf, struct seq_file *m) +{ + print_event_info(buf, m); + seq_puts(m, "# TASK-PID TGID CPU# TIMESTAMP FUNCTION\n"); + seq_puts(m, "# | | | | | |\n"); +} + static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file *m) { print_event_info(buf, m); @@ -2481,6 +2510,18 @@ static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file seq_puts(m, "# | | | |||| | |\n"); } +static void print_func_help_header_irq_tgid(struct trace_buffer *buf, struct seq_file *m) +{ + print_event_info(buf, m); + seq_puts(m, "# _-----=> irqs-off\n"); + seq_puts(m, "# / _----=> need-resched\n"); + seq_puts(m, "# | / _---=> hardirq/softirq\n"); + seq_puts(m, "# || / _--=> preempt-depth\n"); + seq_puts(m, "# ||| / delay\n"); + seq_puts(m, "# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION\n"); + seq_puts(m, "# | | | | |||| | |\n"); +} + void print_trace_header(struct seq_file *m, struct trace_iterator *iter) { @@ -2781,9 +2822,15 @@ void trace_default_header(struct seq_file *m) } else { if (!(trace_flags & TRACE_ITER_VERBOSE)) { if (trace_flags & TRACE_ITER_IRQ_INFO) - print_func_help_header_irq(iter->trace_buffer, m); + if (trace_flags & TRACE_ITER_TGID) + print_func_help_header_irq_tgid(iter->trace_buffer, m); + else + print_func_help_header_irq(iter->trace_buffer, m); else - print_func_help_header(iter->trace_buffer, m); + if (trace_flags & TRACE_ITER_TGID) + print_func_help_header_tgid(iter->trace_buffer, m); + else + print_func_help_header(iter->trace_buffer, m); } } } @@ -3684,9 +3731,53 @@ tracing_saved_cmdlines_read(struct file *file, char __user *ubuf, } static const struct file_operations tracing_saved_cmdlines_fops = { - .open = tracing_open_generic, - .read = tracing_saved_cmdlines_read, - .llseek = generic_file_llseek, + .open = tracing_open_generic, + .read = tracing_saved_cmdlines_read, + .llseek = generic_file_llseek, +}; + +static ssize_t +tracing_saved_tgids_read(struct file *file, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + char *file_buf; + char *buf; + int len = 0; + int pid; + int i; + + file_buf = kmalloc(SAVED_CMDLINES*(16+1+16), GFP_KERNEL); + if (!file_buf) + return -ENOMEM; + + buf = file_buf; + + for (i = 0; i < SAVED_CMDLINES; i++) { + int tgid; + int r; + + pid = map_cmdline_to_pid[i]; + if (pid == -1 || pid == NO_CMDLINE_MAP) + continue; + + tgid = trace_find_tgid(pid); + r = sprintf(buf, "%d %d\n", pid, tgid); + buf += r; + len += r; + } + + len = simple_read_from_buffer(ubuf, cnt, ppos, + file_buf, len); + + kfree(file_buf); + + return len; +} + +static const struct file_operations tracing_saved_tgids_fops = { + .open = tracing_open_generic, + .read = tracing_saved_tgids_read, + .llseek = generic_file_llseek, }; static ssize_t @@ -6215,6 +6306,9 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer) trace_create_file("trace_marker", 0220, d_tracer, tr, &tracing_mark_fops); + trace_create_file("saved_tgids", 0444, d_tracer, + tr, &tracing_saved_tgids_fops); + trace_create_file("trace_clock", 0644, d_tracer, tr, &trace_clock_fops); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 02b592f2d4b7..2c72f3d558e7 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -645,6 +645,7 @@ static inline void __trace_stack(struct trace_array *tr, unsigned long flags, extern cycle_t ftrace_now(int cpu); extern void trace_find_cmdline(int pid, char comm[]); +extern int trace_find_tgid(int pid); #ifdef CONFIG_DYNAMIC_FTRACE extern unsigned long ftrace_update_tot_cnt; @@ -891,6 +892,7 @@ enum trace_iterator_flags { TRACE_ITER_IRQ_INFO = 0x800000, TRACE_ITER_MARKERS = 0x1000000, TRACE_ITER_FUNCTION = 0x2000000, + TRACE_ITER_TGID = 0x4000000, }; /* diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index ed32284fbe32..df6202d7a9f4 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -717,11 +717,25 @@ int trace_print_context(struct trace_iterator *iter) unsigned long secs, usec_rem; char comm[TASK_COMM_LEN]; int ret; + int tgid; trace_find_cmdline(entry->pid, comm); - ret = trace_seq_printf(s, "%16s-%-5d [%03d] ", - comm, entry->pid, iter->cpu); + ret = trace_seq_printf(s, "%16s-%-5d ", comm, entry->pid); + if (!ret) + return 0; + + if (trace_flags & TRACE_ITER_TGID) { + tgid = trace_find_tgid(entry->pid); + if (tgid < 0) + ret = trace_seq_puts(s, "(-----) "); + else + ret = trace_seq_printf(s, "(%5d) ", tgid); + if (!ret) + return 0; + } + + ret = trace_seq_printf(s, "[%03d] ", iter->cpu); if (!ret) return 0; From ca1e0afee8bd83650a060be6038614ca3838b2ab Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Wed, 21 Nov 2012 20:14:09 -0800 Subject: [PATCH 226/358] trace: add non-hierarchical function_graph option Add the 'funcgraph-flat' option to the function_graph tracer to use the default trace printing format rather than the hierarchical formatting normally used. Change-Id: If2900bfb86e6f8f51379f56da4f6fabafa630909 Signed-off-by: Jamie Gennis --- Documentation/trace/ftrace.txt | 29 +++++ kernel/trace/trace_functions_graph.c | 43 ++----- kernel/trace/trace_output.c | 164 +++++++++++++++++++++++++++ 3 files changed, 205 insertions(+), 31 deletions(-) diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt index bd365988e8d8..a0dcdbc112ee 100644 --- a/Documentation/trace/ftrace.txt +++ b/Documentation/trace/ftrace.txt @@ -2017,6 +2017,35 @@ will produce: 1) 1.449 us | } +You can disable the hierarchical function call formatting and instead print a +flat list of function entry and return events. This uses the format described +in the Output Formatting section and respects all the trace options that +control that formatting. Hierarchical formatting is the default. + + hierachical: echo nofuncgraph-flat > trace_options + flat: echo funcgraph-flat > trace_options + + ie: + + # tracer: function_graph + # + # entries-in-buffer/entries-written: 68355/68355 #P:2 + # + # _-----=> irqs-off + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth + # ||| / delay + # TASK-PID CPU# |||| TIMESTAMP FUNCTION + # | | | |||| | | + sh-1806 [001] d... 198.843443: graph_ent: func=_raw_spin_lock + sh-1806 [001] d... 198.843445: graph_ent: func=__raw_spin_lock + sh-1806 [001] d..1 198.843447: graph_ret: func=__raw_spin_lock + sh-1806 [001] d..1 198.843449: graph_ret: func=_raw_spin_lock + sh-1806 [001] d..1 198.843451: graph_ent: func=_raw_spin_unlock_irqrestore + sh-1806 [001] d... 198.843453: graph_ret: func=_raw_spin_unlock_irqrestore + + You might find other useful features for this tracer in the following "dynamic ftrace" section such as tracing only specific functions or tasks. diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 0b99120d395c..4c2daacd8a23 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -46,6 +46,8 @@ struct fgraph_data { #define TRACE_GRAPH_PRINT_DURATION 0x10 #define TRACE_GRAPH_PRINT_ABS_TIME 0x20 #define TRACE_GRAPH_PRINT_IRQS 0x40 +#define TRACE_GRAPH_PRINT_FLAT 0x80 + static unsigned int max_depth; @@ -64,6 +66,8 @@ static struct tracer_opt trace_opts[] = { { TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) }, /* Display interrupts */ { TRACER_OPT(funcgraph-irqs, TRACE_GRAPH_PRINT_IRQS) }, + /* Use standard trace formatting rather than hierarchical */ + { TRACER_OPT(funcgraph-flat, TRACE_GRAPH_PRINT_FLAT) }, { } /* Empty entry */ }; @@ -1284,6 +1288,9 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags) int cpu = iter->cpu; int ret; + if (flags & TRACE_GRAPH_PRINT_FLAT) + return TRACE_TYPE_UNHANDLED; + if (data && per_cpu_ptr(data->cpu_data, cpu)->ignore) { per_cpu_ptr(data->cpu_data, cpu)->ignore = 0; return TRACE_TYPE_HANDLED; @@ -1341,13 +1348,6 @@ print_graph_function(struct trace_iterator *iter) return print_graph_function_flags(iter, tracer_flags.val); } -static enum print_line_t -print_graph_function_event(struct trace_iterator *iter, int flags, - struct trace_event *event) -{ - return print_graph_function(iter); -} - static void print_lat_header(struct seq_file *s, u32 flags) { static const char spaces[] = " " /* 16 spaces */ @@ -1414,6 +1414,11 @@ void print_graph_headers_flags(struct seq_file *s, u32 flags) { struct trace_iterator *iter = s->private; + if (flags & TRACE_GRAPH_PRINT_FLAT) { + trace_default_header(s); + return; + } + if (!(trace_flags & TRACE_ITER_CONTEXT_INFO)) return; @@ -1484,20 +1489,6 @@ static int func_graph_set_flag(u32 old_flags, u32 bit, int set) return 0; } -static struct trace_event_functions graph_functions = { - .trace = print_graph_function_event, -}; - -static struct trace_event graph_trace_entry_event = { - .type = TRACE_GRAPH_ENT, - .funcs = &graph_functions, -}; - -static struct trace_event graph_trace_ret_event = { - .type = TRACE_GRAPH_RET, - .funcs = &graph_functions -}; - static struct tracer graph_trace __tracer_data = { .name = "function_graph", .open = graph_trace_open, @@ -1573,16 +1564,6 @@ static __init int init_graph_trace(void) { max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1); - if (!register_ftrace_event(&graph_trace_entry_event)) { - pr_warning("Warning: could not register graph trace events\n"); - return 1; - } - - if (!register_ftrace_event(&graph_trace_ret_event)) { - pr_warning("Warning: could not register graph trace events\n"); - return 1; - } - return register_tracer(&graph_trace); } diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index df6202d7a9f4..1083b614ce4e 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -1064,6 +1064,168 @@ static struct trace_event trace_fn_event = { .funcs = &trace_fn_funcs, }; +/* TRACE_GRAPH_ENT */ +static enum print_line_t trace_graph_ent_trace(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct trace_seq *s = &iter->seq; + struct ftrace_graph_ent_entry *field; + + trace_assign_type(field, iter->ent); + + if (!trace_seq_puts(s, "graph_ent: func=")) + return TRACE_TYPE_PARTIAL_LINE; + + if (!seq_print_ip_sym(s, field->graph_ent.func, flags)) + return TRACE_TYPE_PARTIAL_LINE; + + if (!trace_seq_puts(s, "\n")) + return TRACE_TYPE_PARTIAL_LINE; + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ent_raw(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ent_entry *field; + + trace_assign_type(field, iter->ent); + + if (!trace_seq_printf(&iter->seq, "%lx %d\n", + field->graph_ent.func, + field->graph_ent.depth)) + return TRACE_TYPE_PARTIAL_LINE; + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ent_hex(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ent_entry *field; + struct trace_seq *s = &iter->seq; + + trace_assign_type(field, iter->ent); + + SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.func); + SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.depth); + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ent_bin(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ent_entry *field; + struct trace_seq *s = &iter->seq; + + trace_assign_type(field, iter->ent); + + SEQ_PUT_FIELD_RET(s, field->graph_ent.func); + SEQ_PUT_FIELD_RET(s, field->graph_ent.depth); + + return TRACE_TYPE_HANDLED; +} + +static struct trace_event_functions trace_graph_ent_funcs = { + .trace = trace_graph_ent_trace, + .raw = trace_graph_ent_raw, + .hex = trace_graph_ent_hex, + .binary = trace_graph_ent_bin, +}; + +static struct trace_event trace_graph_ent_event = { + .type = TRACE_GRAPH_ENT, + .funcs = &trace_graph_ent_funcs, +}; + +/* TRACE_GRAPH_RET */ +static enum print_line_t trace_graph_ret_trace(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct trace_seq *s = &iter->seq; + struct trace_entry *entry = iter->ent; + struct ftrace_graph_ret_entry *field; + + trace_assign_type(field, entry); + + if (!trace_seq_puts(s, "graph_ret: func=")) + return TRACE_TYPE_PARTIAL_LINE; + + if (!seq_print_ip_sym(s, field->ret.func, flags)) + return TRACE_TYPE_PARTIAL_LINE; + + if (!trace_seq_puts(s, "\n")) + return TRACE_TYPE_PARTIAL_LINE; + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ret_raw(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ret_entry *field; + + trace_assign_type(field, iter->ent); + + if (!trace_seq_printf(&iter->seq, "%lx %lld %lld %ld %d\n", + field->ret.func, + field->ret.calltime, + field->ret.rettime, + field->ret.overrun, + field->ret.depth)); + return TRACE_TYPE_PARTIAL_LINE; + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ret_hex(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ret_entry *field; + struct trace_seq *s = &iter->seq; + + trace_assign_type(field, iter->ent); + + SEQ_PUT_HEX_FIELD_RET(s, field->ret.func); + SEQ_PUT_HEX_FIELD_RET(s, field->ret.calltime); + SEQ_PUT_HEX_FIELD_RET(s, field->ret.rettime); + SEQ_PUT_HEX_FIELD_RET(s, field->ret.overrun); + SEQ_PUT_HEX_FIELD_RET(s, field->ret.depth); + + return TRACE_TYPE_HANDLED; +} + +static enum print_line_t trace_graph_ret_bin(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct ftrace_graph_ret_entry *field; + struct trace_seq *s = &iter->seq; + + trace_assign_type(field, iter->ent); + + SEQ_PUT_FIELD_RET(s, field->ret.func); + SEQ_PUT_FIELD_RET(s, field->ret.calltime); + SEQ_PUT_FIELD_RET(s, field->ret.rettime); + SEQ_PUT_FIELD_RET(s, field->ret.overrun); + SEQ_PUT_FIELD_RET(s, field->ret.depth); + + return TRACE_TYPE_HANDLED; +} + +static struct trace_event_functions trace_graph_ret_funcs = { + .trace = trace_graph_ret_trace, + .raw = trace_graph_ret_raw, + .hex = trace_graph_ret_hex, + .binary = trace_graph_ret_bin, +}; + +static struct trace_event trace_graph_ret_event = { + .type = TRACE_GRAPH_RET, + .funcs = &trace_graph_ret_funcs, +}; + /* TRACE_CTX an TRACE_WAKE */ static enum print_line_t trace_ctxwake_print(struct trace_iterator *iter, char *delim) @@ -1454,6 +1616,8 @@ static struct trace_event trace_print_event = { static struct trace_event *events[] __initdata = { &trace_fn_event, + &trace_graph_ent_event, + &trace_graph_ret_event, &trace_ctx_event, &trace_wake_event, &trace_stack_event, From 8c96753c629ff11c349e16743786898758f1a471 Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Mon, 25 Feb 2013 23:48:04 +0900 Subject: [PATCH 227/358] cpufreq: interactive: allow arbitrary speed / delay mappings Accept a string of delays and speeds at which to apply the delay before raising each step above hispeed. For example, "80000 1300000:200000 1500000:40000" means that the delay at or above 1GHz, until 1.3GHz is 80 msecs, the delay until 1.5GHz is 200 msecs and the delay at or above 1.5GHz is 40 msecs when hispeed_freq is 1GHz. [toddpoynor@google.com: add documentation] Change-Id: Ifeebede8b1acbdd0a53e5c6916bccbf764dc854f Signed-off-by: Minsung Kim --- Documentation/cpu-freq/governors.txt | 12 +- drivers/cpufreq/cpufreq_interactive.c | 185 +++++++++++++++++--------- 2 files changed, 134 insertions(+), 63 deletions(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index 3732ec162873..2ed766ec75fc 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -260,7 +260,17 @@ Default is 99%. above_hispeed_delay: When speed is at or above hispeed_freq, wait for this long before raising speed in response to continued high load. -Default is 20000 uS. +The format is a single delay value, optionally followed by pairs of +CPU speeds and the delay to use at or above those speeds. Colons can +be used between the speeds and associated delays for readability. For +example: + + 80000 1300000:200000 1500000:40000 + +uses delay 80000 uS until CPU speed 1.3 GHz, at which speed delay +200000 uS is used until speed 1.5 GHz, at which speed (and above) +delay 40000 uS is used. If speeds are specified these must appear in +ascending order. Default is 20000 uS. timer_rate: Sample rate for reevaluating CPU load when the CPU is not idle. A deferrable timer is used, such that the CPU will not be woken diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 3447e58831d1..620b46cb9447 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -94,7 +94,11 @@ static unsigned long timer_rate = DEFAULT_TIMER_RATE; * timer interval. */ #define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE -static unsigned long above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY; +static unsigned int default_above_hispeed_delay[] = { + DEFAULT_ABOVE_HISPEED_DELAY }; +static spinlock_t above_hispeed_delay_lock; +static unsigned int *above_hispeed_delay = default_above_hispeed_delay; +static int nabove_hispeed_delay = ARRAY_SIZE(default_above_hispeed_delay); /* Non-zero means indefinite speed boost active */ static int boost_val; @@ -144,6 +148,23 @@ static void cpufreq_interactive_timer_resched( spin_unlock_irqrestore(&pcpu->load_lock, flags); } +static unsigned int freq_to_above_hispeed_delay(unsigned int freq) +{ + int i; + unsigned int ret; + unsigned long flags; + + spin_lock_irqsave(&above_hispeed_delay_lock, flags); + + for (i = 0; i < nabove_hispeed_delay - 1 && + freq >= above_hispeed_delay[i+1]; i += 2) + ; + + ret = above_hispeed_delay[i]; + spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + return ret; +} + static unsigned int freq_to_targetload(unsigned int freq) { int i; @@ -316,7 +337,8 @@ static void cpufreq_interactive_timer(unsigned long data) if (pcpu->target_freq >= hispeed_freq && new_freq > pcpu->target_freq && - now - pcpu->hispeed_validate_time < above_hispeed_delay_val) { + now - pcpu->hispeed_validate_time < + freq_to_above_hispeed_delay(pcpu->policy->cur)) { trace_cpufreq_interactive_notyet( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); @@ -580,6 +602,56 @@ static struct notifier_block cpufreq_notifier_block = { .notifier_call = cpufreq_interactive_notifier, }; +static unsigned int *get_tokenized_data(const char *buf, int *num_tokens) +{ + const char *cp; + int i; + int ntokens = 1; + unsigned int *tokenized_data; + + cp = buf; + while ((cp = strpbrk(cp + 1, " :"))) + ntokens++; + + if (!(ntokens & 0x1)) { + tokenized_data = ERR_PTR(-EINVAL); + goto err; + } + + tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); + if (!tokenized_data) { + tokenized_data = ERR_PTR(-ENOMEM); + goto err; + } + + cp = buf; + i = 0; + while (i < ntokens) { + if (sscanf(cp, "%u", &tokenized_data[i++]) != 1) { + tokenized_data = ERR_PTR(-EINVAL); + goto err_kfree; + } + + cp = strpbrk(cp, " :"); + if (!cp) + break; + cp++; + } + + if (i != ntokens) { + tokenized_data = ERR_PTR(-EINVAL); + goto err_kfree; + } + + *num_tokens = ntokens; + return tokenized_data; + +err_kfree: + kfree(tokenized_data); +err: + return tokenized_data; +} + static ssize_t show_target_loads( struct kobject *kobj, struct attribute *attr, char *buf) { @@ -602,40 +674,13 @@ static ssize_t store_target_loads( struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { - int ret; - const char *cp; + int ntokens; unsigned int *new_target_loads = NULL; - int ntokens = 1; - int i; unsigned long flags; - cp = buf; - while ((cp = strpbrk(cp + 1, " :"))) - ntokens++; - - if (!(ntokens & 0x1)) - goto err_inval; - - new_target_loads = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); - if (!new_target_loads) { - ret = -ENOMEM; - goto err; - } - - cp = buf; - i = 0; - while (i < ntokens) { - if (sscanf(cp, "%u", &new_target_loads[i++]) != 1) - goto err_inval; - - cp = strpbrk(cp, " :"); - if (!cp) - break; - cp++; - } - - if (i != ntokens) - goto err_inval; + new_target_loads = get_tokenized_data(buf, &ntokens); + if (IS_ERR(new_target_loads)) + return PTR_RET(new_target_loads); spin_lock_irqsave(&target_loads_lock, flags); if (target_loads != default_target_loads) @@ -644,18 +689,56 @@ static ssize_t store_target_loads( ntarget_loads = ntokens; spin_unlock_irqrestore(&target_loads_lock, flags); return count; - -err_inval: - ret = -EINVAL; -err: - kfree(new_target_loads); - return ret; } static struct global_attr target_loads_attr = __ATTR(target_loads, S_IRUGO | S_IWUSR, show_target_loads, store_target_loads); +static ssize_t show_above_hispeed_delay( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + int i; + ssize_t ret = 0; + unsigned long flags; + + spin_lock_irqsave(&above_hispeed_delay_lock, flags); + + for (i = 0; i < nabove_hispeed_delay; i++) + ret += sprintf(buf + ret, "%u%s", above_hispeed_delay[i], + i & 0x1 ? ":" : " "); + + ret += sprintf(buf + ret, "\n"); + spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + return ret; +} + +static ssize_t store_above_hispeed_delay( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ntokens; + unsigned int *new_above_hispeed_delay = NULL; + unsigned long flags; + + new_above_hispeed_delay = get_tokenized_data(buf, &ntokens); + if (IS_ERR(new_above_hispeed_delay)) + return PTR_RET(new_above_hispeed_delay); + + spin_lock_irqsave(&above_hispeed_delay_lock, flags); + if (above_hispeed_delay != default_above_hispeed_delay) + kfree(above_hispeed_delay); + above_hispeed_delay = new_above_hispeed_delay; + nabove_hispeed_delay = ntokens; + spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + return count; + +} + +static struct global_attr above_hispeed_delay_attr = + __ATTR(above_hispeed_delay, S_IRUGO | S_IWUSR, + show_above_hispeed_delay, store_above_hispeed_delay); + static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -724,28 +807,6 @@ static ssize_t store_min_sample_time(struct kobject *kobj, static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, show_min_sample_time, store_min_sample_time); -static ssize_t show_above_hispeed_delay(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - return sprintf(buf, "%lu\n", above_hispeed_delay_val); -} - -static ssize_t store_above_hispeed_delay(struct kobject *kobj, - struct attribute *attr, - const char *buf, size_t count) -{ - int ret; - unsigned long val; - - ret = strict_strtoul(buf, 0, &val); - if (ret < 0) - return ret; - above_hispeed_delay_val = val; - return count; -} - -define_one_global_rw(above_hispeed_delay); - static ssize_t show_timer_rate(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -865,9 +926,9 @@ define_one_global_rw(boostpulse_duration); static struct attribute *interactive_attributes[] = { &target_loads_attr.attr, + &above_hispeed_delay_attr.attr, &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, - &above_hispeed_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, &timer_slack.attr, From 395f1e0301b17359a20a7dafdb530434f6834fc7 Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Fri, 22 Feb 2013 11:39:18 +0800 Subject: [PATCH 228/358] cpufreq: interactive: add io_is_busy interface Previously the idle time returned from get_cpu_idle_time_us included the iowait time. So the iowait time was always calculated as idle time. But now the idle time returned from get_cpu_idle_time_us does not include the iowait time anymore because of below commit which cause the iowait time always calculated as busy time: 6beea0c nohz: Fix update_ts_time_stat idle accounting Add the io_is_busy interface, as does the ondemand governor, and let the user configure the iowait time as busy or idle through the io_is_busy sysfs interface. By default, io_is_busy is disabled. [toddpoynor@google.com: minor updates] Change-Id: If7d70ff864c43bc9c8d7fd7cfc66f930d339f9b4 Signed-off-by: Lianwei Wang Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 66 ++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 620b46cb9447..7c734fa57e24 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #define CREATE_TRACE_POINTS @@ -114,6 +115,8 @@ static u64 boostpulse_endtime; #define DEFAULT_TIMER_SLACK (4 * DEFAULT_TIMER_RATE) static int timer_slack_val = DEFAULT_TIMER_SLACK; +static bool io_is_busy; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -127,6 +130,42 @@ struct cpufreq_governor cpufreq_gov_interactive = { .owner = THIS_MODULE, }; +static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, + cputime64_t *wall) +{ + u64 idle_time; + u64 cur_wall_time; + u64 busy_time; + + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); + + busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; + + idle_time = cur_wall_time - busy_time; + if (wall) + *wall = jiffies_to_usecs(cur_wall_time); + + return jiffies_to_usecs(idle_time); +} + +static inline cputime64_t get_cpu_idle_time(unsigned int cpu, + cputime64_t *wall) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, wall); + + if (idle_time == -1ULL) + idle_time = get_cpu_idle_time_jiffy(cpu, wall); + else if (!io_is_busy) + idle_time += get_cpu_iowait_time_us(cpu, wall); + + return idle_time; +} + static void cpufreq_interactive_timer_resched( struct cpufreq_interactive_cpuinfo *pcpu) { @@ -141,7 +180,7 @@ static void cpufreq_interactive_timer_resched( spin_lock_irqsave(&pcpu->load_lock, flags); pcpu->time_in_idle = - get_cpu_idle_time_us(smp_processor_id(), + get_cpu_idle_time(smp_processor_id(), &pcpu->time_in_idle_timestamp); pcpu->cputime_speedadj = 0; pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; @@ -278,7 +317,7 @@ static u64 update_load(int cpu) unsigned int delta_time; u64 active_time; - now_idle = get_cpu_idle_time_us(cpu, &now); + now_idle = get_cpu_idle_time(cpu, &now); delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); active_time = delta_time - delta_idle; @@ -924,6 +963,28 @@ static ssize_t store_boostpulse_duration( define_one_global_rw(boostpulse_duration); +static ssize_t show_io_is_busy(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", io_is_busy); +} + +static ssize_t store_io_is_busy(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + io_is_busy = val; + return count; +} + +static struct global_attr io_is_busy_attr = __ATTR(io_is_busy, 0644, + show_io_is_busy, store_io_is_busy); + static struct attribute *interactive_attributes[] = { &target_loads_attr.attr, &above_hispeed_delay_attr.attr, @@ -935,6 +996,7 @@ static struct attribute *interactive_attributes[] = { &boost.attr, &boostpulse.attr, &boostpulse_duration.attr, + &io_is_busy_attr.attr, NULL, }; From c5277ed6f0b15e1e1fa49923a707043a67827d4f Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 20 Mar 2013 15:40:46 -0700 Subject: [PATCH 229/358] cpufreq: interactive: fix crash on error paths in get_tokenized_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use separate variable for error code, free proper pointer. Change-Id: Ia83cccb195997789ac6afbf5b8761f7b278196d6 Reported-by: Arve Hjønnevåg Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 7c734fa57e24..3dc067e6eb45 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -647,29 +647,26 @@ static unsigned int *get_tokenized_data(const char *buf, int *num_tokens) int i; int ntokens = 1; unsigned int *tokenized_data; + int err = -EINVAL; cp = buf; while ((cp = strpbrk(cp + 1, " :"))) ntokens++; - if (!(ntokens & 0x1)) { - tokenized_data = ERR_PTR(-EINVAL); + if (!(ntokens & 0x1)) goto err; - } tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); if (!tokenized_data) { - tokenized_data = ERR_PTR(-ENOMEM); + err = -ENOMEM; goto err; } cp = buf; i = 0; while (i < ntokens) { - if (sscanf(cp, "%u", &tokenized_data[i++]) != 1) { - tokenized_data = ERR_PTR(-EINVAL); + if (sscanf(cp, "%u", &tokenized_data[i++]) != 1) goto err_kfree; - } cp = strpbrk(cp, " :"); if (!cp) @@ -677,10 +674,8 @@ static unsigned int *get_tokenized_data(const char *buf, int *num_tokens) cp++; } - if (i != ntokens) { - tokenized_data = ERR_PTR(-EINVAL); + if (i != ntokens) goto err_kfree; - } *num_tokens = ntokens; return tokenized_data; @@ -688,7 +683,7 @@ static unsigned int *get_tokenized_data(const char *buf, int *num_tokens) err_kfree: kfree(tokenized_data); err: - return tokenized_data; + return ERR_PTR(err); } static ssize_t show_target_loads( From 0786698238dc6b5de519e2aca21975d47037a02c Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Mon, 25 Mar 2013 15:04:41 -0700 Subject: [PATCH 230/358] ARM: add config option to build zImage/dtb combo Allows a defconfig to set a default dtb to concatenate with a zImage to create a zImage-dtb. Signed-off-by: Erik Gilling Change-Id: I34b643b1c49228fbae88a56e46c93c478089620d --- arch/arm/Kconfig | 15 +++++++++++++++ arch/arm/Makefile | 2 ++ 2 files changed, 17 insertions(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 0f41da796690..4cec3565f6bc 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1950,6 +1950,21 @@ config DEPRECATED_PARAM_STRUCT This was deprecated in 2001 and announced to live on for 5 years. Some old boot loaders still use this way. +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 DTB to + be built by default (instead of a standalone zImage.) The image + will built in arch/arm/boot/zImage-dtb. + +config BUILD_ARM_APPENDED_DTB_IMAGE_NAME + string "Default dtb name" + depends on BUILD_ARM_APPENDED_DTB_IMAGE + help + 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. config ZBOOT_ROM_TEXT diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 08a9ef58d9c3..1115c76d9d90 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -277,6 +277,8 @@ libs-y := arch/arm/lib/ $(libs-y) # Default target when executing plain make ifeq ($(CONFIG_XIP_KERNEL),y) KBUILD_IMAGE := xipImage +else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y) +KBUILD_IMAGE := zImage-dtb.$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAME) else KBUILD_IMAGE := zImage endif From 8ef87c0a99ebf2943da995d412b475c1d65f688e Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 21 Mar 2013 20:46:00 -0700 Subject: [PATCH 231/358] cpufreq: interactive: base above_hispeed_delay on target freq, not current MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Time to wait should be based on the intended target speed, not the actual speed (which may be held high by another CPU). Change-Id: Ifc5bb55d06adddb9a02af90af05398a78f282272 Reported-by: Arve Hjønnevåg Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 3dc067e6eb45..9dd4d38f2c9e 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -377,7 +377,7 @@ static void cpufreq_interactive_timer(unsigned long data) if (pcpu->target_freq >= hispeed_freq && new_freq > pcpu->target_freq && now - pcpu->hispeed_validate_time < - freq_to_above_hispeed_delay(pcpu->policy->cur)) { + freq_to_above_hispeed_delay(pcpu->target_freq)) { trace_cpufreq_interactive_notyet( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); From c569c581c88511ca5c633a25d9640e5a91e83f93 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 8 Apr 2013 15:09:26 -0700 Subject: [PATCH 232/358] netfilter: qtaguid: rate limit some of the printks Some of the printks are in the packet handling path. We now ratelimit the very unlikely errors to avoid kmsg spamming. Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 992a6e044902..4ec6d23876c5 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1328,12 +1329,12 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, } if (unlikely(!el_dev)) { - pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", - par->hooknum, __func__); + pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); BUG(); } else if (unlikely(!el_dev->name)) { - pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", - par->hooknum, __func__); + pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); BUG(); } else { proto = ipx_proto(skb, par); @@ -1416,8 +1417,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, iface_entry = get_iface_entry(ifname); if (!iface_entry) { - pr_err("qtaguid: iface_stat: stat_update() %s not found\n", - ifname); + pr_err_ratelimited("qtaguid: iface_stat: stat_update() " + "%s not found\n", ifname); return; } /* It is ok to process data when an iface_entry is inactive */ From ef5983ef500479762df7bdf28bc6b433ca834974 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 17 Apr 2013 16:58:36 -0700 Subject: [PATCH 233/358] ARM: convert build of appended dtb zImage to list of dtbs Allow CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES to specify a space separated list of dtbs to append to the zImage, and name the resulting file zImage-dtb Change-Id: I36d9108a2349bdbb373e95076dcb1417d8c7dce6 Signed-off-by: Colin Cross Conflicts: arch/arm/boot/Makefile scripts/Makefile.lib --- arch/arm/Kconfig | 14 +++++++------- arch/arm/Makefile | 5 ++++- arch/arm/boot/.gitignore | 1 + arch/arm/boot/Makefile | 12 ++++++++++++ arch/arm/boot/dts/Makefile | 11 +++++++++-- scripts/Makefile.lib | 6 ++++++ 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4cec3565f6bc..f3b988ee9f4c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1954,16 +1954,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 DTB 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 list of + DTBs to be built by default (instead of a standalone zImage.) + The image will built in arch/arm/boot/zImage-dtb -config BUILD_ARM_APPENDED_DTB_IMAGE_NAME - string "Default dtb name" +config BUILD_ARM_APPENDED_DTB_IMAGE_NAMES + string "Default dtb names" depends on BUILD_ARM_APPENDED_DTB_IMAGE help - name of the dtb to append when building a concatenated - zImage/dtb. + Space separated list of names of dtbs 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. diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 1115c76d9d90..55eba0e8c4c3 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -278,7 +278,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.$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAME) +KBUILD_IMAGE := zImage-dtb else KBUILD_IMAGE := zImage endif @@ -316,6 +316,9 @@ PHONY += dtbs 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) diff --git a/arch/arm/boot/.gitignore b/arch/arm/boot/.gitignore index 3c79f85975aa..ad7a0253ea96 100644 --- a/arch/arm/boot/.gitignore +++ b/arch/arm/boot/.gitignore @@ -4,3 +4,4 @@ xipImage bootpImage uImage *.dtb +zImage-dtb \ No newline at end of file diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index ec2f8065f955..a30beab472e6 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -27,6 +27,14 @@ 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 @@ -55,6 +63,10 @@ $(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),) diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 032030361bef..78a3b81a94f4 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -323,13 +323,20 @@ dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb \ zynq-zc706.dtb \ zynq-zed.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-y) +targets += $(DTB_LIST) 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-y)) +dtbs: $(addprefix $(obj)/, $(DTB_LIST)) $(Q)rm -f $(obj)/../*.dtb clean-files := *.dtb diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 79c059e70860..85c0e97b2238 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -274,6 +274,12 @@ $(obj)/%.dtb: $(src)/%.dts FORCE dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) +# cat +# --------------------------------------------------------------------------- +# Concatentate multiple files together +quiet_cmd_cat = CAT $@ +cmd_cat = (cat $(filter-out FORCE,$^) > $@) || (rm -f $@; false) + # Bzip2 # --------------------------------------------------------------------------- From a15c77663133e55e67b77e5c1cf0a5fb504aa99c Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Thu, 18 Apr 2013 20:36:21 -0700 Subject: [PATCH 234/358] trace/events: fix gpu event timestamp formatting This change fixes the how the gpu_sched_switch timestamp field is formatted. Signed-off-by: Jamie Gennis Change-Id: I273234935254ed15772c9e561c9af20e480004ae --- include/trace/events/gpu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/trace/events/gpu.h b/include/trace/events/gpu.h index 09efa71d66c6..7e15cdfafe5a 100644 --- a/include/trace/events/gpu.h +++ b/include/trace/events/gpu.h @@ -11,6 +11,7 @@ ({ \ u64 t = ns + (NSEC_PER_USEC / 2); \ do_div(t, NSEC_PER_SEC); \ + t; \ }) #define show_usecs_from_ns(ns) \ @@ -77,7 +78,7 @@ TRACE_EVENT(gpu_sched_switch, __entry->next_job_id = next_job_id; ), - TP_printk("gpu_name=%s ts=%5llu.%06lu next_ctx_id=%lu next_prio=%ld " + TP_printk("gpu_name=%s ts=%llu.%06lu next_ctx_id=%lu next_prio=%ld " "next_job_id=%lu", __get_str(gpu_name), (unsigned long long)show_secs_from_ns(__entry->timestamp), From c9d2aef3060c33238e4a17582339c03fd731a949 Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Wed, 6 Feb 2013 15:49:47 -0800 Subject: [PATCH 235/358] android: configs: Initial commit of Android config fragments - Add 2 files that contain the minimal and recommended kernel config options respectively. - Add a README to explain their purpose and how to use them to generate a device config compatible with Android. Change-Id: I3a4883f3b04d2820e90ceb3c4d02390d6458d6ce Signed-off-by: Rom Lemarchand --- android/configs/README | 13 +++ android/configs/android-base.cfg | 135 ++++++++++++++++++++++++ android/configs/android-recommended.cfg | 115 ++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 android/configs/README create mode 100644 android/configs/android-base.cfg create mode 100644 android/configs/android-recommended.cfg diff --git a/android/configs/README b/android/configs/README new file mode 100644 index 000000000000..391dffa6f85f --- /dev/null +++ b/android/configs/README @@ -0,0 +1,13 @@ +The files in this directory are meant to be used as a base for an Android +kernel config. All devices should have the options in android-base.cfg enabled. +While not mandatory, the options in android-recommended.cfg enable advanced +Android features. + +Assuming you already have a minimalist defconfig for your device, a possible +way to enable these options would be: + + ARCH= scripts/kconfig/merge_config.sh /_defconfig android/configs/android-base.cfg android/configs/android-recommended.cfg + +This will generate a .config that can then be used to save a new defconfig or +compile a new kernel with Android features enabled. + diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg new file mode 100644 index 000000000000..664797780529 --- /dev/null +++ b/android/configs/android-base.cfg @@ -0,0 +1,135 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EMBEDDED=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +# CONFIG_OABI_COMPAT is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_INET_ESP=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_REJECT_SKERR=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_TARGET_REJECT_SKERR=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_NETDEVICES=y +CONFIG_TUN=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_FB=y +CONFIG_SYNC=y +CONFIG_USB_GADGET=y +CONFIG_USB_G_ANDROID=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_SWITCH=y +CONFIG_RTC_CLASS=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_INTF_ALARM_DEV=y diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg new file mode 100644 index 000000000000..6a4d421c9fed --- /dev/null +++ b/android/configs/android-recommended.cfg @@ -0,0 +1,115 @@ +CONFIG_PANIC_TIMEOUT=5 +CONFIG_KALLSYMS_ALL=y +CONFIG_PERF_EVENTS=y +CONFIG_COMPACTION=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_RUNTIME=y +CONFIG_PM_DEBUG=y +CONFIG_SUSPEND_TIME=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_UID_STAT=y +CONFIG_MD=y +CONFIG_DM_UEVENT=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_TABLET_USB_WACOM=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_POWER_SUPPLY=y +CONFIG_BATTERY_ANDROID=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_UHID=y +CONFIG_USB_HIDDEV=y +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_USB_USBNET=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_EHCI_HCD=y +CONFIG_ION=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_SCHED_TRACER=y From a97e45685bcd0f719f40c45453267d7a213c4a62 Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Tue, 16 Apr 2013 21:52:50 +0900 Subject: [PATCH 236/358] cpufreq: interactive: fix uninitialized spinlock Add missing spinlock init Backtrace: [] (dump_backtrace+0x0/0x10c) from [] (dump_stack+0x18/0x1c) r6:00000032 r5:c0bd09ec r4:e6848000 r3:00000000 [] (dump_stack+0x0/0x1c) from [] (spin_dump+0x80/0x94) [] (spin_dump+0x0/0x94) from [] (spin_bug+0x2c/0x30) r5:c08f91fc r4:c0bd09ec [] (spin_bug+0x0/0x30) from [] (do_raw_spin_unlock+0x88/0xcc) r5:e547bac0 r4:c0bd09ec [] (do_raw_spin_unlock+0x0/0xcc) from [] (_raw_spin_unlock_irqrestore+0x14/0x40) r5:e547bac0 r4:60000013 [] (_raw_spin_unlock_irqrestore+0x0/0x40) from [] (store_above_hispeed_delay+0x6c/0x80) r4:c0b4cf78 r3:00000007 [] (store_above_hispeed_delay+0x0/0x80) from [] (kobj_attr_store+0x1c/0x28) r7:e68ff000 r6:00000032 r5:e58137c0 r4:e61cde80 [] (kobj_attr_store+0x0/0x28) from [] (sysfs_write_file+0x104/0x184) [] (sysfs_write_file+0x0/0x184) from [] (vfs_write+0xb0/0x140) [] (vfs_write+0x0/0x140) from [] (sys_write+0x44/0x70) r8:00000000 r7:00000004 r6:00000032 r5:bee43c90 r4:e5600300 [] (sys_write+0x0/0x70) from [] (ret_fast_syscall+0x0/0x30) r9:e6842000 r8:c000e584 r6:00000032 r5:bee43c90 r4:00000009 Change-Id: I80a1e0b3fecb24adba501ff44f568479deeff7fa Signed-off-by: Minsung Kim --- drivers/cpufreq/cpufreq_interactive.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 9dd4d38f2c9e..baf8740f1677 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -1148,6 +1148,7 @@ static int __init cpufreq_interactive_init(void) spin_lock_init(&target_loads_lock); spin_lock_init(&speedchange_cpumask_lock); + spin_lock_init(&above_hispeed_delay_lock); mutex_init(&gov_lock); speedchange_task = kthread_create(cpufreq_interactive_speedchange_task, NULL, From b1aac7dca31d42a8f85c763676482df8cd9d5436 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 29 Apr 2013 16:07:00 -0700 Subject: [PATCH 237/358] ARM: fault: assume no context when IRQs are disabled during data abort. Bail out early if IRQs are disabled in do_page_fault or else [14415.157266] BUG: sleeping function called from invalid context at arch/arm/mm/fault.c:301 Russell King's idea from http://comments.gmane.org/gmane.linux.ports.arm.omap/59256 Signed-off-by: JP Abgrall --- arch/arm/mm/fault.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index eb8830a4c5ed..b784579545e8 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -274,10 +274,10 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) local_irq_enable(); /* - * If we're in an interrupt or have no user + * If we're in an interrupt, or have no irqs, or have no user * context, we must not take the fault.. */ - if (in_atomic() || !mm) + if (in_atomic() || irqs_disabled() || !mm) goto no_context; if (user_mode(regs)) From fd7509d9e36b2deb2ff4428d8189d6320a29d19a Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 3 May 2013 14:57:29 -0700 Subject: [PATCH 238/358] lowmemorykiller: make default lowmemorykiller debug message useful lowmemorykiller debug messages are inscrutable and mostly useful for debugging the lowmemorykiller, not explaining why a process was killed. Make the messages more useful by prefixing them with "lowmemorykiller: " and explaining in more readable terms what was killed, who it was killed for, and why it was killed. The messages now look like: [ 76.997631] lowmemorykiller: Killing 'droid.gallery3d' (2172), adj 1000, [ 76.997635] to free 27436kB on behalf of 'kswapd0' (29) because [ 76.997638] cache 122624kB is below limit 122880kB for oom_score_adj 1000 [ 76.997641] Free memory is -53356kB above reserved A negative number for free memory above reserved means some of the reserved memory has been used and is being regenerated by kswapd, which is likely what called the shrinkers. Change-Id: I1fe983381e73e124b90aa5d91cb66e55eaca390f Signed-off-by: Colin Cross --- drivers/staging/android/lowmemorykiller.c | 24 ++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 7157305f6c81..89fed36b4bfb 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -82,6 +82,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) int tasksize; int i; short min_score_adj = OOM_SCORE_ADJ_MAX + 1; + int minfree = 0; int selected_tasksize = 0; short selected_oom_score_adj; int array_size = ARRAY_SIZE(lowmem_adj); @@ -94,8 +95,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (lowmem_minfree_size < array_size) array_size = lowmem_minfree_size; for (i = 0; i < array_size; i++) { - if (other_free < lowmem_minfree[i] && - other_file < lowmem_minfree[i]) { + minfree = lowmem_minfree[i]; + if (other_free < minfree && other_file < minfree) { min_score_adj = lowmem_adj[i]; break; } @@ -150,13 +151,22 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) selected = p; selected_tasksize = tasksize; selected_oom_score_adj = oom_score_adj; - lowmem_print(2, "select %d (%s), adj %hd, size %d, to kill\n", - p->pid, p->comm, oom_score_adj, tasksize); + lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n", + p->comm, p->pid, oom_score_adj, tasksize); } if (selected) { - lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n", - selected->pid, selected->comm, - selected_oom_score_adj, selected_tasksize); + lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \ + " to free %ldkB on behalf of '%s' (%d) because\n" \ + " cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \ + " Free memory is %ldkB above reserved\n", + selected->comm, selected->pid, + selected_oom_score_adj, + selected_tasksize * (long)(PAGE_SIZE / 1024), + current->comm, current->pid, + other_file * (long)(PAGE_SIZE / 1024), + minfree * (long)(PAGE_SIZE / 1024), + min_score_adj, + other_free * (long)(PAGE_SIZE / 1024)); lowmem_deathpending_timeout = jiffies + HZ; send_sig(SIGKILL, selected, 0); set_tsk_thread_flag(selected, TIF_MEMDIE); From 2b193287e32cf16d5f4d7e280485bc609617df94 Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Wed, 15 May 2013 20:13:13 -0700 Subject: [PATCH 239/358] mmc: Add tracepoints of mmc block operations Add tracepoints to record the start and end of each mmc block operation. This includes read, write, erase, secure erase, trim, secure trim1 and secure trim 2, discard and sanitize commands. Change-Id: Ic5d1cbdb9adb940d8b1a2a13c73970023575df50 Signed-off-by: Ken Sumrall --- drivers/mmc/card/block.c | 5 +++ drivers/mmc/core/core.c | 16 ++++++- include/trace/events/mmc.h | 91 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 include/trace/events/mmc.h diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 717614933016..499ab365600d 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -36,6 +36,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + #include #include #include @@ -422,9 +425,11 @@ static int ioctl_do_sanitize(struct mmc_card *card) pr_debug("%s: %s - SANITIZE IN PROGRESS...\n", mmc_hostname(card->host), __func__); + trace_mmc_blk_erase_start(EXT_CSD_SANITIZE_START, 0, 0); err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_SANITIZE_START, 1, MMC_SANITIZE_REQ_TIMEOUT); + trace_mmc_blk_erase_end(EXT_CSD_SANITIZE_START, 0, 0); if (err) pr_err("%s: %s - EXT_CSD_SANITIZE_START failed. err=%d\n", diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index e02d9bb10125..7ee8abeea127 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -31,6 +31,8 @@ #include #include +#include + #include #include #include @@ -177,6 +179,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) pr_debug("%s: %d bytes transferred: %d\n", mmc_hostname(host), mrq->data->bytes_xfered, mrq->data->error); + trace_mmc_blk_rw_end(cmd->opcode, cmd->arg, mrq->data); } if (mrq->stop) { @@ -560,8 +563,12 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, mmc_start_bkops(host->card, true); } - if (!err && areq) + if (!err && areq) { + trace_mmc_blk_rw_start(areq->mrq->cmd->opcode, + areq->mrq->cmd->arg, + areq->mrq->data); start_err = __mmc_start_data_req(host, areq->mrq); + } if (host->areq) mmc_post_req(host, host->areq->mrq, 0); @@ -1888,8 +1895,13 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, struct mmc_command cmd = {0}; unsigned int qty = 0; unsigned long timeout; + unsigned int fr, nr; int err; + fr = from; + nr = to - from + 1; + trace_mmc_blk_erase_start(arg, fr, nr); + /* * qty is used to calculate the erase timeout which depends on how many * erase groups (or allocation units in SD terminology) are affected. @@ -1993,6 +2005,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG)); out: + + trace_mmc_blk_erase_end(arg, fr, nr); return err; } diff --git a/include/trace/events/mmc.h b/include/trace/events/mmc.h new file mode 100644 index 000000000000..82b368dbcefc --- /dev/null +++ b/include/trace/events/mmc.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2013 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. + * + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mmc + +#if !defined(_TRACE_MMC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_MMC_H + +#include +#include +#include + +/* + * Unconditional logging of mmc block erase operations, + * including cmd, address, size + */ +DECLARE_EVENT_CLASS(mmc_blk_erase_class, + TP_PROTO(unsigned int cmd, unsigned int addr, unsigned int size), + TP_ARGS(cmd, addr, size), + TP_STRUCT__entry( + __field(unsigned int, cmd) + __field(unsigned int, addr) + __field(unsigned int, size) + ), + TP_fast_assign( + __entry->cmd = cmd; + __entry->addr = addr; + __entry->size = size; + ), + TP_printk("cmd=%u,addr=0x%08x,size=0x%08x", + __entry->cmd, __entry->addr, __entry->size) +); + +DEFINE_EVENT(mmc_blk_erase_class, mmc_blk_erase_start, + TP_PROTO(unsigned int cmd, unsigned int addr, unsigned int size), + TP_ARGS(cmd, addr, size)); + +DEFINE_EVENT(mmc_blk_erase_class, mmc_blk_erase_end, + TP_PROTO(unsigned int cmd, unsigned int addr, unsigned int size), + TP_ARGS(cmd, addr, size)); + +/* + * Logging of start of read or write mmc block operation, + * including cmd, address, size + */ +DECLARE_EVENT_CLASS(mmc_blk_rw_class, + TP_PROTO(unsigned int cmd, unsigned int addr, struct mmc_data *data), + TP_ARGS(cmd, addr, data), + TP_STRUCT__entry( + __field(unsigned int, cmd) + __field(unsigned int, addr) + __field(unsigned int, size) + ), + TP_fast_assign( + __entry->cmd = cmd; + __entry->addr = addr; + __entry->size = data->blocks; + ), + TP_printk("cmd=%u,addr=0x%08x,size=0x%08x", + __entry->cmd, __entry->addr, __entry->size) +); + +DEFINE_EVENT_CONDITION(mmc_blk_rw_class, mmc_blk_rw_start, + TP_PROTO(unsigned int cmd, unsigned int addr, struct mmc_data *data), + TP_ARGS(cmd, addr, data), + TP_CONDITION(((cmd == MMC_READ_MULTIPLE_BLOCK) || + (cmd == MMC_WRITE_MULTIPLE_BLOCK)) && + data)); + +DEFINE_EVENT_CONDITION(mmc_blk_rw_class, mmc_blk_rw_end, + TP_PROTO(unsigned int cmd, unsigned int addr, struct mmc_data *data), + TP_ARGS(cmd, addr, data), + TP_CONDITION(((cmd == MMC_READ_MULTIPLE_BLOCK) || + (cmd == MMC_WRITE_MULTIPLE_BLOCK)) && + data)); +#endif /* _TRACE_MMC_H */ + +/* This part must be outside protection */ +#include From 97bf70d4a1e7d9c1e421c6e0080e6334432e6992 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 30 May 2013 15:31:17 -0700 Subject: [PATCH 240/358] misc: uidstat: avoid create_stat() race and blockage. * create_stat() race would lead to: [ 58.132324] proc_dir_entry 'uid_stat/10061' already registered * blocking kmalloc reported by sbranden tcp_read_sock() uid_stat_tcp_rcv() create_stat() kmalloc(GFP_KERNEL) Signed-off-by: JP Abgrall --- drivers/misc/uid_stat.c | 52 ++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c index 2141124a6c12..509822c81e97 100644 --- a/drivers/misc/uid_stat.c +++ b/drivers/misc/uid_stat.c @@ -38,17 +38,13 @@ struct uid_stat { }; static struct uid_stat *find_uid_stat(uid_t uid) { - unsigned long flags; struct uid_stat *entry; - spin_lock_irqsave(&uid_lock, flags); list_for_each_entry(entry, &uid_list, link) { if (entry->uid == uid) { - spin_unlock_irqrestore(&uid_lock, flags); return entry; } } - spin_unlock_irqrestore(&uid_lock, flags); return NULL; } @@ -90,13 +86,10 @@ static int tcp_rcv_read_proc(char *page, char **start, off_t off, /* Create a new entry for tracking the specified uid. */ static struct uid_stat *create_stat(uid_t uid) { - unsigned long flags; - char uid_s[32]; struct uid_stat *new_uid; - struct proc_dir_entry *entry; - /* Create the uid stat struct and append it to the list. */ - if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL) + new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC); + if (!new_uid) return NULL; new_uid->uid = uid; @@ -104,11 +97,15 @@ static struct uid_stat *create_stat(uid_t uid) { atomic_set(&new_uid->tcp_rcv, INT_MIN); atomic_set(&new_uid->tcp_snd, INT_MIN); - spin_lock_irqsave(&uid_lock, flags); list_add_tail(&new_uid->link, &uid_list); - spin_unlock_irqrestore(&uid_lock, flags); + return new_uid; +} - sprintf(uid_s, "%d", uid); +static void create_stat_proc(struct uid_stat *new_uid) +{ + char uid_s[32]; + struct proc_dir_entry *entry; + sprintf(uid_s, "%d", new_uid->uid); entry = proc_mkdir(uid_s, parent); /* Keep reference to uid_stat so we know what uid to read stats from. */ @@ -117,17 +114,31 @@ static struct uid_stat *create_stat(uid_t uid) { create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc, (void *) new_uid); +} - return new_uid; +static struct uid_stat *find_or_create_uid_stat(uid_t uid) +{ + struct uid_stat *entry; + unsigned long flags; + spin_lock_irqsave(&uid_lock, flags); + entry = find_uid_stat(uid); + if (entry) { + spin_unlock_irqrestore(&uid_lock, flags); + return entry; + } + entry = create_stat(uid); + spin_unlock_irqrestore(&uid_lock, flags); + if (entry) + create_stat_proc(entry); + return entry; } int uid_stat_tcp_snd(uid_t uid, int size) { struct uid_stat *entry; activity_stats_update(); - if ((entry = find_uid_stat(uid)) == NULL && - ((entry = create_stat(uid)) == NULL)) { - return -1; - } + entry = find_or_create_uid_stat(uid); + if (!entry) + return -1; atomic_add(size, &entry->tcp_snd); return 0; } @@ -135,10 +146,9 @@ int uid_stat_tcp_snd(uid_t uid, int size) { int uid_stat_tcp_rcv(uid_t uid, int size) { struct uid_stat *entry; activity_stats_update(); - if ((entry = find_uid_stat(uid)) == NULL && - ((entry = create_stat(uid)) == NULL)) { - return -1; - } + entry = find_or_create_uid_stat(uid); + if (!entry) + return -1; atomic_add(size, &entry->tcp_rcv); return 0; } From 5b3988690975f78d064e24b8ea786c2b7a0673e9 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Wed, 8 May 2013 14:50:11 -0700 Subject: [PATCH 241/358] android: configs: Add Logitech unifying receivers to recommended Change-Id: I7647cd7037731df69dfdd513a0808b396d9d5bdd Signed-off-by: Michael Wright --- android/configs/android-recommended.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index 6a4d421c9fed..03e0c9139968 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -66,6 +66,7 @@ CONFIG_HID_TWINHAN=y CONFIG_HID_KENSINGTON=y CONFIG_HID_LCPOWER=y CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y CONFIG_LOGITECH_FF=y CONFIG_LOGIRUMBLEPAD2_FF=y CONFIG_LOGIG940_FF=y From a25b7e549e42eb7071bc7fd3a4ba953ee3ca9837 Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Fri, 31 May 2013 11:54:35 -0700 Subject: [PATCH 242/358] android: configs: Enable KSM support by default This will be a noop unless the KSM thread is enabled by userspace Change-Id: Ia5fde14504cc0da50522e2f875d8d021f9e054ba Signed-off-by: Rom Lemarchand --- android/configs/android-recommended.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index 03e0c9139968..c3eee848689c 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -2,6 +2,7 @@ CONFIG_PANIC_TIMEOUT=5 CONFIG_KALLSYMS_ALL=y CONFIG_PERF_EVENTS=y CONFIG_COMPACTION=y +CONFIG_KSM=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_PM_WAKELOCKS_LIMIT=0 # CONFIG_PM_WAKELOCKS_GC is not set From 22375c4d57693accdbdbc1b1a7570bad50f012f6 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 7 Jun 2013 11:56:34 -0700 Subject: [PATCH 243/358] android: configs: Reorder config fragments Because there is not tool to consistently generate these config fragments, lets keep the alphabetical instead of random. Change-Id: I0f098f6be6bdd272544295a3d5a48d04411e4514 Signed-off-by: JP Abgrall --- android/configs/android-base.cfg | 182 ++++++++++++------------ android/configs/android-recommended.cfg | 128 ++++++++--------- 2 files changed, 155 insertions(+), 155 deletions(-) diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index 664797780529..1969f30c0f79 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -1,63 +1,60 @@ -CONFIG_EXPERIMENTAL=y -CONFIG_SYSVIPC=y +# CONFIG_INET_LRO is not set +# CONFIG_OABI_COMPAT is not set +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_INTF_ALARM_DEV=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ASHMEM=y +CONFIG_BLK_DEV_DM=y +CONFIG_BLK_DEV_INITRD=y CONFIG_CGROUPS=y +CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y -CONFIG_CGROUP_CPUACCT=y -CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_SCHED=y -CONFIG_RT_GROUP_SCHED=y -CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_PREEMPT=y -CONFIG_PM_AUTOSLEEP=y -CONFIG_PM_WAKELOCKS=y -CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y -# CONFIG_OABI_COMPAT is not set -CONFIG_NET=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_XFRM_USER=y -CONFIG_NET_KEY=y -CONFIG_INET=y -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_IP_MULTIPLE_TABLES=y -CONFIG_INET_ESP=y -# CONFIG_INET_LRO is not set -CONFIG_IPV6_PRIVACY=y -CONFIG_IPV6_ROUTER_PREF=y -CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_EMBEDDED=y +CONFIG_EXPERIMENTAL=y +CONFIG_FB=y +CONFIG_HIGH_RES_TIMERS=y CONFIG_INET6_AH=y CONFIG_INET6_ESP=y CONFIG_INET6_IPCOMP=y +CONFIG_INET=y +CONFIG_INET_ESP=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_TARGET_REJECT_SKERR=y CONFIG_IPV6_MIP6=y CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_REJECT_SKERR=y +CONFIG_NET=y +CONFIG_NETDEVICES=y CONFIG_NETFILTER=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_TPROXY=y -CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_MARK=y -CONFIG_NETFILTER_XT_TARGET_NFLOG=y -CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -CONFIG_NETFILTER_XT_TARGET_TPROXY=y -CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -69,67 +66,70 @@ CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_QTAGUID=y -CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y -CONFIG_NF_CONNTRACK_IPV4=y -CONFIG_IP_NF_IPTABLES=y -CONFIG_IP_NF_MATCH_AH=y -CONFIG_IP_NF_MATCH_ECN=y -CONFIG_IP_NF_MATCH_TTL=y -CONFIG_IP_NF_FILTER=y -CONFIG_IP_NF_TARGET_REJECT=y -CONFIG_IP_NF_TARGET_REJECT_SKERR=y -CONFIG_NF_NAT=y -CONFIG_IP_NF_TARGET_MASQUERADE=y -CONFIG_IP_NF_TARGET_NETMAP=y -CONFIG_IP_NF_TARGET_REDIRECT=y -CONFIG_IP_NF_MANGLE=y -CONFIG_IP_NF_RAW=y -CONFIG_IP_NF_ARPTABLES=y -CONFIG_IP_NF_ARPFILTER=y -CONFIG_IP_NF_ARP_MANGLE=y -CONFIG_NF_CONNTRACK_IPV6=y -CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_FILTER=y -CONFIG_IP6_NF_TARGET_REJECT=y -CONFIG_IP6_NF_TARGET_REJECT_SKERR=y -CONFIG_IP6_NF_MANGLE=y -CONFIG_IP6_NF_RAW=y -CONFIG_NET_SCHED=y -CONFIG_NET_SCH_HTB=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NET_CLS_ACT=y CONFIG_NET_CLS_U32=y CONFIG_NET_EMATCH=y CONFIG_NET_EMATCH_U32=y -CONFIG_NET_CLS_ACT=y -CONFIG_NETDEVICES=y -CONFIG_TUN=y +CONFIG_NET_KEY=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_NAT=y +CONFIG_NO_HZ=y +CONFIG_PACKET=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y CONFIG_PPP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y CONFIG_PPP_BSDCOMP=y CONFIG_PPP_DEFLATE=y CONFIG_PPP_MPPE=y -CONFIG_PPPOLAC=y -CONFIG_PPPOPNS=y -CONFIG_FB=y +CONFIG_PREEMPT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_RTC_CLASS=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_STAGING=y +CONFIG_SWITCH=y CONFIG_SYNC=y +CONFIG_SYSVIPC=y +CONFIG_TUN=y +CONFIG_UNIX=y CONFIG_USB_GADGET=y CONFIG_USB_G_ANDROID=y CONFIG_USB_OTG_WAKELOCK=y -CONFIG_SWITCH=y -CONFIG_RTC_CLASS=y -CONFIG_STAGING=y -CONFIG_ANDROID=y -CONFIG_ANDROID_BINDER_IPC=y -CONFIG_ASHMEM=y -CONFIG_ANDROID_LOGGER=y -CONFIG_ANDROID_LOW_MEMORY_KILLER=y -CONFIG_ANDROID_INTF_ALARM_DEV=y +CONFIG_XFRM_USER=y diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index c3eee848689c..175f7c2c1877 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -2,47 +2,24 @@ CONFIG_PANIC_TIMEOUT=5 CONFIG_KALLSYMS_ALL=y CONFIG_PERF_EVENTS=y CONFIG_COMPACTION=y -CONFIG_KSM=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_INPUT_MOUSE is not set +# CONFIG_LEGACY_PTYS is not set # CONFIG_PM_WAKELOCKS_GC is not set -CONFIG_PM_RUNTIME=y -CONFIG_PM_DEBUG=y -CONFIG_SUSPEND_TIME=y +# CONFIG_VT is not set +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BATTERY_ANDROID=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_UID_STAT=y -CONFIG_MD=y CONFIG_DM_UEVENT=y -CONFIG_INPUT_EVDEV=y -CONFIG_INPUT_KEYRESET=y -# CONFIG_INPUT_MOUSE is not set -CONFIG_INPUT_JOYSTICK=y -CONFIG_JOYSTICK_XPAD=y -CONFIG_JOYSTICK_XPAD_FF=y -CONFIG_JOYSTICK_XPAD_LEDS=y -CONFIG_INPUT_TABLET=y -CONFIG_TABLET_USB_ACECAD=y -CONFIG_TABLET_USB_AIPTEK=y -CONFIG_TABLET_USB_GTCO=y -CONFIG_TABLET_USB_HANWANG=y -CONFIG_TABLET_USB_KBTAB=y -CONFIG_TABLET_USB_WACOM=y -CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y -CONFIG_INPUT_UINPUT=y -CONFIG_INPUT_GPIO=y -# CONFIG_VT is not set -# CONFIG_LEGACY_PTYS is not set -CONFIG_POWER_SUPPLY=y -CONFIG_BATTERY_ANDROID=y -CONFIG_MEDIA_SUPPORT=y -CONFIG_BACKLIGHT_LCD_SUPPORT=y -CONFIG_SOUND=y -CONFIG_SND=y -CONFIG_UHID=y -CONFIG_USB_HIDDEV=y +CONFIG_DRAGONRISE_FF=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FUSE_FS=y +CONFIG_GREENASIA_FF=y CONFIG_HID_A4TECH=y CONFIG_HID_ACRUX=y CONFIG_HID_ACRUX_FF=y @@ -50,27 +27,20 @@ CONFIG_HID_APPLE=y CONFIG_HID_BELKIN=y CONFIG_HID_CHERRY=y CONFIG_HID_CHICONY=y -CONFIG_HID_PRODIKEYS=y CONFIG_HID_CYPRESS=y CONFIG_HID_DRAGONRISE=y -CONFIG_DRAGONRISE_FF=y -CONFIG_HID_EMS_FF=y CONFIG_HID_ELECOM=y +CONFIG_HID_EMS_FF=y CONFIG_HID_EZKEY=y +CONFIG_HID_GREENASIA=y +CONFIG_HID_GYRATION=y CONFIG_HID_HOLTEK=y +CONFIG_HID_KENSINGTON=y CONFIG_HID_KEYTOUCH=y CONFIG_HID_KYE=y -CONFIG_HID_UCLOGIC=y -CONFIG_HID_WALTOP=y -CONFIG_HID_GYRATION=y -CONFIG_HID_TWINHAN=y -CONFIG_HID_KENSINGTON=y CONFIG_HID_LCPOWER=y CONFIG_HID_LOGITECH=y CONFIG_HID_LOGITECH_DJ=y -CONFIG_LOGITECH_FF=y -CONFIG_LOGIRUMBLEPAD2_FF=y -CONFIG_LOGIG940_FF=y CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y CONFIG_HID_MONTEREY=y @@ -78,40 +48,70 @@ CONFIG_HID_MULTITOUCH=y CONFIG_HID_NTRIG=y CONFIG_HID_ORTEK=y CONFIG_HID_PANTHERLORD=y -CONFIG_PANTHERLORD_FF=y CONFIG_HID_PETALYNX=y CONFIG_HID_PICOLCD=y CONFIG_HID_PRIMAX=y +CONFIG_HID_PRODIKEYS=y CONFIG_HID_ROCCAT=y CONFIG_HID_SAITEK=y CONFIG_HID_SAMSUNG=y +CONFIG_HID_SMARTJOYPLUS=y CONFIG_HID_SONY=y CONFIG_HID_SPEEDLINK=y CONFIG_HID_SUNPLUS=y -CONFIG_HID_GREENASIA=y -CONFIG_GREENASIA_FF=y -CONFIG_HID_SMARTJOYPLUS=y -CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_THRUSTMASTER=y CONFIG_HID_TIVO=y CONFIG_HID_TOPSEED=y -CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_UCLOGIC=y CONFIG_HID_WACOM=y +CONFIG_HID_WALTOP=y CONFIG_HID_WIIMOTE=y CONFIG_HID_ZEROPLUS=y CONFIG_HID_ZYDACRON=y -CONFIG_USB_USBNET=y -CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -CONFIG_USB_EHCI_HCD=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_GPIO=y +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_KEYRESET=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_TABLET=y +CONFIG_INPUT_UINPUT=y CONFIG_ION=y -CONFIG_ANDROID_RAM_CONSOLE=y -CONFIG_ANDROID_TIMED_GPIO=y -CONFIG_EXT4_FS=y -CONFIG_EXT4_FS_SECURITY=y -CONFIG_FUSE_FS=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_KSM=y +CONFIG_LOGIG940_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGITECH_FF=y +CONFIG_MD=y +CONFIG_MEDIA_SUPPORT=y CONFIG_MSDOS_FS=y -CONFIG_VFAT_FS=y +CONFIG_PANTHERLORD_FF=y +CONFIG_PM_DEBUG=y +CONFIG_PM_RUNTIME=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +CONFIG_POWER_SUPPLY=y +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_TRACER=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_SND=y +CONFIG_SOUND=y +CONFIG_SUSPEND_TIME=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_TABLET_USB_WACOM=y +CONFIG_TIMER_STATS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y -CONFIG_SCHEDSTATS=y -CONFIG_TIMER_STATS=y -CONFIG_SCHED_TRACER=y +CONFIG_UHID=y +CONFIG_UID_STAT=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_USBNET=y +CONFIG_VFAT_FS=y From f5651a4f67db8f9bd0a256fd36b26466085fee7d Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 7 Jun 2013 15:23:31 -0700 Subject: [PATCH 244/358] android: configs: no MODULES for base, no SIP for recommended We don't like CONFIG_MODULES anymore. Connection tracker handling of large SIP fails. Change-Id: Ie3c65aefcc6181752d6656c97e63035e5b5653ff Signed-off-by: JP Abgrall --- android/configs/android-base.cfg | 1 + android/configs/android-recommended.cfg | 1 + 2 files changed, 2 insertions(+) diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index 1969f30c0f79..2b98436550cf 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -1,4 +1,5 @@ # CONFIG_INET_LRO is not set +# CONFIG_MODULES is not set # CONFIG_OABI_COMPAT is not set CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index 175f7c2c1877..ca3cc8613366 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -5,6 +5,7 @@ CONFIG_COMPACTION=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set # CONFIG_INPUT_MOUSE is not set # CONFIG_LEGACY_PTYS is not set +# CONFIG_NF_CONNTRACK_SIP is not set # CONFIG_PM_WAKELOCKS_GC is not set # CONFIG_VT is not set CONFIG_ANDROID_RAM_CONSOLE=y From 88e6612ab55ca324276cfce7a0862aa2b50bf76b Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 22 Apr 2013 16:44:58 -0700 Subject: [PATCH 245/358] cpufreq: interactive: handle errors from cpufreq_frequency_table_target Add checks for error return from cpufreq_frequency_table_target, and be less noisy on the existing call with an error check. CPU hotplug and system shutdown may cause this call to return -EINVAL. Bug: 8613560 Change-Id: Id78d8829920462c0db1c7e14e717d91740d6cb44 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index baf8740f1677..1b5d9301e2d7 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -246,9 +246,10 @@ static unsigned int choose_freq( * than or equal to the target load. */ - cpufreq_frequency_table_target( - pcpu->policy, pcpu->freq_table, loadadjfreq / tl, - CPUFREQ_RELATION_L, &index); + if (cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, loadadjfreq / tl, + CPUFREQ_RELATION_L, &index)) + break; freq = pcpu->freq_table[index].frequency; if (freq > prevfreq) { @@ -260,10 +261,11 @@ static unsigned int choose_freq( * Find the highest frequency that is less * than freqmax. */ - cpufreq_frequency_table_target( - pcpu->policy, pcpu->freq_table, - freqmax - 1, CPUFREQ_RELATION_H, - &index); + if (cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmax - 1, CPUFREQ_RELATION_H, + &index)) + break; freq = pcpu->freq_table[index].frequency; if (freq == freqmin) { @@ -286,10 +288,11 @@ static unsigned int choose_freq( * Find the lowest frequency that is higher * than freqmin. */ - cpufreq_frequency_table_target( - pcpu->policy, pcpu->freq_table, - freqmin + 1, CPUFREQ_RELATION_L, - &index); + if (cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmin + 1, CPUFREQ_RELATION_L, + &index)) + break; freq = pcpu->freq_table[index].frequency; /* @@ -388,11 +391,8 @@ static void cpufreq_interactive_timer(unsigned long data) if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_L, - &index)) { - pr_warn_once("timer %d: cpufreq_frequency_table_target error\n", - (int) data); + &index)) goto rearm; - } new_freq = pcpu->freq_table[index].frequency; From fd74fd8323e8ed51b5f03da371670870beefb199 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 5 Apr 2013 13:25:21 -0700 Subject: [PATCH 246/358] cpufreq: interactive: reduce chance of zero time delta on load eval Reschedule load sampling timer after timestamp of sample start taken, hold spinlock across entire sequence to avoid preemption. Avoid the WARN for zero time delta in the load sampling timer function. Change-Id: Idc10a756f09141decb6df92669521a1ebf0dbc10 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 1b5d9301e2d7..b4d12b266c64 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -169,21 +169,23 @@ static inline cputime64_t get_cpu_idle_time(unsigned int cpu, static void cpufreq_interactive_timer_resched( struct cpufreq_interactive_cpuinfo *pcpu) { - unsigned long expires = jiffies + usecs_to_jiffies(timer_rate); + unsigned long expires; unsigned long flags; - mod_timer_pinned(&pcpu->cpu_timer, expires); - if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { - expires += usecs_to_jiffies(timer_slack_val); - mod_timer_pinned(&pcpu->cpu_slack_timer, expires); - } - spin_lock_irqsave(&pcpu->load_lock, flags); pcpu->time_in_idle = get_cpu_idle_time(smp_processor_id(), &pcpu->time_in_idle_timestamp); pcpu->cputime_speedadj = 0; pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; + expires = jiffies + usecs_to_jiffies(timer_rate); + mod_timer_pinned(&pcpu->cpu_timer, expires); + + if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { + expires += usecs_to_jiffies(timer_slack_val); + mod_timer_pinned(&pcpu->cpu_slack_timer, expires); + } + spin_unlock_irqrestore(&pcpu->load_lock, flags); } From 8c0d99f25ac3b202dd52d4591541ee63d56761cd Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Tue, 23 Apr 2013 22:32:01 +0900 Subject: [PATCH 247/358] cpufreq: interactive: avoid underflow on active time calculation Check for idle time delta less than elapsed time delta, avoid underflow computing active time. Change-Id: I3e4c6ef1ad794eec49ed379c0c50fa727fd6ad28 Signed-off-by: Minsung Kim --- drivers/cpufreq/cpufreq_interactive.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index b4d12b266c64..7303f50a1180 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -325,7 +325,12 @@ static u64 update_load(int cpu) now_idle = get_cpu_idle_time(cpu, &now); delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); - active_time = delta_time - delta_idle; + + if (delta_time <= delta_idle) + active_time = 0; + else + active_time = delta_time - delta_idle; + pcpu->cputime_speedadj += active_time * pcpu->policy->cur; pcpu->time_in_idle = now_idle; From 0939bc64adcfb660356953d616a9416640d02848 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:16 +0000 Subject: [PATCH 248/358] epoll: use freezable blocking call Avoid waking up every thread sleeping in an epoll_wait call during suspend and resume by calling a freezable blocking call. Previous patches modified the freezer to avoid sending wakeups to threads that are blocked in freezable blocking calls. This call was selected to be converted to a freezable call because it doesn't hold any locks or release any resources when interrupted that might be needed by another freezing task or a kernel driver during suspend, and is a common site where idle userspace tasks are blocked. Change-Id: I848d08d28c89302fd42bbbdfa76489a474ab27bf Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- fs/eventpoll.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index af903128891c..9aaf0e5a3ecb 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1637,7 +1638,8 @@ fetch_events: } spin_unlock_irqrestore(&ep->lock, flags); - if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS)) + if (!freezable_schedule_hrtimeout_range(to, slack, + HRTIMER_MODE_ABS)) timed_out = 1; spin_lock_irqsave(&ep->lock, flags); From 706ec89676ddaa848337bfe9e7d971b84395ab2d Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Thu, 16 May 2013 12:07:23 +0800 Subject: [PATCH 249/358] cpufreq: interactive: fix race on cpufreq TRANSITION notifier The cpufreq TRANSTION notifier callback does not check the governor_enabled state on affected CPUS, which will case kernel panic in update_load because the policy object maybe NULL or invalid when governor_enabled is false. Change-Id: Ie0f1718124f61e2f9b5da57abc6981ada5b83908 Signed-off-by: Lianwei Wang --- drivers/cpufreq/cpufreq_interactive.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 7303f50a1180..691923d6c868 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -634,9 +634,19 @@ static int cpufreq_interactive_notifier( for_each_cpu(cpu, pcpu->policy->cpus) { struct cpufreq_interactive_cpuinfo *pjcpu = &per_cpu(cpuinfo, cpu); + if (cpu != freq->cpu) { + if (!down_read_trylock(&pjcpu->enable_sem)) + continue; + if (!pjcpu->governor_enabled) { + up_read(&pjcpu->enable_sem); + continue; + } + } spin_lock_irqsave(&pjcpu->load_lock, flags); update_load(cpu); spin_unlock_irqrestore(&pjcpu->load_lock, flags); + if (cpu != freq->cpu) + up_read(&pjcpu->enable_sem); } up_read(&pcpu->enable_sem); From e6b2223858c529764702740c1abfb2190541ff06 Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Fri, 26 Apr 2013 13:30:51 +0800 Subject: [PATCH 250/358] cpufreq: interactive: resched timer if max freq raised When the policy max freq is raised, and before the timer is rescheduled in idle callback, the cpu freq may stuck at a lower freq. The target_freq shall be updated too, else on a high load situation, the new_freq is always equal to target_freq and which will cause freq stuck at a lower freq too. Reschedule the timer on gov limits callback. Change-Id: I6c187001ab43e859731429b64f75a74eebc37a24 Signed-off-by: Lianwei Wang --- drivers/cpufreq/cpufreq_interactive.c | 64 ++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 691923d6c868..90958fdd64dc 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -189,6 +189,32 @@ static void cpufreq_interactive_timer_resched( spin_unlock_irqrestore(&pcpu->load_lock, flags); } +/* The caller shall take enable_sem write semaphore to avoid any timer race. + * The cpu_timer and cpu_slack_timer must be deactivated when calling this + * function. + */ +static void cpufreq_interactive_timer_start(int cpu) +{ + struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); + unsigned long expires = jiffies + usecs_to_jiffies(timer_rate); + unsigned long flags; + + pcpu->cpu_timer.expires = expires; + add_timer_on(&pcpu->cpu_timer, cpu); + if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { + expires += usecs_to_jiffies(timer_slack_val); + pcpu->cpu_slack_timer.expires = expires; + add_timer_on(&pcpu->cpu_slack_timer, cpu); + } + + spin_lock_irqsave(&pcpu->load_lock, flags); + pcpu->time_in_idle = + get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp); + pcpu->cputime_speedadj = 0; + pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; + spin_unlock_irqrestore(&pcpu->load_lock, flags); +} + static unsigned int freq_to_above_hispeed_delay(unsigned int freq) { int i; @@ -1058,8 +1084,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, hispeed_freq = policy->max; for_each_cpu(j, policy->cpus) { - unsigned long expires; - pcpu = &per_cpu(cpuinfo, j); pcpu->policy = policy; pcpu->target_freq = policy->cur; @@ -1070,14 +1094,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->hispeed_validate_time = pcpu->floor_validate_time; down_write(&pcpu->enable_sem); - expires = jiffies + usecs_to_jiffies(timer_rate); - pcpu->cpu_timer.expires = expires; - add_timer_on(&pcpu->cpu_timer, j); - if (timer_slack_val >= 0) { - expires += usecs_to_jiffies(timer_slack_val); - pcpu->cpu_slack_timer.expires = expires; - add_timer_on(&pcpu->cpu_slack_timer, j); - } + cpufreq_interactive_timer_start(j); pcpu->governor_enabled = 1; up_write(&pcpu->enable_sem); } @@ -1136,6 +1153,33 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, else if (policy->min > policy->cur) __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + + /* hold write semaphore to avoid race */ + down_write(&pcpu->enable_sem); + if (pcpu->governor_enabled == 0) { + up_write(&pcpu->enable_sem); + continue; + } + + /* update target_freq firstly */ + 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. + * 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(j); + up_write(&pcpu->enable_sem); + } break; } return 0; From d0e52b6d3b4aed6b4c235de42117838a9395315f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 26 Nov 2012 16:09:13 -0800 Subject: [PATCH 251/358] usb: otg: otg-wakelock: Fix build for 3.7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- drivers/usb/phy/otg-wakelock.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/usb/phy/otg-wakelock.c b/drivers/usb/phy/otg-wakelock.c index e17e27299062..479376bfa484 100644 --- a/drivers/usb/phy/otg-wakelock.c +++ b/drivers/usb/phy/otg-wakelock.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -138,13 +139,15 @@ MODULE_PARM_DESC(enabled, "enable wakelock when VBUS present"); static int __init otg_wakelock_init(void) { int ret; + struct usb_phy *phy; - otgwl_xceiv = usb_get_transceiver(); + phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (!otgwl_xceiv) { + if (IS_ERR(phy)) { pr_err("%s: No USB transceiver found\n", __func__); - return -ENODEV; + return PTR_ERR(phy); } + otgwl_xceiv = phy; snprintf(vbus_lock.name, sizeof(vbus_lock.name), "vbus-%s", dev_name(otgwl_xceiv->dev)); From 923be37aa87fcc257040ff830676c0e867b00d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 26 Nov 2012 16:23:33 -0800 Subject: [PATCH 252/358] ARM: fiq_debugger: Fix to compile on 3.7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use for_each_irq_desc in arch/arm/common/fiq_debugger.c Signed-off-by: Arve Hjønnevåg --- arch/arm/common/fiq_debugger.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index 053680b6c326..5e9005bfdbb9 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -374,16 +374,17 @@ static void dump_allregs(struct fiq_debugger_state *state, unsigned *regs) static void dump_irqs(struct fiq_debugger_state *state) { int n; + struct irq_desc *desc; debug_printf(state, "irqnr total since-last status name\n"); - for (n = 0; n < NR_IRQS; n++) { - struct irqaction *act = irq_desc[n].action; + for_each_irq_desc(n, desc) { + struct irqaction *act = desc->action; if (!act && !kstat_irqs(n)) continue; debug_printf(state, "%5d: %10u %11u %8x %s\n", n, kstat_irqs(n), kstat_irqs(n) - state->last_irqs[n], - irq_desc[n].status_use_accessors, + desc->status_use_accessors, (act && act->name) ? act->name : "???"); state->last_irqs[n] = kstat_irqs(n); } From fc75780f290156fe29d18aba126e0d6aaa9861c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 26 Nov 2012 20:05:37 -0800 Subject: [PATCH 253/358] ARM: fiq_debugger: Use kmsg_dumper to dump kernel logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/common/fiq_debugger.c | 40 +++++++++------------------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index 5e9005bfdbb9..eabd94b98f4b 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -207,29 +208,19 @@ static void debug_prompt(struct fiq_debugger_state *state) debug_puts(state, "debug> "); } -int log_buf_copy(char *dest, int idx, int len); static void dump_kernel_log(struct fiq_debugger_state *state) { - char buf[1024]; - int idx = 0; - int ret; - int saved_oip; + char buf[512]; + size_t len; + struct kmsg_dumper dumper = { .active = true }; - /* setting oops_in_progress prevents log_buf_copy() - * from trying to take a spinlock which will make it - * very unhappy in some cases... - */ - saved_oip = oops_in_progress; - oops_in_progress = 1; - for (;;) { - ret = log_buf_copy(buf, idx, 1023); - if (ret <= 0) - break; - buf[ret] = 0; + + kmsg_dump_rewind_nolock(&dumper); + while (kmsg_dump_get_line_nolock(&dumper, true, buf, + sizeof(buf) - 1, &len)) { + buf[len] = 0; debug_puts(state, buf); - idx += ret; } - oops_in_progress = saved_oip; } static char *mode_name(unsigned cpsr) @@ -523,18 +514,7 @@ static void begin_syslog_dump(struct fiq_debugger_state *state) static void end_syslog_dump(struct fiq_debugger_state *state) { - char buf[128]; - int ret; - int idx = 0; - - while (1) { - ret = log_buf_copy(buf, idx, sizeof(buf) - 1); - if (ret <= 0) - break; - buf[ret] = 0; - debug_printf(state, "%s", buf); - idx += ret; - } + dump_kernel_log(state); } #endif From 4d3efc79e0d00debc60ab11bf731769187410122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 15 Jan 2013 15:10:31 -0800 Subject: [PATCH 254/358] ARM: fiq_debugger: Update tty code for 3.9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/common/fiq_debugger.c | 63 ++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index eabd94b98f4b..65b943c76300 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -89,8 +89,7 @@ struct fiq_debugger_state { #ifdef CONFIG_FIQ_DEBUGGER_CONSOLE spinlock_t console_lock; struct console console; - struct tty_struct *tty; - int tty_open_count; + struct tty_port tty_port; struct fiq_debugger_ringbuf *tty_rbuf; bool syslog_dumping; #endif @@ -768,6 +767,22 @@ static irqreturn_t wakeup_irq_handler(int irq, void *dev) return IRQ_HANDLED; } +static void debug_handle_console_irq_context(struct fiq_debugger_state *state) +{ +#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) + if (state->tty_port.ops) { + int i; + int count = fiq_debugger_ringbuf_level(state->tty_rbuf); + for (i = 0; i < count; i++) { + int c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0); + tty_insert_flip_char(&state->tty_port, c, TTY_NORMAL); + if (!fiq_debugger_ringbuf_consume(state->tty_rbuf, 1)) + pr_warn("fiq tty failed to consume byte\n"); + } + tty_flip_buffer_push(&state->tty_port); + } +#endif +} static void debug_handle_irq_context(struct fiq_debugger_state *state) { @@ -779,19 +794,7 @@ static void debug_handle_irq_context(struct fiq_debugger_state *state) mod_timer(&state->sleep_timer, jiffies + HZ * 5); spin_unlock_irqrestore(&state->sleep_timer_lock, flags); } -#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) - if (state->tty) { - int i; - int count = fiq_debugger_ringbuf_level(state->tty_rbuf); - for (i = 0; i < count; i++) { - int c = fiq_debugger_ringbuf_peek(state->tty_rbuf, 0); - tty_insert_flip_char(state->tty, c, TTY_NORMAL); - if (!fiq_debugger_ringbuf_consume(state->tty_rbuf, 1)) - pr_warn("fiq tty failed to consume byte\n"); - } - tty_flip_buffer_push(state->tty); - } -#endif + debug_handle_console_irq_context(state); if (state->debug_busy) { debug_irq_exec(state, state->debug_cmd); if (!state->console_enable) @@ -995,26 +998,21 @@ int fiq_tty_open(struct tty_struct *tty, struct file *filp) int line = tty->index; struct fiq_debugger_state **states = tty->driver->driver_state; struct fiq_debugger_state *state = states[line]; - if (state->tty_open_count++) - return 0; - tty->driver_data = state; - state->tty = tty; - return 0; + return tty_port_open(&state->tty_port, tty, filp); } void fiq_tty_close(struct tty_struct *tty, struct file *filp) { - struct fiq_debugger_state *state = tty->driver_data; - if (--state->tty_open_count) - return; - state->tty = NULL; + tty_port_close(tty->port, tty, filp); } int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { int i; - struct fiq_debugger_state *state = tty->driver_data; + int line = tty->index; + struct fiq_debugger_state **states = tty->driver->driver_state; + struct fiq_debugger_state *state = states[line]; if (!state->console_enable) return count; @@ -1042,7 +1040,8 @@ static int fiq_tty_poll_init(struct tty_driver *driver, int line, char *options) static int fiq_tty_poll_get_char(struct tty_driver *driver, int line) { - struct fiq_debugger_state *state = driver->ttys[line]->driver_data; + struct fiq_debugger_state **states = driver->driver_state; + struct fiq_debugger_state *state = states[line]; int c = NO_POLL_CHAR; debug_uart_enable(state); @@ -1064,13 +1063,16 @@ static int fiq_tty_poll_get_char(struct tty_driver *driver, int line) static void fiq_tty_poll_put_char(struct tty_driver *driver, int line, char ch) { - struct fiq_debugger_state *state = driver->ttys[line]->driver_data; + struct fiq_debugger_state **states = driver->driver_state; + struct fiq_debugger_state *state = states[line]; debug_uart_enable(state); debug_putc(state, ch); debug_uart_disable(state); } #endif +static const struct tty_port_operations fiq_tty_port_ops; + static const struct tty_operations fiq_tty_driver_ops = { .write = fiq_tty_write, .write_room = fiq_tty_write_room, @@ -1150,8 +1152,11 @@ static int fiq_debugger_tty_init_one(struct fiq_debugger_state *state) goto err; } - tty_dev = tty_register_device(fiq_tty_driver, state->pdev->id, - &state->pdev->dev); + tty_port_init(&state->tty_port); + state->tty_port.ops = &fiq_tty_port_ops; + + tty_dev = tty_port_register_device(&state->tty_port, fiq_tty_driver, + state->pdev->id, &state->pdev->dev); if (IS_ERR(tty_dev)) { pr_err("Failed to register fiq debugger tty device\n"); ret = PTR_ERR(tty_dev); From 1eaf3d3d963ff6a1fecc987f3c727acd136411e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 26 Nov 2012 19:30:22 -0800 Subject: [PATCH 255/358] mmc: block: Remove call to mmc_blk_set_blksize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It no longer exists. Signed-off-by: Arve Hjønnevåg --- drivers/mmc/card/block.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 499ab365600d..feb9bf5e4c17 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1957,9 +1957,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) return 0; } -static int -mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card); - static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { int ret; @@ -1970,10 +1967,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) unsigned int cmd_flags = req ? req->cmd_flags : 0; #ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME - if (mmc_bus_needs_resume(card->host)) { + if (mmc_bus_needs_resume(card->host)) mmc_resume_bus(card->host); - mmc_blk_set_blksize(md, card); - } #endif if (req && !mq->mqrq_prev->req) From fd4cd0375b86232124b1041dc9a874f147f3b066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 30 Nov 2012 17:05:40 -0800 Subject: [PATCH 256/358] ARM: decompressor: Flush tlb before swiching domain 0 to client mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the bootloader used a page table that is incompatible with domain 0 in client mode, and boots with the mmu on, then swithing domain 0 to client mode causes a fault if we don't flush the tlb after updating the page table pointer. v2: Add ISB before loading dacr. Signed-off-by: Arve Hjønnevåg --- arch/arm/boot/compressed/head.S | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 066b03480b63..2a2dff9daa36 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -734,6 +734,8 @@ __armv7_mmu_cache_on: bic r6, r6, #1 << 31 @ 32-bit translation system bic r6, r6, #3 << 0 @ use only ttbr0 mcrne p15, 0, r3, c2, c0, 0 @ load page table pointer + mcrne p15, 0, r0, c8, c7, 0 @ flush I,D TLBs + mcr p15, 0, r0, c7, c5, 4 @ ISB mcrne p15, 0, r1, c3, c0, 0 @ load domain access control mcrne p15, 0, r6, c2, c0, 2 @ load ttb control #endif From d08dcd312645cf98272d01d88c8be34082c4aee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 27 Nov 2012 19:29:04 -0800 Subject: [PATCH 257/358] usb: gadget: android: Fixes and hacks to make android usb gadget compile on 3.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- drivers/usb/gadget/android.c | 16 +++++----------- drivers/usb/gadget/f_accessory.c | 2 +- drivers/usb/gadget/f_adb.c | 2 +- drivers/usb/gadget/f_audio_source.c | 2 +- drivers/usb/gadget/f_mtp.c | 4 ++-- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 32679eec28ed..52e7f231cb2d 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -367,11 +367,14 @@ static void functionfs_closed_callback(struct ffs_data *ffs) mutex_unlock(&dev->mutex); } -static int functionfs_check_dev_callback(const char *dev_name) +static void *functionfs_acquire_dev_callback(const char *dev_name) { return 0; } +static void functionfs_release_dev_callback(struct ffs_data *ffs_data) +{ +} struct adb_data { bool opened; @@ -1362,7 +1365,7 @@ static int android_bind(struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; struct usb_gadget *gadget = cdev->gadget; - int gcnum, id, ret; + int id, ret; /* * Start disconnected. Userspace will connect the gadget once @@ -1400,15 +1403,6 @@ static int android_bind(struct usb_composite_dev *cdev) strings_dev[STRING_SERIAL_IDX].id = id; device_desc.iSerialNumber = id; - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - else { - pr_warning("%s: controller '%s' not recognized\n", - longname, gadget->name); - device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); - } - usb_gadget_set_selfpowered(gadget); dev->cdev = cdev; diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index a244265c1143..092964c2b506 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -1121,7 +1121,7 @@ static int acc_bind_config(struct usb_configuration *c) dev->cdev = c->cdev; dev->function.name = "accessory"; dev->function.strings = acc_strings, - dev->function.descriptors = fs_acc_descs; + dev->function.fs_descriptors = fs_acc_descs; dev->function.hs_descriptors = hs_acc_descs; dev->function.bind = acc_function_bind; dev->function.unbind = acc_function_unbind; diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c index 1629ffb5b979..a1d70d276953 100644 --- a/drivers/usb/gadget/f_adb.c +++ b/drivers/usb/gadget/f_adb.c @@ -566,7 +566,7 @@ static int adb_bind_config(struct usb_configuration *c) dev->cdev = c->cdev; dev->function.name = "adb"; - dev->function.descriptors = fs_adb_descs; + dev->function.fs_descriptors = fs_adb_descs; dev->function.hs_descriptors = hs_adb_descs; dev->function.bind = adb_function_bind; dev->function.unbind = adb_function_unbind; diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index c757409edf94..56dcf217cfe5 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -599,7 +599,7 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) hs_as_in_ep_desc.bEndpointAddress = fs_as_in_ep_desc.bEndpointAddress; - f->descriptors = fs_audio_desc; + f->fs_descriptors = fs_audio_desc; f->hs_descriptors = hs_audio_desc; for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) { diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 1638977a5410..9ab94697c196 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -1212,10 +1212,10 @@ static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) dev->function.name = "mtp"; dev->function.strings = mtp_strings; if (ptp_config) { - dev->function.descriptors = fs_ptp_descs; + dev->function.fs_descriptors = fs_ptp_descs; dev->function.hs_descriptors = hs_ptp_descs; } else { - dev->function.descriptors = fs_mtp_descs; + dev->function.fs_descriptors = fs_mtp_descs; dev->function.hs_descriptors = hs_mtp_descs; } dev->function.bind = mtp_function_bind; From 490f769605f535472398bd1073fee0111625806f Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Thu, 1 Mar 2012 13:17:07 -0800 Subject: [PATCH 258/358] HACK: usb: gadget: Fix enumeration on boot The Android gadget driver disconnects the gadget on bind and expects the gadget to stay disconnected until it calls usb_gadget_connect when userspace is ready. Removed the call to usb_gadget_connect in usb_gadget_probe_driver to avoid enabling the pullup before userspace is ready. Change-Id: I63707ac6e16a44eca52351a4bf80407d25fbd35e Signed-off-by: Benoit Goby --- drivers/usb/gadget/udc-core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 27768a7d986a..ce42e45e99ab 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -351,7 +351,15 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri driver->unbind(udc->gadget); goto err1; } - usb_gadget_connect(udc->gadget); + /* + * HACK: The Android gadget driver disconnects the gadget + * on bind and expects the gadget to stay disconnected until + * it calls usb_gadget_connect when userspace is ready. Remove + * the call to usb_gadget_connect bellow to avoid enabling the + * pullup before userspace is ready. + * + * usb_gadget_connect(udc->gadget); + */ kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); return 0; From 83cdc3e22062408ba4c2bfa633805048297eeeed Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 5 Nov 2012 18:47:08 -0800 Subject: [PATCH 259/358] usb: gadget: Fix android gadget driver build Removed obsolete f_adb function Change-Id: Idfb4110429bc0ea63f493c68ad667f49ca471987 Signed-off-by: Benoit Goby --- drivers/usb/gadget/Kconfig | 7 + drivers/usb/gadget/android.c | 165 ++------- drivers/usb/gadget/f_adb.c | 619 ---------------------------------- drivers/usb/gadget/u_serial.c | 3 +- 4 files changed, 43 insertions(+), 751 deletions(-) delete mode 100644 drivers/usb/gadget/f_adb.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b7f516f68716..9e2ef763bfb7 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -1021,6 +1021,7 @@ config USB_G_PRINTER config USB_G_ANDROID boolean "Android Composite Gadget" + select USB_LIBCOMPOSITE help The Android Composite Gadget supports multiple USB functions: adb, acm, mass storage, mtp, accessory @@ -1028,6 +1029,12 @@ config USB_G_ANDROID Each function can be configured and enabled/disabled dynamically from userspace through a sysfs interface. +config USB_ANDROID_RNDIS_DWORD_ALIGNED + boolean "Use double word aligned" + depends on USB_G_ANDROID + help + Provides dword aligned for DMA controller. + if TTY config USB_CDC_COMPOSITE diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 52e7f231cb2d..e7a3807b2c83 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -30,24 +30,11 @@ #include "gadget_chips.h" -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#include "usbstring.c" -#include "config.c" -#include "epautoconf.c" -#include "composite.c" - #include "f_fs.c" #include "f_audio_source.c" #include "f_mass_storage.c" #include "u_serial.c" #include "f_acm.c" -#include "f_adb.c" #include "f_mtp.c" #include "f_accessory.c" #define USB_ETH_RNDIS y @@ -376,99 +363,6 @@ static void functionfs_release_dev_callback(struct ffs_data *ffs_data) { } -struct adb_data { - bool opened; - bool enabled; -}; - -static int -adb_function_init(struct android_usb_function *f, - struct usb_composite_dev *cdev) -{ - f->config = kzalloc(sizeof(struct adb_data), GFP_KERNEL); - if (!f->config) - return -ENOMEM; - - return adb_setup(); -} - -static void adb_function_cleanup(struct android_usb_function *f) -{ - adb_cleanup(); - kfree(f->config); -} - -static int -adb_function_bind_config(struct android_usb_function *f, - struct usb_configuration *c) -{ - return adb_bind_config(c); -} - -static void adb_android_function_enable(struct android_usb_function *f) -{ - struct android_dev *dev = _android_dev; - struct adb_data *data = f->config; - - data->enabled = true; - - /* Disable the gadget until adbd is ready */ - if (!data->opened) - android_disable(dev); -} - -static void adb_android_function_disable(struct android_usb_function *f) -{ - struct android_dev *dev = _android_dev; - struct adb_data *data = f->config; - - data->enabled = false; - - /* Balance the disable that was called in closed_callback */ - if (!data->opened) - android_enable(dev); -} - -static struct android_usb_function adb_function = { - .name = "adb", - .enable = adb_android_function_enable, - .disable = adb_android_function_disable, - .init = adb_function_init, - .cleanup = adb_function_cleanup, - .bind_config = adb_function_bind_config, -}; - -static void adb_ready_callback(void) -{ - struct android_dev *dev = _android_dev; - struct adb_data *data = adb_function.config; - - mutex_lock(&dev->mutex); - - data->opened = true; - - if (data->enabled) - android_enable(dev); - - mutex_unlock(&dev->mutex); -} - -static void adb_closed_callback(void) -{ - struct android_dev *dev = _android_dev; - struct adb_data *data = adb_function.config; - - mutex_lock(&dev->mutex); - - data->opened = false; - - if (data->enabled) - android_disable(dev); - - mutex_unlock(&dev->mutex); -} - - #define MAX_ACM_INSTANCES 4 struct acm_function_config { int instances; @@ -985,7 +879,6 @@ static struct android_usb_function audio_source_function = { static struct android_usb_function *supported_functions[] = { &ffs_function, - &adb_function, &acm_function, &mtp_function, &ptp_function, @@ -1418,13 +1311,8 @@ static int android_usb_unbind(struct usb_composite_dev *cdev) return 0; } -static struct usb_composite_driver android_usb_driver = { - .name = "android_usb", - .dev = &device_desc, - .strings = dev_strings, - .unbind = android_usb_unbind, - .max_speed = USB_SPEED_HIGH, -}; +/* HACK: android needs to override setup for accessory to work */ +static int (*composite_setup)(struct usb_gadget *gadget, const struct usb_ctrlrequest *c); static int android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) @@ -1437,7 +1325,6 @@ android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) unsigned long flags; req->zero = 0; - req->complete = composite_setup_complete; req->length = 0; gadget->ep0->driver_data = cdev; @@ -1471,25 +1358,30 @@ android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) return value; } -static void android_disconnect(struct usb_gadget *gadget) +static void android_disconnect(struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; - struct usb_composite_dev *cdev = get_gadget_data(gadget); - unsigned long flags; - composite_disconnect(gadget); /* accessory HID support can be active while the accessory function is not actually enabled, so we need to inform it when we are disconnected. */ acc_disconnect(); - spin_lock_irqsave(&cdev->lock, flags); dev->connected = 0; schedule_work(&dev->work); - spin_unlock_irqrestore(&cdev->lock, flags); } +static struct usb_composite_driver android_usb_driver = { + .name = "android_usb", + .dev = &device_desc, + .strings = dev_strings, + .bind = android_bind, + .unbind = android_usb_unbind, + .disconnect = android_disconnect, + .max_speed = USB_SPEED_HIGH, +}; + static int android_create_device(struct android_dev *dev) { struct device_attribute **attrs = android_usb_attributes; @@ -1524,8 +1416,10 @@ static int __init init(void) return PTR_ERR(android_class); dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; + if (!dev) { + err = -ENOMEM; + goto err_dev; + } dev->disable_depth = 1; dev->functions = supported_functions; @@ -1535,18 +1429,29 @@ static int __init init(void) err = android_create_device(dev); if (err) { - class_destroy(android_class); - kfree(dev); - return err; + pr_err("%s: failed to create android device %d", __func__, err); + goto err_create; } _android_dev = dev; - /* Override composite driver functions */ - composite_driver.setup = android_setup; - composite_driver.disconnect = android_disconnect; + err = usb_composite_probe(&android_usb_driver); + if (err) { + pr_err("%s: failed to probe driver %d", __func__, err); + goto err_create; + } - return usb_composite_probe(&android_usb_driver, android_bind); + /* HACK: exchange composite's setup with ours */ + composite_setup = android_usb_driver.gadget_driver.setup; + android_usb_driver.gadget_driver.setup = android_setup; + + return 0; + +err_create: + kfree(dev); +err_dev: + class_destroy(android_class); + return err; } module_init(init); diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c deleted file mode 100644 index a1d70d276953..000000000000 --- a/drivers/usb/gadget/f_adb.c +++ /dev/null @@ -1,619 +0,0 @@ -/* - * Gadget Driver for Android ADB - * - * Copyright (C) 2008 Google, Inc. - * Author: Mike Lockwood - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ADB_BULK_BUFFER_SIZE 4096 - -/* number of tx requests to allocate */ -#define TX_REQ_MAX 4 - -static const char adb_shortname[] = "android_adb"; - -struct adb_dev { - struct usb_function function; - struct usb_composite_dev *cdev; - spinlock_t lock; - - struct usb_ep *ep_in; - struct usb_ep *ep_out; - - int online; - int error; - - atomic_t read_excl; - atomic_t write_excl; - atomic_t open_excl; - - struct list_head tx_idle; - - wait_queue_head_t read_wq; - wait_queue_head_t write_wq; - struct usb_request *rx_req; - int rx_done; -}; - -static struct usb_interface_descriptor adb_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bNumEndpoints = 2, - .bInterfaceClass = 0xFF, - .bInterfaceSubClass = 0x42, - .bInterfaceProtocol = 1, -}; - -static struct usb_endpoint_descriptor adb_highspeed_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor adb_highspeed_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor adb_fullspeed_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_endpoint_descriptor adb_fullspeed_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -static struct usb_descriptor_header *fs_adb_descs[] = { - (struct usb_descriptor_header *) &adb_interface_desc, - (struct usb_descriptor_header *) &adb_fullspeed_in_desc, - (struct usb_descriptor_header *) &adb_fullspeed_out_desc, - NULL, -}; - -static struct usb_descriptor_header *hs_adb_descs[] = { - (struct usb_descriptor_header *) &adb_interface_desc, - (struct usb_descriptor_header *) &adb_highspeed_in_desc, - (struct usb_descriptor_header *) &adb_highspeed_out_desc, - NULL, -}; - -static void adb_ready_callback(void); -static void adb_closed_callback(void); - -/* temporary variable used between adb_open() and adb_gadget_bind() */ -static struct adb_dev *_adb_dev; - -static inline struct adb_dev *func_to_adb(struct usb_function *f) -{ - return container_of(f, struct adb_dev, function); -} - - -static struct usb_request *adb_request_new(struct usb_ep *ep, int buffer_size) -{ - struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (!req) - return NULL; - - /* now allocate buffers for the requests */ - req->buf = kmalloc(buffer_size, GFP_KERNEL); - if (!req->buf) { - usb_ep_free_request(ep, req); - return NULL; - } - - return req; -} - -static void adb_request_free(struct usb_request *req, struct usb_ep *ep) -{ - if (req) { - kfree(req->buf); - usb_ep_free_request(ep, req); - } -} - -static inline int adb_lock(atomic_t *excl) -{ - if (atomic_inc_return(excl) == 1) { - return 0; - } else { - atomic_dec(excl); - return -1; - } -} - -static inline void adb_unlock(atomic_t *excl) -{ - atomic_dec(excl); -} - -/* add a request to the tail of a list */ -void adb_req_put(struct adb_dev *dev, struct list_head *head, - struct usb_request *req) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - list_add_tail(&req->list, head); - spin_unlock_irqrestore(&dev->lock, flags); -} - -/* remove a request from the head of a list */ -struct usb_request *adb_req_get(struct adb_dev *dev, struct list_head *head) -{ - unsigned long flags; - struct usb_request *req; - - spin_lock_irqsave(&dev->lock, flags); - if (list_empty(head)) { - req = 0; - } else { - req = list_first_entry(head, struct usb_request, list); - list_del(&req->list); - } - spin_unlock_irqrestore(&dev->lock, flags); - return req; -} - -static void adb_complete_in(struct usb_ep *ep, struct usb_request *req) -{ - struct adb_dev *dev = _adb_dev; - - if (req->status != 0) - dev->error = 1; - - adb_req_put(dev, &dev->tx_idle, req); - - wake_up(&dev->write_wq); -} - -static void adb_complete_out(struct usb_ep *ep, struct usb_request *req) -{ - struct adb_dev *dev = _adb_dev; - - dev->rx_done = 1; - if (req->status != 0 && req->status != -ECONNRESET) - dev->error = 1; - - wake_up(&dev->read_wq); -} - -static int adb_create_bulk_endpoints(struct adb_dev *dev, - struct usb_endpoint_descriptor *in_desc, - struct usb_endpoint_descriptor *out_desc) -{ - struct usb_composite_dev *cdev = dev->cdev; - struct usb_request *req; - struct usb_ep *ep; - int i; - - DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); - - ep = usb_ep_autoconfig(cdev->gadget, in_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_in = ep; - - ep = usb_ep_autoconfig(cdev->gadget, out_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for adb ep_out got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_out = ep; - - /* now allocate requests for our endpoints */ - req = adb_request_new(dev->ep_out, ADB_BULK_BUFFER_SIZE); - if (!req) - goto fail; - req->complete = adb_complete_out; - dev->rx_req = req; - - for (i = 0; i < TX_REQ_MAX; i++) { - req = adb_request_new(dev->ep_in, ADB_BULK_BUFFER_SIZE); - if (!req) - goto fail; - req->complete = adb_complete_in; - adb_req_put(dev, &dev->tx_idle, req); - } - - return 0; - -fail: - printk(KERN_ERR "adb_bind() could not allocate requests\n"); - return -1; -} - -static ssize_t adb_read(struct file *fp, char __user *buf, - size_t count, loff_t *pos) -{ - struct adb_dev *dev = fp->private_data; - struct usb_request *req; - int r = count, xfer; - int ret; - - pr_debug("adb_read(%d)\n", count); - if (!_adb_dev) - return -ENODEV; - - if (count > ADB_BULK_BUFFER_SIZE) - return -EINVAL; - - if (adb_lock(&dev->read_excl)) - return -EBUSY; - - /* we will block until we're online */ - while (!(dev->online || dev->error)) { - pr_debug("adb_read: waiting for online state\n"); - ret = wait_event_interruptible(dev->read_wq, - (dev->online || dev->error)); - if (ret < 0) { - adb_unlock(&dev->read_excl); - return ret; - } - } - if (dev->error) { - r = -EIO; - goto done; - } - -requeue_req: - /* queue a request */ - req = dev->rx_req; - req->length = count; - dev->rx_done = 0; - ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC); - if (ret < 0) { - pr_debug("adb_read: failed to queue req %p (%d)\n", req, ret); - r = -EIO; - dev->error = 1; - goto done; - } else { - pr_debug("rx %p queue\n", req); - } - - /* wait for a request to complete */ - ret = wait_event_interruptible(dev->read_wq, dev->rx_done); - if (ret < 0) { - if (ret != -ERESTARTSYS) - dev->error = 1; - r = ret; - usb_ep_dequeue(dev->ep_out, req); - goto done; - } - if (!dev->error) { - /* 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); - xfer = (req->actual < count) ? req->actual : count; - if (copy_to_user(buf, req->buf, xfer)) - r = -EFAULT; - - } else - r = -EIO; - -done: - adb_unlock(&dev->read_excl); - pr_debug("adb_read returning %d\n", r); - return r; -} - -static ssize_t adb_write(struct file *fp, const char __user *buf, - size_t count, loff_t *pos) -{ - struct adb_dev *dev = fp->private_data; - struct usb_request *req = 0; - int r = count, xfer; - int ret; - - if (!_adb_dev) - return -ENODEV; - pr_debug("adb_write(%d)\n", count); - - if (adb_lock(&dev->write_excl)) - return -EBUSY; - - while (count > 0) { - if (dev->error) { - pr_debug("adb_write dev->error\n"); - r = -EIO; - break; - } - - /* get an idle tx request to use */ - req = 0; - ret = wait_event_interruptible(dev->write_wq, - (req = adb_req_get(dev, &dev->tx_idle)) || dev->error); - - if (ret < 0) { - r = ret; - break; - } - - if (req != 0) { - if (count > ADB_BULK_BUFFER_SIZE) - xfer = ADB_BULK_BUFFER_SIZE; - else - xfer = count; - if (copy_from_user(req->buf, buf, xfer)) { - r = -EFAULT; - break; - } - - req->length = xfer; - ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC); - if (ret < 0) { - pr_debug("adb_write: xfer error %d\n", ret); - dev->error = 1; - r = -EIO; - break; - } - - buf += xfer; - count -= xfer; - - /* zero this so we don't try to free it on error exit */ - req = 0; - } - } - - if (req) - adb_req_put(dev, &dev->tx_idle, req); - - adb_unlock(&dev->write_excl); - pr_debug("adb_write returning %d\n", r); - return r; -} - -static int adb_open(struct inode *ip, struct file *fp) -{ - pr_info("adb_open\n"); - if (!_adb_dev) - return -ENODEV; - - if (adb_lock(&_adb_dev->open_excl)) - return -EBUSY; - - fp->private_data = _adb_dev; - - /* clear the error latch */ - _adb_dev->error = 0; - - adb_ready_callback(); - - return 0; -} - -static int adb_release(struct inode *ip, struct file *fp) -{ - pr_info("adb_release\n"); - - adb_closed_callback(); - - adb_unlock(&_adb_dev->open_excl); - return 0; -} - -/* file operations for ADB device /dev/android_adb */ -static const struct file_operations adb_fops = { - .owner = THIS_MODULE, - .read = adb_read, - .write = adb_write, - .open = adb_open, - .release = adb_release, -}; - -static struct miscdevice adb_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = adb_shortname, - .fops = &adb_fops, -}; - - - - -static int -adb_function_bind(struct usb_configuration *c, struct usb_function *f) -{ - struct usb_composite_dev *cdev = c->cdev; - struct adb_dev *dev = func_to_adb(f); - int id; - int ret; - - dev->cdev = cdev; - DBG(cdev, "adb_function_bind dev: %p\n", dev); - - /* allocate interface ID(s) */ - id = usb_interface_id(c, f); - if (id < 0) - return id; - adb_interface_desc.bInterfaceNumber = id; - - /* allocate endpoints */ - ret = adb_create_bulk_endpoints(dev, &adb_fullspeed_in_desc, - &adb_fullspeed_out_desc); - if (ret) - return ret; - - /* support high speed hardware */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - adb_highspeed_in_desc.bEndpointAddress = - adb_fullspeed_in_desc.bEndpointAddress; - adb_highspeed_out_desc.bEndpointAddress = - adb_fullspeed_out_desc.bEndpointAddress; - } - - DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - f->name, dev->ep_in->name, dev->ep_out->name); - return 0; -} - -static void -adb_function_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct adb_dev *dev = func_to_adb(f); - struct usb_request *req; - - - dev->online = 0; - dev->error = 1; - - wake_up(&dev->read_wq); - - adb_request_free(dev->rx_req, dev->ep_out); - while ((req = adb_req_get(dev, &dev->tx_idle))) - adb_request_free(req, dev->ep_in); -} - -static int adb_function_set_alt(struct usb_function *f, - unsigned intf, unsigned alt) -{ - struct adb_dev *dev = func_to_adb(f); - struct usb_composite_dev *cdev = f->config->cdev; - int ret; - - DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt); - - ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); - if (ret) - return ret; - - ret = usb_ep_enable(dev->ep_in); - if (ret) - return ret; - - ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); - if (ret) - return ret; - - ret = usb_ep_enable(dev->ep_out); - if (ret) { - usb_ep_disable(dev->ep_in); - return ret; - } - dev->online = 1; - - /* readers may be blocked waiting for us to go online */ - wake_up(&dev->read_wq); - return 0; -} - -static void adb_function_disable(struct usb_function *f) -{ - struct adb_dev *dev = func_to_adb(f); - struct usb_composite_dev *cdev = dev->cdev; - - DBG(cdev, "adb_function_disable cdev %p\n", cdev); - dev->online = 0; - dev->error = 1; - usb_ep_disable(dev->ep_in); - usb_ep_disable(dev->ep_out); - - /* readers may be blocked waiting for us to go online */ - wake_up(&dev->read_wq); - - VDBG(cdev, "%s disabled\n", dev->function.name); -} - -static int adb_bind_config(struct usb_configuration *c) -{ - struct adb_dev *dev = _adb_dev; - - printk(KERN_INFO "adb_bind_config\n"); - - dev->cdev = c->cdev; - dev->function.name = "adb"; - dev->function.fs_descriptors = fs_adb_descs; - dev->function.hs_descriptors = hs_adb_descs; - dev->function.bind = adb_function_bind; - dev->function.unbind = adb_function_unbind; - dev->function.set_alt = adb_function_set_alt; - dev->function.disable = adb_function_disable; - - return usb_add_function(c, &dev->function); -} - -static int adb_setup(void) -{ - struct adb_dev *dev; - int ret; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - spin_lock_init(&dev->lock); - - init_waitqueue_head(&dev->read_wq); - init_waitqueue_head(&dev->write_wq); - - atomic_set(&dev->open_excl, 0); - atomic_set(&dev->read_excl, 0); - atomic_set(&dev->write_excl, 0); - - INIT_LIST_HEAD(&dev->tx_idle); - - _adb_dev = dev; - - ret = misc_register(&adb_device); - if (ret) - goto err; - - return 0; - -err: - kfree(dev); - printk(KERN_ERR "adb gadget driver failed to initialize\n"); - return ret; -} - -static void adb_cleanup(void) -{ - misc_deregister(&adb_device); - - kfree(_adb_dev); - _adb_dev = NULL; -} diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index b369292d4b90..5946eb43d4f4 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -1124,8 +1124,7 @@ int gserial_alloc_line(unsigned char *line_num) /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ - tty_dev = tty_port_register_device(&ports[port_num].port->port, - gs_tty_driver, port_num, NULL); + tty_dev = tty_register_device(gs_tty_driver, port_num, NULL); if (IS_ERR(tty_dev)) { struct gs_port *port; pr_err("%s: failed to register tty for port %d, err %ld\n", From 5717eb7821eaa078d3fbcb94fe1b6ed00a40ebcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 4 Mar 2013 17:41:34 -0800 Subject: [PATCH 260/358] usb: gadget: android: Fixes and hacks to make android usb gadget compile on 3.9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- drivers/usb/gadget/android.c | 38 +++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index e7a3807b2c83..eb23eac52f10 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -34,6 +34,7 @@ #include "f_audio_source.c" #include "f_mass_storage.c" #include "u_serial.c" +#define USB_FACM_INCLUDED #include "f_acm.c" #include "f_mtp.c" #include "f_accessory.c" @@ -149,7 +150,7 @@ static struct usb_configuration android_config_driver = { .unbind = android_unbind_config, .bConfigurationValue = 1, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 0xFA, /* 500ma */ + .MaxPower = 500, /* 500ma */ }; static void android_work(struct work_struct *data) @@ -366,22 +367,41 @@ static void functionfs_release_dev_callback(struct ffs_data *ffs_data) #define MAX_ACM_INSTANCES 4 struct acm_function_config { int instances; + unsigned char port_num[MAX_ACM_INSTANCES]; }; static int acm_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) { - f->config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL); - if (!f->config) - return -ENOMEM; + int i; + int ret; + struct acm_function_config *config; - return gserial_setup(cdev->gadget, MAX_ACM_INSTANCES); + config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL); + if (!config) + return -ENOMEM; + f->config = config; + + for (i = 0; i < MAX_ACM_INSTANCES; i++) { + ret = gserial_alloc_line(&config->port_num[i]); + if (ret) + goto err_alloc_line; + } + return 0; +err_alloc_line: + while (i-- > 0) + gserial_free_line(config->port_num[i]); + return ret; } static void acm_function_cleanup(struct android_usb_function *f) { - gserial_cleanup(); + int i; + struct acm_function_config *config = f->config; + + for (i = 0; i < MAX_ACM_INSTANCES; i++) + gserial_free_line(config->port_num[i]); kfree(f->config); f->config = NULL; } @@ -1312,7 +1332,7 @@ static int android_usb_unbind(struct usb_composite_dev *cdev) } /* HACK: android needs to override setup for accessory to work */ -static int (*composite_setup)(struct usb_gadget *gadget, const struct usb_ctrlrequest *c); +static int (*composite_setup_func)(struct usb_gadget *gadget, const struct usb_ctrlrequest *c); static int android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) @@ -1343,7 +1363,7 @@ android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) value = acc_ctrlrequest(cdev, c); if (value < 0) - value = composite_setup(gadget, c); + value = composite_setup_func(gadget, c); spin_lock_irqsave(&cdev->lock, flags); if (!dev->connected) { @@ -1442,7 +1462,7 @@ static int __init init(void) } /* HACK: exchange composite's setup with ours */ - composite_setup = android_usb_driver.gadget_driver.setup; + composite_setup_func = android_usb_driver.gadget_driver.setup; android_usb_driver.gadget_driver.setup = android_setup; return 0; From 1dff231237d355d8a791e9fd30a80778bb4c5925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 4 Mar 2013 19:14:10 -0800 Subject: [PATCH 261/358] usb: gadget: android: move init to late_initcall for now MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gserial_alloc_line crashes when called from module_init Signed-off-by: Arve Hjønnevåg --- drivers/usb/gadget/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index eb23eac52f10..07e7a8d5026a 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -1473,7 +1473,7 @@ err_dev: class_destroy(android_class); return err; } -module_init(init); +late_initcall(init); static void __exit cleanup(void) { From c0a7ccbe94522adb9e5bffb947fd81c1dddbf225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 16 May 2013 20:27:45 -0700 Subject: [PATCH 262/358] usb: gadget: android: 3.10 fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- drivers/usb/gadget/Kconfig | 2 + drivers/usb/gadget/android.c | 73 +++++++++++++++++++++++++---------- drivers/usb/gadget/u_serial.c | 4 +- 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 9e2ef763bfb7..e272b648d329 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -1021,7 +1021,9 @@ config USB_G_PRINTER config USB_G_ANDROID boolean "Android Composite Gadget" + select USB_F_ACM select USB_LIBCOMPOSITE + select USB_U_SERIAL help The Android Composite Gadget supports multiple USB functions: adb, acm, mass storage, mtp, accessory diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 07e7a8d5026a..31f3e009b4a4 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -33,9 +33,6 @@ #include "f_fs.c" #include "f_audio_source.c" #include "f_mass_storage.c" -#include "u_serial.c" -#define USB_FACM_INCLUDED -#include "f_acm.c" #include "f_mtp.c" #include "f_accessory.c" #define USB_ETH_RNDIS y @@ -367,7 +364,9 @@ static void functionfs_release_dev_callback(struct ffs_data *ffs_data) #define MAX_ACM_INSTANCES 4 struct acm_function_config { int instances; - unsigned char port_num[MAX_ACM_INSTANCES]; + int instances_on; + struct usb_function *f_acm[MAX_ACM_INSTANCES]; + struct usb_function_instance *f_acm_inst[MAX_ACM_INSTANCES]; }; static int @@ -384,14 +383,24 @@ acm_function_init(struct android_usb_function *f, f->config = config; for (i = 0; i < MAX_ACM_INSTANCES; i++) { - ret = gserial_alloc_line(&config->port_num[i]); - if (ret) - goto err_alloc_line; + config->f_acm_inst[i] = usb_get_function_instance("acm"); + if (IS_ERR(config->f_acm_inst[i])) { + ret = PTR_ERR(config->f_acm_inst[i]); + goto err_usb_get_function_instance; + } + config->f_acm[i] = usb_get_function(config->f_acm_inst[i]); + if (IS_ERR(config->f_acm[i])) { + ret = PTR_ERR(config->f_acm[i]); + goto err_usb_get_function; + } } return 0; -err_alloc_line: - while (i-- > 0) - gserial_free_line(config->port_num[i]); +err_usb_get_function_instance: + while (i-- > 0) { + usb_put_function(config->f_acm[i]); +err_usb_get_function: + usb_put_function_instance(config->f_acm_inst[i]); + } return ret; } @@ -400,8 +409,10 @@ static void acm_function_cleanup(struct android_usb_function *f) int i; struct acm_function_config *config = f->config; - for (i = 0; i < MAX_ACM_INSTANCES; i++) - gserial_free_line(config->port_num[i]); + for (i = 0; i < MAX_ACM_INSTANCES; i++) { + usb_put_function(config->f_acm[i]); + usb_put_function_instance(config->f_acm_inst[i]); + } kfree(f->config); f->config = NULL; } @@ -414,17 +425,33 @@ acm_function_bind_config(struct android_usb_function *f, int ret = 0; struct acm_function_config *config = f->config; - for (i = 0; i < config->instances; i++) { - ret = acm_bind_config(c, i); + config->instances_on = config->instances; + for (i = 0; i < config->instances_on; i++) { + ret = usb_add_function(c, config->f_acm[i]); if (ret) { pr_err("Could not bind acm%u config\n", i); - break; + goto err_usb_add_function; } } + return 0; + +err_usb_add_function: + while (i-- > 0) + usb_remove_function(c, config->f_acm[i]); return ret; } +static void acm_function_unbind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + int i; + struct acm_function_config *config = f->config; + + for (i = 0; i < config->instances_on; i++) + usb_remove_function(c, config->f_acm[i]); +} + static ssize_t acm_instances_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -459,6 +486,7 @@ static struct android_usb_function acm_function = { .init = acm_function_init, .cleanup = acm_function_cleanup, .bind_config = acm_function_bind_config, + .unbind_config = acm_function_unbind_config, .attributes = acm_function_attributes, }; @@ -532,6 +560,7 @@ struct rndis_function_config { char manufacturer[256]; /* "Wireless" RNDIS; auto-detected by Windows */ bool wceis; + struct eth_dev *dev; }; static int @@ -555,6 +584,7 @@ rndis_function_bind_config(struct android_usb_function *f, struct usb_configuration *c) { int ret; + struct eth_dev *dev; struct rndis_function_config *rndis = f->config; if (!rndis) { @@ -566,11 +596,13 @@ rndis_function_bind_config(struct android_usb_function *f, rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); - ret = gether_setup_name(c->cdev->gadget, rndis->ethaddr, "rndis"); - if (ret) { + dev = gether_setup_name(c->cdev->gadget, rndis->ethaddr, "rndis"); + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); pr_err("%s: gether_setup failed\n", __func__); return ret; } + rndis->dev = dev; if (rndis->wceis) { /* "Wireless" RNDIS; auto-detected by Windows */ @@ -585,13 +617,14 @@ rndis_function_bind_config(struct android_usb_function *f, } return rndis_bind_config_vendor(c, rndis->ethaddr, rndis->vendorID, - rndis->manufacturer); + rndis->manufacturer, rndis->dev); } static void rndis_function_unbind_config(struct android_usb_function *f, struct usb_configuration *c) { - gether_cleanup(); + struct rndis_function_config *rndis = f->config; + gether_cleanup(rndis->dev); } static ssize_t rndis_manufacturer_show(struct device *dev, @@ -881,7 +914,7 @@ static ssize_t audio_source_pcm_show(struct device *dev, return sprintf(buf, "%d %d\n", config->card, config->device); } -static DEVICE_ATTR(pcm, S_IRUGO | S_IWUSR, audio_source_pcm_show, NULL); +static DEVICE_ATTR(pcm, S_IRUGO, audio_source_pcm_show, NULL); static struct device_attribute *audio_source_function_attributes[] = { &dev_attr_pcm, diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 5946eb43d4f4..72068082e83c 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -1124,7 +1124,9 @@ int gserial_alloc_line(unsigned char *line_num) /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ - tty_dev = tty_register_device(gs_tty_driver, port_num, NULL); + tty_dev = tty_port_register_device(&ports[port_num].port->port, + gs_tty_driver, port_num, NULL); + if (IS_ERR(tty_dev)) { struct gs_port *port; pr_err("%s: failed to register tty for port %d, err %ld\n", From 2122c01d0e9a5124a64f6d0c223ea8b926ec1d8a Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 20 Feb 2013 16:38:34 -0800 Subject: [PATCH 263/358] netfilter: xt_qtaguid: fix bad tcp_time_wait sock handling Since (41063e9 ipv4: Early TCP socket demux), skb's can have an sk which is not a struct sock but the smaller struct inet_timewait_sock without an sk->sk_socket. Now we bypass sk_state == TCP_TIME_WAIT Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 4ec6d23876c5..5e07c3375b56 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1694,14 +1694,13 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return NULL; } - /* - * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. - * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 - * Not fixed in 3.0-r3 :( - */ if (sk) { MT_DEBUG("qtaguid: %p->sk_proto=%u " "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); + /* + * When in TCP_TIME_WAIT the sk is not a "struct sock" but + * "struct inet_timewait_sock" which is missing fields. + */ if (sk->sk_state == TCP_TIME_WAIT) { xt_socket_put_sk(sk); sk = NULL; @@ -1785,6 +1784,13 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } sk = skb->sk; + /* + * When in TCP_TIME_WAIT the sk is not a "struct sock" but + * "struct inet_timewait_sock" which is missing fields. + * So we ignore it. + */ + if (sk && sk->sk_state == TCP_TIME_WAIT) + sk = NULL; if (sk == NULL) { /* * A missing sk->sk_socket happens when packets are in-flight From 4546f6a22254571cac0dd332d8240fdaf771a52c Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 14 Mar 2013 13:26:14 -0700 Subject: [PATCH 264/358] selinux: binder: Fix COMMON_AUDIT_DATA_INIT compile issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The COMMON_AUDIT_DATA_INIT macros have been removed, and are now replaced with open coded ad.type initialization. Thus, this patch updates the selinux_binder_transfer_file function so it builds. Change-Id: Ide41069a87638e294899768d09302f4013794e4c Cc: Stephen Smalley Cc: Arve Hjønnevåg Cc: Android Kernel Team Signed-off-by: John Stultz --- security/selinux/hooks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 228d9577a3f1..aefbe1ca8f5d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1947,7 +1947,7 @@ static int selinux_binder_transfer_file(struct task_struct *from, struct task_st struct selinux_audit_data sad = {0,}; int rc; - COMMON_AUDIT_DATA_INIT(&ad, PATH); + ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = file->f_path; ad.selinux_audit_data = &sad; From e8d4e22118b137835d2795a828816e2885fb3e91 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 18 Mar 2013 11:57:28 -0700 Subject: [PATCH 265/358] mmc: core: Remove stray CONFIG_EXPERIMENTAL dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIG_EXPERIMENTAL has been removed from the kernel, so clean up its use in MMC_EMBEDDED_SDIO and MMC_PARANOID_SD_INIT options. Change-Id: If414c265134b36740a84564274a631803c8e81b4 Cc: Arve Hjønnevåg Cc: San Mehat Cc: Android Kernel Team Reported-by: Jon Medhurst (Tixy) Signed-off-by: John Stultz --- drivers/mmc/core/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index a7c3cdc59f76..ae10a372af0f 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -29,7 +29,6 @@ config MMC_CLKGATE config MMC_EMBEDDED_SDIO boolean "MMC embedded SDIO device support (EXPERIMENTAL)" - depends on EXPERIMENTAL help If you say Y here, support will be added for embedded SDIO devices which do not contain the necessary enumeration @@ -37,7 +36,6 @@ config MMC_EMBEDDED_SDIO config MMC_PARANOID_SD_INIT bool "Enable paranoid SD card initialization (EXPERIMENTAL)" - depends on EXPERIMENTAL help If you say Y here, the MMC layer will be extra paranoid about re-trying SD init requests. This can be a useful From 6c6023bf6e9daad56068db3b0df60c7a0db602bd Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 9 Apr 2013 10:30:26 -0700 Subject: [PATCH 266/358] pstore: Update Documentation/android.txt Update Documentation/android.txt to reference PSTORE_CONSOLE and PSTORE_RAM instead of ANDROID_RAM_CONSOLE Change-Id: I2c56e73f8c65c3ddbe6ddbf1faadfacb42a09575 Reported-by: Jon Medhurst (Tixy) Signed-off-by: John Stultz --- Documentation/android.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/android.txt b/Documentation/android.txt index 72a62afdf202..0f40a78b045f 100644 --- a/Documentation/android.txt +++ b/Documentation/android.txt @@ -92,8 +92,8 @@ DNOTIFY 1.3 Recommended enabled config options ------------------------------ ANDROID_PMEM -ANDROID_RAM_CONSOLE -ANDROID_RAM_CONSOLE_ERROR_CORRECTION +PSTORE_CONSOLE +PSTORE_RAM SCHEDSTATS DEBUG_PREEMPT DEBUG_MUTEXES From 5eb9874fd416b6545efe10b2986a58cb4f6a0043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 13 May 2013 20:39:30 -0700 Subject: [PATCH 267/358] net: activity_stats: Stop using obsolete create_proc_read_entry api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert to use seq_read Signed-off-by: Arve Hjønnevåg --- net/activity_stats.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/net/activity_stats.c b/net/activity_stats.c index 8a3e93470069..4609ce2043eb 100644 --- a/net/activity_stats.c +++ b/net/activity_stats.c @@ -15,6 +15,7 @@ */ #include +#include #include #include @@ -59,29 +60,20 @@ void activity_stats_update(void) spin_unlock_irqrestore(&activity_lock, flags); } -static int activity_stats_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int activity_stats_show(struct seq_file *m, void *v) { int i; - int len; - char *p = page; + int ret; - /* Only print if offset is 0, or we have enough buffer space */ - if (off || count < (30 * BUCKET_MAX + 22)) - return -ENOMEM; - - len = snprintf(p, count, "Min Bucket(sec) Count\n"); - count -= len; - p += len; + seq_printf(m, "Min Bucket(sec) Count\n"); for (i = 0; i < BUCKET_MAX; i++) { - len = snprintf(p, count, "%15d %lu\n", 1 << i, activity_stats[i]); - count -= len; - p += len; + ret = seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]); + if (ret) + return ret; } - *eof = 1; - return p - page; + return 0; } static int activity_stats_notifier(struct notifier_block *nb, @@ -100,14 +92,26 @@ static int activity_stats_notifier(struct notifier_block *nb, return 0; } +static int activity_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, activity_stats_show, PDE_DATA(inode)); +} + +static const struct file_operations activity_stats_fops = { + .open = activity_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + static struct notifier_block activity_stats_notifier_block = { .notifier_call = activity_stats_notifier, }; static int __init activity_stats_init(void) { - create_proc_read_entry("activity", S_IRUGO, - init_net.proc_net_stat, activity_stats_read_proc, NULL); + proc_create("activity", S_IRUGO, + init_net.proc_net_stat, &activity_stats_fops); return register_pm_notifier(&activity_stats_notifier_block); } From 63326301cfddc1059101d517dde73b740b00bc2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 13 May 2013 20:42:46 -0700 Subject: [PATCH 268/358] netfilter: xt_quota2: 3.10 fixes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Stop using obsolete create_proc_entry api. - Use proc_set_user instead of directly accessing the private structure. Signed-off-by: Arve Hjønnevåg --- net/netfilter/xt_quota2.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index aace72928530..44ebdcc75965 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -122,22 +122,23 @@ static void quota2_log(unsigned int hooknum, } #endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ -static int quota_proc_read(char *page, char **start, off_t offset, - int count, int *eof, void *data) +static int quota_proc_read(struct file *file, char __user *buf, + size_t size, loff_t *ppos) { - struct xt_quota_counter *e = data; - int ret; + struct xt_quota_counter *e = PDE_DATA(file_inode(file)); + char tmp[24]; + size_t tmp_size; spin_lock_bh(&e->lock); - ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); + tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", e->quota); spin_unlock_bh(&e->lock); - return ret; + return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } static int quota_proc_write(struct file *file, const char __user *input, - unsigned long size, void *data) + size_t size, loff_t *ppos) { - struct xt_quota_counter *e = data; + struct xt_quota_counter *e = PDE_DATA(file_inode(file)); char buf[sizeof("18446744073709551616")]; if (size > sizeof(buf)) @@ -152,6 +153,12 @@ static int quota_proc_write(struct file *file, const char __user *input, return size; } +static const struct file_operations q2_counter_fops = { + .read = quota_proc_read, + .write = quota_proc_write, + .llseek = default_llseek, +}; + static struct xt_quota_counter * q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) { @@ -215,8 +222,8 @@ q2_get_counter(const struct xt_quota_mtinfo2 *q) spin_unlock_bh(&counter_list_lock); /* create_proc_entry() is not spin_lock happy */ - p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, - proc_xt_quota); + p = e->procfs_entry = proc_create_data(e->name, quota_list_perms, + proc_xt_quota, &q2_counter_fops, e); if (IS_ERR_OR_NULL(p)) { spin_lock_bh(&counter_list_lock); @@ -224,11 +231,7 @@ q2_get_counter(const struct xt_quota_mtinfo2 *q) spin_unlock_bh(&counter_list_lock); goto out; } - p->data = e; - p->read_proc = quota_proc_read; - p->write_proc = quota_proc_write; - p->uid = quota_list_uid; - p->gid = quota_list_gid; + proc_set_user(p, quota_list_uid, quota_list_gid); return e; out: From 9e3c8517e50f0146e961c0a501296e7131d593ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 13 May 2013 20:34:22 -0700 Subject: [PATCH 269/358] misc: uidstat: Remove use of obsolete create_proc_read_entry api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- drivers/misc/uid_stat.c | 50 +++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c index 509822c81e97..4766c1f83b94 100644 --- a/drivers/misc/uid_stat.c +++ b/drivers/misc/uid_stat.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -48,42 +49,27 @@ static struct uid_stat *find_uid_stat(uid_t uid) { return NULL; } -static int tcp_snd_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int uid_stat_atomic_int_show(struct seq_file *m, void *v) { - int len; unsigned int bytes; - char *p = page; - struct uid_stat *uid_entry = (struct uid_stat *) data; - if (!data) - return 0; + atomic_t *counter = m->private; - bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN); - p += sprintf(p, "%u\n", bytes); - len = (p - page) - off; - *eof = (len <= count) ? 1 : 0; - *start = page + off; - return len; + bytes = (unsigned int) (atomic_read(counter) + INT_MIN); + return seq_printf(m, "%u\n", bytes); } -static int tcp_rcv_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file) { - int len; - unsigned int bytes; - char *p = page; - struct uid_stat *uid_entry = (struct uid_stat *) data; - if (!data) - return 0; - - bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN); - p += sprintf(p, "%u\n", bytes); - len = (p - page) - off; - *eof = (len <= count) ? 1 : 0; - *start = page + off; - return len; + return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode)); } +static const struct file_operations uid_stat_read_atomic_int_fops = { + .open = uid_stat_read_atomic_int_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + /* Create a new entry for tracking the specified uid. */ static struct uid_stat *create_stat(uid_t uid) { struct uid_stat *new_uid; @@ -109,11 +95,11 @@ static void create_stat_proc(struct uid_stat *new_uid) entry = proc_mkdir(uid_s, parent); /* Keep reference to uid_stat so we know what uid to read stats from. */ - create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc, - (void *) new_uid); + proc_create_data("tcp_snd", S_IRUGO, entry, + &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd); - create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc, - (void *) new_uid); + proc_create_data("tcp_rcv", S_IRUGO, entry, + &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv); } static struct uid_stat *find_or_create_uid_stat(uid_t uid) From 80e03303d93b4b2fda0349f87f9a99678f31c0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 13 May 2013 20:45:02 -0700 Subject: [PATCH 270/358] netfilter: xt_qtaguid: 3.10 fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop using obsolete procfs api. Signed-off-by: Arve Hjønnevåg --- net/netfilter/xt_qtaguid.c | 1045 ++++++++++++++++++------------------ 1 file changed, 530 insertions(+), 515 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 5e07c3375b56..e476b88f9d68 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" +#include "../../fs/proc/internal.h" /* * We only use the xt_socket funcs within a similar context to avoid unexpected @@ -122,104 +124,6 @@ static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; static struct proc_dir_entry *iface_stat_fmt_procfile; -/* - * Ordering of locks: - * outer locks: - * iface_stat_list_lock - * sock_tag_list_lock - * inner locks: - * uid_tag_data_tree_lock - * tag_counter_set_list_lock - * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock - * is acquired. - * - * Call tree with all lock holders as of 2012-04-27: - * - * iface_stat_fmt_proc_read() - * iface_stat_list_lock - * (struct iface_stat) - * - * qtaguid_ctrl_proc_read() - * sock_tag_list_lock - * (sock_tag_tree) - * (struct proc_qtu_data->sock_tag_list) - * prdebug_full_state() - * sock_tag_list_lock - * (sock_tag_tree) - * uid_tag_data_tree_lock - * (uid_tag_data_tree) - * (proc_qtu_data_tree) - * iface_stat_list_lock - * - * qtaguid_stats_proc_read() - * iface_stat_list_lock - * struct iface_stat->tag_stat_list_lock - * - * qtudev_open() - * uid_tag_data_tree_lock - * - * qtudev_release() - * sock_tag_data_list_lock - * uid_tag_data_tree_lock - * prdebug_full_state() - * sock_tag_list_lock - * uid_tag_data_tree_lock - * iface_stat_list_lock - * - * iface_netdev_event_handler() - * iface_stat_create() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock - * - * iface_inetaddr_event_handler() - * iface_stat_create() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock - * - * iface_inet6addr_event_handler() - * iface_stat_create_ipv6() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock - * - * qtaguid_mt() - * account_for_uid() - * if_tag_stat_update() - * get_sock_stat() - * sock_tag_list_lock - * struct iface_stat->tag_stat_list_lock - * tag_stat_update() - * get_active_counter_set() - * tag_counter_set_list_lock - * tag_stat_update() - * get_active_counter_set() - * tag_counter_set_list_lock - * - * - * qtaguid_ctrl_parse() - * ctrl_cmd_delete() - * sock_tag_list_lock - * tag_counter_set_list_lock - * iface_stat_list_lock - * struct iface_stat->tag_stat_list_lock - * uid_tag_data_tree_lock - * ctrl_cmd_counter_set() - * tag_counter_set_list_lock - * ctrl_cmd_tag() - * sock_tag_list_lock - * (sock_tag_tree) - * get_tag_ref() - * uid_tag_data_tree_lock - * (uid_tag_data_tree) - * uid_tag_data_tree_lock - * (proc_qtu_data_tree) - * ctrl_cmd_untag() - * sock_tag_list_lock - * uid_tag_data_tree_lock - * - */ static LIST_HEAD(iface_stat_list); static DEFINE_SPINLOCK(iface_stat_list_lock); @@ -690,42 +594,26 @@ static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) } } -static int read_proc_u64(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int read_proc_u64(struct file *file, char __user *buf, + size_t size, loff_t *ppos) { - int len; - uint64_t value; - char *p = page; - uint64_t *iface_entry = data; + uint64_t *valuep = PDE_DATA(file_inode(file)); + char tmp[24]; + size_t tmp_size; - if (!data) - return 0; - - value = *iface_entry; - p += sprintf(p, "%llu\n", value); - len = (p - page) - off; - *eof = (len <= count) ? 1 : 0; - *start = page + off; - return len; + tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", *valuep); + return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } -static int read_proc_bool(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int read_proc_bool(struct file *file, char __user *buf, + size_t size, loff_t *ppos) { - int len; - bool value; - char *p = page; - bool *bool_entry = data; + bool *valuep = PDE_DATA(file_inode(file)); + char tmp[24]; + size_t tmp_size; - if (!data) - return 0; - - value = *bool_entry; - p += sprintf(p, "%u\n", value); - len = (p - page) - off; - *eof = (len <= count) ? 1 : 0; - *start = page + off; - return len; + tmp_size = scnprintf(tmp, sizeof(tmp), "%u\n", *valuep); + return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } static int get_active_counter_set(tag_t tag) @@ -771,144 +659,132 @@ done: } /* This is for fmt2 only */ -static int pp_iface_stat_line(bool header, char *outp, - int char_count, struct iface_stat *iface_entry) +static void pp_iface_stat_header(struct seq_file *m) { - int len; - if (header) { - len = snprintf(outp, char_count, - "ifname " - "total_skb_rx_bytes total_skb_rx_packets " - "total_skb_tx_bytes total_skb_tx_packets " - "rx_tcp_bytes rx_tcp_packets " - "rx_udp_bytes rx_udp_packets " - "rx_other_bytes rx_other_packets " - "tx_tcp_bytes tx_tcp_packets " - "tx_udp_bytes tx_udp_packets " - "tx_other_bytes tx_other_packets\n" - ); - } else { - struct data_counters *cnts; - int cnt_set = 0; /* We only use one set for the device */ - cnts = &iface_entry->totals_via_skb; - len = snprintf( - outp, char_count, - "%s " - "%llu %llu %llu %llu %llu %llu %llu %llu " - "%llu %llu %llu %llu %llu %llu %llu %llu\n", - iface_entry->ifname, - dc_sum_bytes(cnts, cnt_set, IFS_RX), - dc_sum_packets(cnts, cnt_set, IFS_RX), - dc_sum_bytes(cnts, cnt_set, IFS_TX), - dc_sum_packets(cnts, cnt_set, IFS_TX), - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); - } - return len; + seq_puts(m, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n" + ); } -static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, - off_t items_to_skip, int char_count, - int *eof, void *data) +static void pp_iface_stat_line(struct seq_file *m, + struct iface_stat *iface_entry) { - char *outp = page; - int item_index = 0; - int len; - int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */ - struct iface_stat *iface_entry; - struct rtnl_link_stats64 dev_stats, *stats; - struct rtnl_link_stats64 no_dev_stats = {0}; + struct data_counters *cnts; + int cnt_set = 0; /* We only use one set for the device */ + cnts = &iface_entry->totals_via_skb; + seq_printf(m, "%s %llu %llu %llu %llu %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); +} - if (unlikely(module_passive)) { - *eof = 1; - return 0; - } +struct proc_iface_stat_fmt_info { + int fmt; +}; - CT_DEBUG("qtaguid:proc iface_stat_fmt " - "pid=%u tgid=%u uid=%u " - "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", - current->pid, current->tgid, current_fsuid(), - page, *num_items_returned, - items_to_skip, char_count, *eof); - - if (*eof) - return 0; - - if (fmt == 2 && item_index++ >= items_to_skip) { - len = pp_iface_stat_line(true, outp, char_count, NULL); - if (len >= char_count) { - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } +static void *iface_stat_fmt_proc_start(struct seq_file *m, loff_t *pos) +{ + struct proc_iface_stat_fmt_info *p = m->private; + loff_t n = *pos; /* * This lock will prevent iface_stat_update() from changing active, * and in turn prevent an interface from unregistering itself. */ spin_lock_bh(&iface_stat_list_lock); - list_for_each_entry(iface_entry, &iface_stat_list, list) { - if (item_index++ < items_to_skip) - continue; - if (iface_entry->active) { - stats = dev_get_stats(iface_entry->net_dev, - &dev_stats); - } else { - stats = &no_dev_stats; - } - /* - * If the meaning of the data changes, then update the fmtX - * string. - */ - if (fmt == 1) { - len = snprintf( - outp, char_count, - "%s %d " - "%llu %llu %llu %llu " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->active, - iface_entry->totals_via_dev[IFS_RX].bytes, - iface_entry->totals_via_dev[IFS_RX].packets, - iface_entry->totals_via_dev[IFS_TX].bytes, - iface_entry->totals_via_dev[IFS_TX].packets, - stats->rx_bytes, stats->rx_packets, - stats->tx_bytes, stats->tx_packets - ); - } else { - len = pp_iface_stat_line(false, outp, char_count, - iface_entry); - } - if (len >= char_count) { - spin_unlock_bh(&iface_stat_list_lock); - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } - spin_unlock_bh(&iface_stat_list_lock); + if (unlikely(module_passive)) + return NULL; - *eof = 1; - return outp - page; + if (!n && p->fmt == 2) + pp_iface_stat_header(m); + + return seq_list_start(&iface_stat_list, n); } +static void *iface_stat_fmt_proc_next(struct seq_file *m, void *p, loff_t *pos) +{ + return seq_list_next(p, &iface_stat_list, pos); +} + +static void iface_stat_fmt_proc_stop(struct seq_file *m, void *p) +{ + spin_unlock_bh(&iface_stat_list_lock); +} + +static int iface_stat_fmt_proc_show(struct seq_file *m, void *v) +{ + struct proc_iface_stat_fmt_info *p = m->private; + struct iface_stat *iface_entry; + struct rtnl_link_stats64 dev_stats, *stats; + struct rtnl_link_stats64 no_dev_stats = {0}; + + + CT_DEBUG("qtaguid:proc iface_stat_fmt pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); + + iface_entry = list_entry(v, struct iface_stat, list); + + if (iface_entry->active) { + stats = dev_get_stats(iface_entry->net_dev, + &dev_stats); + } else { + stats = &no_dev_stats; + } + /* + * If the meaning of the data changes, then update the fmtX + * string. + */ + if (p->fmt == 1) { + seq_printf(m, "%s %d %llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals_via_dev[IFS_RX].bytes, + iface_entry->totals_via_dev[IFS_RX].packets, + iface_entry->totals_via_dev[IFS_TX].bytes, + iface_entry->totals_via_dev[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets + ); + } else { + pp_iface_stat_line(m, iface_entry); + } + return 0; +} + +static const struct file_operations read_u64_fops = { + .read = read_proc_u64, + .llseek = default_llseek, +}; + +static const struct file_operations read_bool_fops = { + .read = read_proc_bool, + .llseek = default_llseek, +}; + static void iface_create_proc_worker(struct work_struct *work) { struct proc_dir_entry *proc_entry; @@ -926,20 +802,20 @@ static void iface_create_proc_worker(struct work_struct *work) new_iface->proc_ptr = proc_entry; - create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_TX].bytes); - create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_RX].bytes); - create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_TX].packets); - create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_RX].packets); - create_proc_read_entry("active", proc_iface_perms, proc_entry, - read_proc_bool, &new_iface->active); + proc_create_data("tx_bytes", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_TX].bytes); + proc_create_data("rx_bytes", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_RX].bytes); + proc_create_data("tx_packets", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_TX].packets); + proc_create_data("rx_packets", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_RX].packets); + proc_create_data("active", proc_iface_perms, proc_entry, + &read_bool_fops, &new_iface->active); IF_DEBUG("qtaguid: iface_stat: create_proc(): done " "entry=%p dev=%s\n", new_iface, new_iface->ifname); @@ -1596,6 +1472,33 @@ static struct notifier_block iface_inet6addr_notifier_blk = { .notifier_call = iface_inet6addr_event_handler, }; +static const struct seq_operations iface_stat_fmt_proc_seq_ops = { + .start = iface_stat_fmt_proc_start, + .next = iface_stat_fmt_proc_next, + .stop = iface_stat_fmt_proc_stop, + .show = iface_stat_fmt_proc_show, +}; + +static int proc_iface_stat_fmt_open(struct inode *inode, struct file *file) +{ + struct proc_iface_stat_fmt_info *s; + + s = __seq_open_private(file, &iface_stat_fmt_proc_seq_ops, + sizeof(struct proc_iface_stat_fmt_info)); + if (!s) + return -ENOMEM; + + s->fmt = (int)PDE_DATA(inode); + return 0; +} + +static const struct file_operations proc_iface_stat_fmt_fops = { + .open = proc_iface_stat_fmt_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) { int err; @@ -1607,29 +1510,29 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) goto err; } - iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename, - proc_iface_perms, - parent_procdir); + iface_stat_all_procfile = proc_create_data(iface_stat_all_procfilename, + proc_iface_perms, + parent_procdir, + &proc_iface_stat_fmt_fops, + (void *)1 /* fmt1 */); if (!iface_stat_all_procfile) { pr_err("qtaguid: iface_stat: init " " failed to create stat_old proc entry\n"); err = -1; goto err_zap_entry; } - iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read; - iface_stat_all_procfile->data = (void *)1; /* fmt1 */ - iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename, - proc_iface_perms, - parent_procdir); + iface_stat_fmt_procfile = proc_create_data(iface_stat_fmt_procfilename, + proc_iface_perms, + parent_procdir, + &proc_iface_stat_fmt_fops, + (void *)2 /* fmt2 */); if (!iface_stat_fmt_procfile) { pr_err("qtaguid: iface_stat: init " " failed to create stat_all proc entry\n"); err = -1; goto err_zap_all_stats_entry; } - iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read; - iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */ err = register_netdevice_notifier(&iface_netdev_notifier_blk); @@ -1940,43 +1843,85 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) static void prdebug_full_state(int indent_level, const char *fmt, ...) {} #endif +struct proc_ctrl_print_info { + struct sock *sk; /* socket found by reading to sk_pos */ + loff_t sk_pos; +}; + +static void *qtaguid_ctrl_proc_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct proc_ctrl_print_info *pcpi = m->private; + struct sock_tag *sock_tag_entry = v; + struct rb_node *node; + + (*pos)++; + + if (!v || v == SEQ_START_TOKEN) + return NULL; + + node = rb_next(&sock_tag_entry->sock_node); + if (!node) { + pcpi->sk = NULL; + sock_tag_entry = SEQ_START_TOKEN; + } else { + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + pcpi->sk = sock_tag_entry->sk; + } + pcpi->sk_pos = *pos; + return sock_tag_entry; +} + +static void *qtaguid_ctrl_proc_start(struct seq_file *m, loff_t *pos) +{ + struct proc_ctrl_print_info *pcpi = m->private; + struct sock_tag *sock_tag_entry; + struct rb_node *node; + + spin_lock_bh(&sock_tag_list_lock); + + if (unlikely(module_passive)) + return NULL; + + if (*pos == 0) { + pcpi->sk_pos = 0; + node = rb_first(&sock_tag_tree); + if (!node) { + pcpi->sk = NULL; + return SEQ_START_TOKEN; + } + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + pcpi->sk = sock_tag_entry->sk; + } else { + sock_tag_entry = (pcpi->sk ? get_sock_stat_nl(pcpi->sk) : + NULL) ?: SEQ_START_TOKEN; + if (*pos != pcpi->sk_pos) { + /* seq_read skipped a next call */ + *pos = pcpi->sk_pos; + return qtaguid_ctrl_proc_next(m, sock_tag_entry, pos); + } + } + return sock_tag_entry; +} + +static void qtaguid_ctrl_proc_stop(struct seq_file *m, void *v) +{ + spin_unlock_bh(&sock_tag_list_lock); +} + /* * Procfs reader to get all active socket tags using style "1)" as described in * fs/proc/generic.c */ -static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, - off_t items_to_skip, int char_count, int *eof, - void *data) +static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) { - char *outp = page; - int len; + struct sock_tag *sock_tag_entry = v; uid_t uid; - struct rb_node *node; - struct sock_tag *sock_tag_entry; - int item_index = 0; - int indent_level = 0; long f_count; - if (unlikely(module_passive)) { - *eof = 1; - return 0; - } + CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); - if (*eof) - return 0; - - CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u " - "page=%p off=%ld char_count=%d *eof=%d\n", - current->pid, current->tgid, current_fsuid(), - page, items_to_skip, char_count, *eof); - - spin_lock_bh(&sock_tag_list_lock); - for (node = rb_first(&sock_tag_tree); - node; - node = rb_next(node)) { - if (item_index++ < items_to_skip) - continue; - sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + if (sock_tag_entry != SEQ_START_TOKEN) { uid = get_uid_from_tag(sock_tag_entry->tag); CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " "pid=%u\n", @@ -1987,66 +1932,42 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, ); f_count = atomic_long_read( &sock_tag_entry->socket->file->f_count); - len = snprintf(outp, char_count, - "sock=%p tag=0x%llx (uid=%u) pid=%u " - "f_count=%lu\n", - sock_tag_entry->sk, - sock_tag_entry->tag, uid, - sock_tag_entry->pid, f_count); - if (len >= char_count) { - spin_unlock_bh(&sock_tag_list_lock); - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } - spin_unlock_bh(&sock_tag_list_lock); + seq_printf(m, "sock=%p tag=0x%llx (uid=%u) pid=%u " + "f_count=%lu\n", + sock_tag_entry->sk, + sock_tag_entry->tag, uid, + sock_tag_entry->pid, f_count); + } else { + seq_printf(m, "events: sockets_tagged=%llu " + "sockets_untagged=%llu " + "counter_set_changes=%llu " + "delete_cmds=%llu " + "iface_events=%llu " + "match_calls=%llu " + "match_calls_prepost=%llu " + "match_found_sk=%llu " + "match_found_sk_in_ct=%llu " + "match_found_no_sk_in_ct=%llu " + "match_no_sk=%llu " + "match_no_sk_file=%llu\n", + atomic64_read(&qtu_events.sockets_tagged), + atomic64_read(&qtu_events.sockets_untagged), + atomic64_read(&qtu_events.counter_set_changes), + atomic64_read(&qtu_events.delete_cmds), + atomic64_read(&qtu_events.iface_events), + atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_calls_prepost), + atomic64_read(&qtu_events.match_found_sk), + atomic64_read(&qtu_events.match_found_sk_in_ct), + atomic64_read(&qtu_events.match_found_no_sk_in_ct), + atomic64_read(&qtu_events.match_no_sk), + atomic64_read(&qtu_events.match_no_sk_file)); - if (item_index++ >= items_to_skip) { - len = snprintf(outp, char_count, - "events: sockets_tagged=%llu " - "sockets_untagged=%llu " - "counter_set_changes=%llu " - "delete_cmds=%llu " - "iface_events=%llu " - "match_calls=%llu " - "match_calls_prepost=%llu " - "match_found_sk=%llu " - "match_found_sk_in_ct=%llu " - "match_found_no_sk_in_ct=%llu " - "match_no_sk=%llu " - "match_no_sk_file=%llu\n", - atomic64_read(&qtu_events.sockets_tagged), - atomic64_read(&qtu_events.sockets_untagged), - atomic64_read(&qtu_events.counter_set_changes), - atomic64_read(&qtu_events.delete_cmds), - atomic64_read(&qtu_events.iface_events), - atomic64_read(&qtu_events.match_calls), - atomic64_read(&qtu_events.match_calls_prepost), - atomic64_read(&qtu_events.match_found_sk), - atomic64_read(&qtu_events.match_found_sk_in_ct), - atomic64_read( - &qtu_events.match_found_no_sk_in_ct), - atomic64_read(&qtu_events.match_no_sk), - atomic64_read(&qtu_events.match_no_sk_file)); - if (len >= char_count) { - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; + /* Count the following as part of the last item_index */ + prdebug_full_state(0, "proc ctrl"); } - /* Count the following as part of the last item_index */ - if (item_index > items_to_skip) { - prdebug_full_state(indent_level, "proc ctrl"); - } - - *eof = 1; - return outp - page; + return 0; } /* @@ -2559,7 +2480,7 @@ err: #define MAX_QTAGUID_CTRL_INPUT_LEN 255 static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, - unsigned long count, void *data) + size_t count, loff_t *offp) { char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; @@ -2577,178 +2498,230 @@ static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, } struct proc_print_info { - char *outp; - char **num_items_returned; struct iface_stat *iface_entry; - struct tag_stat *ts_entry; int item_index; - int items_to_skip; - int char_count; + tag_t tag; /* tag found by reading to tag_pos */ + off_t tag_pos; + int tag_item_index; }; -static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) +static void pp_stats_header(struct seq_file *m) { - int len; - struct data_counters *cnts; - - if (!ppi->item_index) { - if (ppi->item_index++ < ppi->items_to_skip) - return 0; - len = snprintf(ppi->outp, ppi->char_count, - "idx iface acct_tag_hex uid_tag_int cnt_set " - "rx_bytes rx_packets " - "tx_bytes tx_packets " - "rx_tcp_bytes rx_tcp_packets " - "rx_udp_bytes rx_udp_packets " - "rx_other_bytes rx_other_packets " - "tx_tcp_bytes tx_tcp_packets " - "tx_udp_bytes tx_udp_packets " - "tx_other_bytes tx_other_packets\n"); - } else { - tag_t tag = ppi->ts_entry->tn.tag; - uid_t stat_uid = get_uid_from_tag(tag); - /* Detailed tags are not available to everybody */ - if (get_atag_from_tag(tag) - && !can_read_other_uid_stats(stat_uid)) { - CT_DEBUG("qtaguid: stats line: " - "%s 0x%llx %u: insufficient priv " - "from pid=%u tgid=%u uid=%u stats.gid=%u\n", - ppi->iface_entry->ifname, - get_atag_from_tag(tag), stat_uid, - current->pid, current->tgid, current_fsuid(), - xt_qtaguid_stats_file->gid); - return 0; - } - if (ppi->item_index++ < ppi->items_to_skip) - return 0; - cnts = &ppi->ts_entry->counters; - len = snprintf( - ppi->outp, ppi->char_count, - "%d %s 0x%llx %u %u " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu\n", - ppi->item_index, - ppi->iface_entry->ifname, - get_atag_from_tag(tag), - stat_uid, - cnt_set, - dc_sum_bytes(cnts, cnt_set, IFS_RX), - dc_sum_packets(cnts, cnt_set, IFS_RX), - dc_sum_bytes(cnts, cnt_set, IFS_TX), - dc_sum_packets(cnts, cnt_set, IFS_TX), - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); - } - return len; + seq_puts(m, + "idx iface acct_tag_hex uid_tag_int cnt_set " + "rx_bytes rx_packets " + "tx_bytes tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n"); } -static bool pp_sets(struct proc_print_info *ppi) +static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, + int cnt_set) { - int len; + int ret; + struct data_counters *cnts; + tag_t tag = ts_entry->tn.tag; + uid_t stat_uid = get_uid_from_tag(tag); + struct proc_print_info *ppi = m->private; + /* Detailed tags are not available to everybody */ + if (get_atag_from_tag(tag) && !can_read_other_uid_stats(stat_uid)) { + CT_DEBUG("qtaguid: stats line: " + "%s 0x%llx %u: insufficient priv " + "from pid=%u tgid=%u uid=%u stats.gid=%u\n", + ppi->iface_entry->ifname, + get_atag_from_tag(tag), stat_uid, + current->pid, current->tgid, current_fsuid(), + xt_qtaguid_stats_file->gid); + return 0; + } + ppi->item_index++; + cnts = &ts_entry->counters; + ret = seq_printf(m, "%d %s 0x%llx %u %u " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu\n", + ppi->item_index, + ppi->iface_entry->ifname, + get_atag_from_tag(tag), + stat_uid, + cnt_set, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + return ret ?: 1; +} + +static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry) +{ + int ret; int counter_set; for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; counter_set++) { - len = pp_stats_line(ppi, counter_set); - if (len >= ppi->char_count) { - *ppi->outp = '\0'; + ret = pp_stats_line(m, ts_entry, counter_set); + if (ret < 0) return false; - } - if (len) { - ppi->outp += len; - ppi->char_count -= len; - (*ppi->num_items_returned)++; - } } return true; } +static int qtaguid_stats_proc_iface_stat_ptr_valid(struct iface_stat *ptr) +{ + struct iface_stat *iface_entry; + + if (!ptr) + return false; + + list_for_each_entry(iface_entry, &iface_stat_list, list) + if (iface_entry == ptr) + return true; + return false; +} + +static void qtaguid_stats_proc_next_iface_entry(struct proc_print_info *ppi) +{ + spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock); + list_for_each_entry_continue(ppi->iface_entry, &iface_stat_list, list) { + spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); + return; + } + ppi->iface_entry = NULL; +} + +static void *qtaguid_stats_proc_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct proc_print_info *ppi = m->private; + struct tag_stat *ts_entry; + struct rb_node *node; + + if (!v) { + pr_err("qtaguid: %s(): unexpected v: NULL\n", __func__); + return NULL; + } + + (*pos)++; + + if (!ppi->iface_entry || unlikely(module_passive)) + return NULL; + + if (v == SEQ_START_TOKEN) + node = rb_first(&ppi->iface_entry->tag_stat_tree); + else + node = rb_next(&((struct tag_stat *)v)->tn.node); + + while (!node) { + qtaguid_stats_proc_next_iface_entry(ppi); + if (!ppi->iface_entry) + return NULL; + node = rb_first(&ppi->iface_entry->tag_stat_tree); + } + + ts_entry = rb_entry(node, struct tag_stat, tn.node); + ppi->tag = ts_entry->tn.tag; + ppi->tag_pos = *pos; + ppi->tag_item_index = ppi->item_index; + return ts_entry; +} + +static void *qtaguid_stats_proc_start(struct seq_file *m, loff_t *pos) +{ + struct proc_print_info *ppi = m->private; + struct tag_stat *ts_entry = NULL; + + spin_lock_bh(&iface_stat_list_lock); + + if (*pos == 0) { + ppi->item_index = 1; + ppi->tag_pos = 0; + if (list_empty(&iface_stat_list)) { + ppi->iface_entry = NULL; + } else { + ppi->iface_entry = list_first_entry(&iface_stat_list, + struct iface_stat, + list); + spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); + } + return SEQ_START_TOKEN; + } + if (!qtaguid_stats_proc_iface_stat_ptr_valid(ppi->iface_entry)) { + if (ppi->iface_entry) { + pr_err("qtaguid: %s(): iface_entry %p not found\n", + __func__, ppi->iface_entry); + ppi->iface_entry = NULL; + } + return NULL; + } + + spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); + + if (!ppi->tag_pos) { + /* seq_read skipped first next call */ + ts_entry = SEQ_START_TOKEN; + } else { + ts_entry = tag_stat_tree_search( + &ppi->iface_entry->tag_stat_tree, ppi->tag); + if (!ts_entry) { + pr_info("qtaguid: %s(): tag_stat.tag 0x%llx not found. Abort.\n", + __func__, ppi->tag); + return NULL; + } + } + + if (*pos == ppi->tag_pos) { /* normal resume */ + ppi->item_index = ppi->tag_item_index; + } else { + /* seq_read skipped a next call */ + *pos = ppi->tag_pos; + ts_entry = qtaguid_stats_proc_next(m, ts_entry, pos); + } + + return ts_entry; +} + +static void qtaguid_stats_proc_stop(struct seq_file *m, void *v) +{ + struct proc_print_info *ppi = m->private; + if (ppi->iface_entry) + spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock); + spin_unlock_bh(&iface_stat_list_lock); +} + /* * Procfs reader to get all tag stats using style "1)" as described in * fs/proc/generic.c * Groups all protocols tx/rx bytes. */ -static int qtaguid_stats_proc_read(char *page, char **num_items_returned, - off_t items_to_skip, int char_count, int *eof, - void *data) +static int qtaguid_stats_proc_show(struct seq_file *m, void *v) { - struct proc_print_info ppi; - int len; + struct tag_stat *ts_entry = v; - ppi.outp = page; - ppi.item_index = 0; - ppi.char_count = char_count; - ppi.num_items_returned = num_items_returned; - ppi.items_to_skip = items_to_skip; + if (v == SEQ_START_TOKEN) + pp_stats_header(m); + else + pp_sets(m, ts_entry); - if (unlikely(module_passive)) { - len = pp_stats_line(&ppi, 0); - /* The header should always be shorter than the buffer. */ - BUG_ON(len >= ppi.char_count); - (*num_items_returned)++; - *eof = 1; - return len; - } - - CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u " - "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", - current->pid, current->tgid, current_fsuid(), - page, *num_items_returned, - items_to_skip, char_count, *eof); - - if (*eof) - return 0; - - /* The idx is there to help debug when things go belly up. */ - len = pp_stats_line(&ppi, 0); - /* Don't advance the outp unless the whole line was printed */ - if (len >= ppi.char_count) { - *ppi.outp = '\0'; - return ppi.outp - page; - } - if (len) { - ppi.outp += len; - ppi.char_count -= len; - (*num_items_returned)++; - } - - spin_lock_bh(&iface_stat_list_lock); - list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) { - struct rb_node *node; - spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock); - for (node = rb_first(&ppi.iface_entry->tag_stat_tree); - node; - node = rb_next(node)) { - ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); - if (!pp_sets(&ppi)) { - spin_unlock_bh( - &ppi.iface_entry->tag_stat_list_lock); - spin_unlock_bh(&iface_stat_list_lock); - return ppi.outp - page; - } - } - spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock); - } - spin_unlock_bh(&iface_stat_list_lock); - - *eof = 1; - return ppi.outp - page; + return 0; } /*------------------------------------------*/ @@ -2913,6 +2886,47 @@ static struct miscdevice qtu_device = { /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */ }; +static const struct seq_operations proc_qtaguid_ctrl_seqops = { + .start = qtaguid_ctrl_proc_start, + .next = qtaguid_ctrl_proc_next, + .stop = qtaguid_ctrl_proc_stop, + .show = qtaguid_ctrl_proc_show, +}; + +static int proc_qtaguid_ctrl_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &proc_qtaguid_ctrl_seqops, + sizeof(struct proc_ctrl_print_info)); +} + +static const struct file_operations proc_qtaguid_ctrl_fops = { + .open = proc_qtaguid_ctrl_open, + .read = seq_read, + .write = qtaguid_ctrl_proc_write, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const struct seq_operations proc_qtaguid_stats_seqops = { + .start = qtaguid_stats_proc_start, + .next = qtaguid_stats_proc_next, + .stop = qtaguid_stats_proc_stop, + .show = qtaguid_stats_proc_show, +}; + +static int proc_qtaguid_stats_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &proc_qtaguid_stats_seqops, + sizeof(struct proc_print_info)); +} + +static const struct file_operations proc_qtaguid_stats_fops = { + .open = proc_qtaguid_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + /*------------------------------------------*/ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) { @@ -2924,26 +2938,27 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) goto no_dir; } - xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, - *res_procdir); + xt_qtaguid_ctrl_file = proc_create_data("ctrl", proc_ctrl_perms, + *res_procdir, + &proc_qtaguid_ctrl_fops, + NULL); if (!xt_qtaguid_ctrl_file) { pr_err("qtaguid: failed to create xt_qtaguid/ctrl " " file\n"); ret = -ENOMEM; goto no_ctrl_entry; } - xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read; - xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write; - xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, - *res_procdir); + xt_qtaguid_stats_file = proc_create_data("stats", proc_stats_perms, + *res_procdir, + &proc_qtaguid_stats_fops, + NULL); if (!xt_qtaguid_stats_file) { pr_err("qtaguid: failed to create xt_qtaguid/stats " "file\n"); ret = -ENOMEM; goto no_stats_entry; } - xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read; /* * TODO: add support counter hacking * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write; From b30ea0536a45c0cd4b3c250f8cec1a0ed4e28ff5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 3 Jul 2013 15:48:04 -0700 Subject: [PATCH 271/358] ARM: kgdb: ignore breakpoint instructions from user mode Avoid conflicts with user mode usage of the same instructions, as with Clang -ftrapv. Change-Id: I12d1c6d8f94376bfd2503cb0be843d7e478fb6ea Signed-off-by: Todd Poynor --- arch/arm/kernel/kgdb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c index 778c2f7024ff..b321c8fbb87d 100644 --- a/arch/arm/kernel/kgdb.c +++ b/arch/arm/kernel/kgdb.c @@ -144,6 +144,8 @@ int kgdb_arch_handle_exception(int exception_vector, int signo, static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr) { + if (user_mode(regs)) + return -1; kgdb_handle_exception(1, SIGTRAP, 0, regs); return 0; @@ -151,6 +153,8 @@ static int kgdb_brk_fn(struct pt_regs *regs, unsigned int instr) static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int instr) { + if (user_mode(regs)) + return -1; compiled_break = 1; kgdb_handle_exception(1, SIGTRAP, 0, regs); From 526cbfac1562fcb58ef705b935f910c6664688f9 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Thu, 1 Sep 2011 15:26:50 -0400 Subject: [PATCH 272/358] add extra free kbytes tunable Add a userspace visible knob to tell the VM to keep an extra amount of memory free, by increasing the gap between each zone's min and low watermarks. This is useful for realtime applications that call system calls and have a bound on the number of allocations that happen in any short time period. In this application, extra_free_kbytes would be left at an amount equal to or larger than than the maximum number of allocations that happen in any burst. It may also be useful to reduce the memory use of virtual machines (temporarily?), in a way that does not cause memory fragmentation like ballooning does. [ccross] Revived for use on old kernels where no other solution exists. The tunable will be removed on kernels that do better at avoiding direct reclaim. Change-Id: I765a42be8e964bfd3e2886d1ca85a29d60c3bb3e Signed-off-by: Rik van Riel Signed-off-by: Colin Cross --- Documentation/sysctl/vm.txt | 16 ++++++++++++++++ kernel/sysctl.c | 9 +++++++++ mm/page_alloc.c | 32 +++++++++++++++++++++++++------- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index d614a9b6a280..9dc0b8a780ec 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -29,6 +29,7 @@ Currently, these files are in /proc/sys/vm: - dirty_writeback_centisecs - drop_caches - extfrag_threshold +- extra_free_kbytes - hugepages_treat_as_movable - hugetlb_shm_group - laptop_mode @@ -204,6 +205,21 @@ fragmentation index is <= extfrag_threshold. The default value is 500. ============================================================== +extra_free_kbytes + +This parameter tells the VM to keep extra free memory between the threshold +where background reclaim (kswapd) kicks in, and the threshold where direct +reclaim (by allocating processes) kicks in. + +This is useful for workloads that require low latency memory allocations +and have a bounded burstiness in memory allocations, for example a +realtime application that receives and transmits network traffic +(causing in-kernel memory allocations) with a maximum total message burst +size of 200MB may need 200MB of extra free memory to avoid direct reclaim +related latencies. + +============================================================== + hugepages_treat_as_movable This parameter controls whether we can allocate hugepages from ZONE_MOVABLE diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 1fee90f4b3a5..797bfa049bee 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -104,6 +104,7 @@ extern char core_pattern[]; extern unsigned int core_pipe_limit; #endif extern int pid_max; +extern int extra_free_kbytes; extern int min_free_order_shift; extern int pid_max_min, pid_max_max; extern int percpu_pagelist_fraction; @@ -1313,6 +1314,14 @@ static struct ctl_table vm_table[] = { .proc_handler = min_free_kbytes_sysctl_handler, .extra1 = &zero, }, + { + .procname = "extra_free_kbytes", + .data = &extra_free_kbytes, + .maxlen = sizeof(extra_free_kbytes), + .mode = 0644, + .proc_handler = min_free_kbytes_sysctl_handler, + .extra1 = &zero, + }, { .procname = "min_free_order_shift", .data = &min_free_order_shift, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 348ca037b055..2be8811e57f7 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -204,10 +204,22 @@ static char * const zone_names[MAX_NR_ZONES] = { "Movable", }; +/* + * Try to keep at least this much lowmem free. Do not allow normal + * allocations below this point, only high priority ones. Automatically + * tuned according to the amount of memory in the system. + */ int min_free_kbytes = 1024; int user_min_free_kbytes = -1; int min_free_order_shift = 1; +/* + * Extra memory for the system to try freeing. Used to temporarily + * free memory, to make space for new workloads. Anyone can allocate + * down to the min watermarks controlled by min_free_kbytes above. + */ +int extra_free_kbytes = 0; + static unsigned long __meminitdata nr_kernel_pages; static unsigned long __meminitdata nr_all_pages; static unsigned long __meminitdata dma_reserve; @@ -5605,6 +5617,7 @@ static void setup_per_zone_lowmem_reserve(void) static void __setup_per_zone_wmarks(void) { unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10); + unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10); unsigned long lowmem_pages = 0; struct zone *zone; unsigned long flags; @@ -5616,11 +5629,14 @@ static void __setup_per_zone_wmarks(void) } for_each_zone(zone) { - u64 tmp; + u64 min, low; spin_lock_irqsave(&zone->lock, flags); - tmp = (u64)pages_min * zone->managed_pages; - do_div(tmp, lowmem_pages); + min = (u64)pages_min * zone->managed_pages; + do_div(min, lowmem_pages); + low = (u64)pages_low * zone->managed_pages; + do_div(low, vm_total_pages); + if (is_highmem(zone)) { /* * __GFP_HIGH and PF_MEMALLOC allocations usually don't @@ -5641,11 +5657,13 @@ static void __setup_per_zone_wmarks(void) * If it's a lowmem zone, reserve a number of pages * proportionate to the zone's size. */ - zone->watermark[WMARK_MIN] = tmp; + zone->watermark[WMARK_MIN] = min; } - zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + (tmp >> 2); - zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1); + zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + + low + (min >> 2); + zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + + low + (min >> 1); __mod_zone_page_state(zone, NR_ALLOC_BATCH, high_wmark_pages(zone) - @@ -5770,7 +5788,7 @@ module_init(init_per_zone_wmark_min) /* * min_free_kbytes_sysctl_handler - just a wrapper around proc_dointvec() so * that we can call two helper functions whenever min_free_kbytes - * changes. + * or extra_free_kbytes changes. */ int min_free_kbytes_sysctl_handler(ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) From 2f68e2b82c0d29b8adbefec65a61d2fc8c98e818 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 30 Jul 2013 16:45:24 -0700 Subject: [PATCH 273/358] power_supply: kill android-battery driver Discontinued in favor of future userspace charging helpers. Change-Id: I840a94ff42e2219cfd8759f919f6188355a63d92 Signed-off-by: Todd Poynor --- android/configs/android-recommended.cfg | 1 - drivers/power/Kconfig | 10 - drivers/power/Makefile | 1 - drivers/power/android_battery.c | 692 ------------------ include/linux/platform_data/android_battery.h | 47 -- 5 files changed, 751 deletions(-) delete mode 100644 drivers/power/android_battery.c delete mode 100644 include/linux/platform_data/android_battery.h diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index ca3cc8613366..546c37f552bc 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -11,7 +11,6 @@ CONFIG_COMPACTION=y CONFIG_ANDROID_RAM_CONSOLE=y CONFIG_ANDROID_TIMED_GPIO=y CONFIG_BACKLIGHT_LCD_SUPPORT=y -CONFIG_BATTERY_ANDROID=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 1a9b52f9fc13..ba6975123071 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -205,16 +205,6 @@ config BATTERY_MAX17042 with MAX17042. This driver also supports max17047/50 chips which are improved version of max17042. -config BATTERY_ANDROID - tristate "Battery driver for Android" - help - Say Y to enable generic support for battery charging according - to common Android policies. - This driver adds periodic battery level and health monitoring, - kernel log reporting and other debugging features, common board - battery file glue logic for battery/case temperature sensors, - etc. - config BATTERY_Z2 tristate "Z2 battery driver" depends on I2C && MACH_ZIPIT2 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 5f6f23ffe777..ee54a3e4c90a 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_WM8350_POWER) += wm8350_power.o obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o -obj-$(CONFIG_BATTERY_ANDROID) += android_battery.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c deleted file mode 100644 index 8d45ff0f3677..000000000000 --- a/drivers/power/android_battery.c +++ /dev/null @@ -1,692 +0,0 @@ -/* - * android_battery.c - * Android Battery Driver - * - * Copyright (C) 2012 Google, Inc. - * Copyright (C) 2012 Samsung Electronics - * - * Based on work by himihee.seo@samsung.com, ms925.kim@samsung.com, and - * joshua.chang@samsung.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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define FAST_POLL (1 * 60) -#define SLOW_POLL (10 * 60) - -struct android_bat_data { - struct android_bat_platform_data *pdata; - struct android_bat_callbacks callbacks; - - struct device *dev; - - struct power_supply psy_bat; - - struct wake_lock monitor_wake_lock; - struct wake_lock charger_wake_lock; - - int charge_source; - - int batt_temp; - int batt_current; - unsigned int batt_health; - unsigned int batt_vcell; - unsigned int batt_soc; - unsigned int charging_status; - bool recharging; - unsigned long charging_start_time; - - struct workqueue_struct *monitor_wqueue; - struct work_struct monitor_work; - struct work_struct charger_work; - - struct alarm monitor_alarm; - ktime_t last_poll; - - struct dentry *debugfs_entry; -}; - -static enum power_supply_property android_battery_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CURRENT_NOW, -}; - -static DEFINE_MUTEX(android_bat_state_lock); - -static void android_bat_update_data(struct android_bat_data *battery); -static int android_bat_enable_charging(struct android_bat_data *battery, - bool enable); - -static char *charge_source_str(int charge_source) -{ - switch (charge_source) { - case CHARGE_SOURCE_NONE: - return "none"; - case CHARGE_SOURCE_AC: - return "ac"; - case CHARGE_SOURCE_USB: - return "usb"; - default: - break; - } - - return "?"; -} - -static int android_bat_get_property(struct power_supply *ps, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct android_bat_data *battery = - container_of(ps, struct android_bat_data, psy_bat); - - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = battery->charging_status; - break; - case POWER_SUPPLY_PROP_HEALTH: - val->intval = battery->batt_health; - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = 1; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = battery->batt_temp; - break; - case POWER_SUPPLY_PROP_ONLINE: - val->intval = 1; - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - android_bat_update_data(battery); - val->intval = battery->batt_vcell; - if (val->intval == -1) - return -EINVAL; - break; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = battery->batt_soc; - if (val->intval == -1) - return -EINVAL; - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; - break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - android_bat_update_data(battery); - val->intval = battery->batt_current; - break; - default: - return -EINVAL; - } - return 0; -} - -static void android_bat_get_temp(struct android_bat_data *battery) -{ - int batt_temp = 42; /* 4.2C */ - int health = battery->batt_health; - - if (battery->pdata->get_temperature) - battery->pdata->get_temperature(&batt_temp); - - if (battery->charge_source != CHARGE_SOURCE_NONE) { - if (batt_temp >= battery->pdata->temp_high_threshold) { - if (health != POWER_SUPPLY_HEALTH_OVERHEAT && - health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { - pr_info("battery overheat (%d>=%d), " \ - "charging unavailable\n", - batt_temp, - battery->pdata->temp_high_threshold); - battery->batt_health = - POWER_SUPPLY_HEALTH_OVERHEAT; - } - } else if (batt_temp <= battery->pdata->temp_high_recovery && - batt_temp >= battery->pdata->temp_low_recovery) { - if (health == POWER_SUPPLY_HEALTH_OVERHEAT || - health == POWER_SUPPLY_HEALTH_COLD) { - pr_info("battery recovery (%d,%d~%d)," \ - "charging available\n", - batt_temp, - battery->pdata->temp_low_recovery, - battery->pdata->temp_high_recovery); - battery->batt_health = - POWER_SUPPLY_HEALTH_GOOD; - } - } else if (batt_temp <= battery->pdata->temp_low_threshold) { - if (health != POWER_SUPPLY_HEALTH_COLD && - health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) { - pr_info("battery cold (%d <= %d)," \ - "charging unavailable\n", - batt_temp, - battery->pdata->temp_low_threshold); - battery->batt_health = - POWER_SUPPLY_HEALTH_COLD; - } - } - } - - battery->batt_temp = batt_temp; -} - -/* - * android_bat_state_lock not held, may call back into - * android_bat_charge_source_changed. Gathering data here can be - * non-atomic; updating our state based on the data may need to be - * atomic. - */ - -static void android_bat_update_data(struct android_bat_data *battery) -{ - int ret; - int v; - - if (battery->pdata->poll_charge_source) - battery->charge_source = battery->pdata->poll_charge_source(); - - if (battery->pdata->get_voltage_now) { - ret = battery->pdata->get_voltage_now(); - battery->batt_vcell = ret >= 0 ? ret : 4242000; - } - - if (battery->pdata->get_capacity) { - ret = battery->pdata->get_capacity(); - battery->batt_soc = ret >= 0 ? ret : 42; - } - - if (battery->pdata->get_current_now) { - ret = battery->pdata->get_current_now(&v); - - if (!ret) - battery->batt_current = v; - } - - android_bat_get_temp(battery); -} - -static void android_bat_set_charge_time(struct android_bat_data *battery, - bool enable) -{ - if (enable && !battery->charging_start_time) { - struct timespec cur_time; - - get_monotonic_boottime(&cur_time); - /* record start time for charge timeout timer */ - battery->charging_start_time = cur_time.tv_sec; - } else if (!enable) { - /* clear charge timeout timer */ - battery->charging_start_time = 0; - } -} - -static int android_bat_enable_charging(struct android_bat_data *battery, - bool enable) -{ - if (enable && (battery->batt_health != POWER_SUPPLY_HEALTH_GOOD)) { - battery->charging_status = - POWER_SUPPLY_STATUS_NOT_CHARGING; - return -EPERM; - } - - if (enable) { - if (battery->pdata && battery->pdata->set_charging_current) - battery->pdata->set_charging_current - (battery->charge_source); - } - - if (battery->pdata && battery->pdata->set_charging_enable) - battery->pdata->set_charging_enable(enable); - - android_bat_set_charge_time(battery, enable); - pr_info("battery: enable=%d charger: %s\n", enable, - charge_source_str(battery->charge_source)); - return 0; -} - -static bool android_bat_charge_timeout(struct android_bat_data *battery, - unsigned long timeout) -{ - struct timespec cur_time; - - if (!battery->charging_start_time) - return 0; - - get_monotonic_boottime(&cur_time); - pr_debug("%s: Start time: %ld, End time: %ld, current time: %ld\n", - __func__, battery->charging_start_time, - battery->charging_start_time + timeout, - cur_time.tv_sec); - return cur_time.tv_sec >= battery->charging_start_time + timeout; -} - -static void android_bat_charging_timer(struct android_bat_data *battery) -{ - if (!battery->charging_start_time && - battery->charging_status == POWER_SUPPLY_STATUS_CHARGING) { - android_bat_enable_charging(battery, true); - battery->recharging = true; - pr_debug("%s: charge status charging but timer is expired\n", - __func__); - } else if (battery->charging_start_time == 0) { - pr_debug("%s: charging_start_time never initialized\n", - __func__); - return; - } - - if (android_bat_charge_timeout( - battery, - battery->recharging ? battery->pdata->recharging_time : - battery->pdata->full_charging_time)) { - android_bat_enable_charging(battery, false); - if (battery->batt_vcell > - battery->pdata->recharging_voltage && - battery->batt_soc == 100) - battery->charging_status = - POWER_SUPPLY_STATUS_FULL; - battery->recharging = false; - battery->charging_start_time = 0; - pr_info("battery: charging timer expired\n"); - } - - return; -} - -static void android_bat_charge_source_changed(struct android_bat_callbacks *ptr, - int charge_source) -{ - struct android_bat_data *battery = - container_of(ptr, struct android_bat_data, callbacks); - - wake_lock(&battery->charger_wake_lock); - mutex_lock(&android_bat_state_lock); - battery->charge_source = charge_source; - - pr_info("battery: charge source type was changed: %s\n", - charge_source_str(battery->charge_source)); - - mutex_unlock(&android_bat_state_lock); - queue_work(battery->monitor_wqueue, &battery->charger_work); -} - -static void android_bat_set_full_status(struct android_bat_callbacks *ptr) -{ - struct android_bat_data *battery = - container_of(ptr, struct android_bat_data, callbacks); - - mutex_lock(&android_bat_state_lock); - pr_info("battery: battery full\n"); - battery->charging_status = POWER_SUPPLY_STATUS_FULL; - android_bat_enable_charging(battery, false); - battery->recharging = false; - mutex_unlock(&android_bat_state_lock); - power_supply_changed(&battery->psy_bat); -} - -static void android_bat_charger_work(struct work_struct *work) -{ - struct android_bat_data *battery = - container_of(work, struct android_bat_data, charger_work); - - mutex_lock(&android_bat_state_lock); - - switch (battery->charge_source) { - case CHARGE_SOURCE_NONE: - battery->charging_status = POWER_SUPPLY_STATUS_DISCHARGING; - android_bat_enable_charging(battery, false); - battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; - battery->recharging = false; - battery->charging_start_time = 0; - break; - case CHARGE_SOURCE_USB: - case CHARGE_SOURCE_AC: - /* - * If charging status indicates a charger was already - * connected prior to this and the status is something - * other than charging ("full" or "not-charging"), leave - * the status alone. - */ - if (battery->charging_status == - POWER_SUPPLY_STATUS_DISCHARGING || - battery->charging_status == POWER_SUPPLY_STATUS_UNKNOWN) - battery->charging_status = POWER_SUPPLY_STATUS_CHARGING; - - /* - * Don't re-enable charging if the battery is full and we - * are not actively re-charging it, or if "not-charging" - * status is set. - */ - if (!((battery->charging_status == POWER_SUPPLY_STATUS_FULL - && !battery->recharging) || battery->charging_status == - POWER_SUPPLY_STATUS_NOT_CHARGING)) - android_bat_enable_charging(battery, true); - - break; - default: - pr_err("%s: Invalid charger type\n", __func__); - break; - } - - mutex_unlock(&android_bat_state_lock); - wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); - power_supply_changed(&battery->psy_bat); -} - - -static void android_bat_monitor_set_alarm(struct android_bat_data *battery, - int seconds) -{ - alarm_start(&battery->monitor_alarm, - ktime_add(battery->last_poll, ktime_set(seconds, 0))); -} - -static void android_bat_monitor_work(struct work_struct *work) -{ - struct android_bat_data *battery = - container_of(work, struct android_bat_data, monitor_work); - struct timespec cur_time; - - wake_lock(&battery->monitor_wake_lock); - android_bat_update_data(battery); - mutex_lock(&android_bat_state_lock); - - switch (battery->charging_status) { - case POWER_SUPPLY_STATUS_FULL: - if (battery->batt_vcell < battery->pdata->recharging_voltage && - !battery->recharging) { - battery->recharging = true; - android_bat_enable_charging(battery, true); - pr_info("battery: start recharging, v=%d\n", - battery->batt_vcell/1000); - } - break; - case POWER_SUPPLY_STATUS_DISCHARGING: - break; - case POWER_SUPPLY_STATUS_CHARGING: - switch (battery->batt_health) { - case POWER_SUPPLY_HEALTH_OVERHEAT: - case POWER_SUPPLY_HEALTH_COLD: - case POWER_SUPPLY_HEALTH_OVERVOLTAGE: - case POWER_SUPPLY_HEALTH_DEAD: - case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: - battery->charging_status = - POWER_SUPPLY_STATUS_NOT_CHARGING; - android_bat_enable_charging(battery, false); - - pr_info("battery: Not charging, health=%d\n", - battery->batt_health); - break; - default: - break; - } - break; - case POWER_SUPPLY_STATUS_NOT_CHARGING: - if (battery->batt_health == POWER_SUPPLY_HEALTH_GOOD) { - pr_info("battery: battery health recovered\n"); - if (battery->charge_source != CHARGE_SOURCE_NONE) { - android_bat_enable_charging(battery, true); - battery->charging_status - = POWER_SUPPLY_STATUS_CHARGING; - } else { - battery->charging_status - = POWER_SUPPLY_STATUS_DISCHARGING; - } - } - break; - default: - pr_err("%s: Undefined battery status: %d\n", __func__, - battery->charging_status); - break; - } - - android_bat_charging_timer(battery); - get_monotonic_boottime(&cur_time); - pr_info("battery: l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", - battery->batt_soc, battery->batt_vcell/1000, - battery->batt_current, battery->batt_temp < 0 ? "-" : "", - abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), - battery->batt_health, battery->charging_status, - battery->recharging ? "r" : "", - battery->charging_start_time ? - cur_time.tv_sec - battery->charging_start_time : 0, - charge_source_str(battery->charge_source)); - mutex_unlock(&android_bat_state_lock); - power_supply_changed(&battery->psy_bat); - battery->last_poll = ktime_get_boottime(); - android_bat_monitor_set_alarm(battery, FAST_POLL); - wake_unlock(&battery->monitor_wake_lock); - return; -} - -static enum alarmtimer_restart android_bat_monitor_alarm( - struct alarm *alarm, ktime_t now) -{ - struct android_bat_data *battery = - container_of(alarm, struct android_bat_data, monitor_alarm); - - wake_lock(&battery->monitor_wake_lock); - queue_work(battery->monitor_wqueue, &battery->monitor_work); - return ALARMTIMER_NORESTART; -} - -static int android_power_debug_dump(struct seq_file *s, void *unused) -{ - struct android_bat_data *battery = s->private; - struct timespec cur_time; - - android_bat_update_data(battery); - get_monotonic_boottime(&cur_time); - mutex_lock(&android_bat_state_lock); - seq_printf(s, "l=%d v=%d c=%d temp=%s%ld.%ld h=%d st=%d%s ct=%lu type=%s\n", - battery->batt_soc, battery->batt_vcell/1000, - battery->batt_current, battery->batt_temp < 0 ? "-" : "", - abs(battery->batt_temp / 10), abs(battery->batt_temp % 10), - battery->batt_health, battery->charging_status, - battery->recharging ? "r" : "", - battery->charging_start_time ? - cur_time.tv_sec - battery->charging_start_time : 0, - charge_source_str(battery->charge_source)); - mutex_unlock(&android_bat_state_lock); - return 0; -} - -static int android_power_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, android_power_debug_dump, inode->i_private); -} - -static const struct file_operations android_power_debug_fops = { - .open = android_power_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int android_bat_probe(struct platform_device *pdev) -{ - struct android_bat_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct android_bat_data *battery; - int ret = 0; - - dev_info(&pdev->dev, "Android Battery Driver\n"); - battery = kzalloc(sizeof(*battery), GFP_KERNEL); - if (!battery) - return -ENOMEM; - - battery->pdata = pdata; - if (!battery->pdata) { - pr_err("%s : No platform data\n", __func__); - ret = -EINVAL; - goto err_pdata; - } - - battery->dev = &pdev->dev; - platform_set_drvdata(pdev, battery); - battery->batt_health = POWER_SUPPLY_HEALTH_GOOD; - - battery->psy_bat.name = "android-battery", - battery->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY, - battery->psy_bat.properties = android_battery_props, - battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props), - battery->psy_bat.get_property = android_bat_get_property, - - battery->batt_vcell = -1; - battery->batt_soc = -1; - - wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND, - "android-battery-monitor"); - wake_lock_init(&battery->charger_wake_lock, WAKE_LOCK_SUSPEND, - "android-chargerdetect"); - - ret = power_supply_register(&pdev->dev, &battery->psy_bat); - if (ret) { - dev_err(battery->dev, "%s: failed to register psy_bat\n", - __func__); - goto err_psy_bat_reg; - } - - battery->monitor_wqueue = - alloc_workqueue(dev_name(&pdev->dev), WQ_FREEZABLE, 1); - if (!battery->monitor_wqueue) { - dev_err(battery->dev, "%s: fail to create workqueue\n", - __func__); - goto err_wq; - } - - INIT_WORK(&battery->monitor_work, android_bat_monitor_work); - INIT_WORK(&battery->charger_work, android_bat_charger_work); - - battery->callbacks.charge_source_changed = - android_bat_charge_source_changed; - battery->callbacks.battery_set_full = - android_bat_set_full_status; - if (battery->pdata && battery->pdata->register_callbacks) - battery->pdata->register_callbacks(&battery->callbacks); - - /* get initial charger status */ - if (battery->pdata->poll_charge_source) - battery->charge_source = battery->pdata->poll_charge_source(); - - wake_lock(&battery->charger_wake_lock); - queue_work(battery->monitor_wqueue, &battery->charger_work); - - wake_lock(&battery->monitor_wake_lock); - battery->last_poll = ktime_get_boottime(); - alarm_init(&battery->monitor_alarm, ALARM_BOOTTIME, - android_bat_monitor_alarm); - queue_work(battery->monitor_wqueue, &battery->monitor_work); - - battery->debugfs_entry = - debugfs_create_file("android-power", S_IRUGO, NULL, - battery, &android_power_debug_fops); - if (!battery->debugfs_entry) - pr_err("failed to create android-power debugfs entry\n"); - - return 0; - -err_wq: - power_supply_unregister(&battery->psy_bat); -err_psy_bat_reg: - wake_lock_destroy(&battery->monitor_wake_lock); - wake_lock_destroy(&battery->charger_wake_lock); -err_pdata: - kfree(battery); - - return ret; -} - -static int android_bat_remove(struct platform_device *pdev) -{ - struct android_bat_data *battery = platform_get_drvdata(pdev); - - alarm_cancel(&battery->monitor_alarm); - flush_workqueue(battery->monitor_wqueue); - destroy_workqueue(battery->monitor_wqueue); - power_supply_unregister(&battery->psy_bat); - wake_lock_destroy(&battery->monitor_wake_lock); - wake_lock_destroy(&battery->charger_wake_lock); - debugfs_remove(battery->debugfs_entry); - kfree(battery); - return 0; -} - -static int android_bat_suspend(struct device *dev) -{ - struct android_bat_data *battery = dev_get_drvdata(dev); - - cancel_work_sync(&battery->monitor_work); - android_bat_monitor_set_alarm( - battery, - battery->charge_source == CHARGE_SOURCE_NONE ? - SLOW_POLL : FAST_POLL); - return 0; -} - -static void android_bat_resume(struct device *dev) -{ - struct android_bat_data *battery = dev_get_drvdata(dev); - - android_bat_monitor_set_alarm(battery, FAST_POLL); - return; -} - -static const struct dev_pm_ops android_bat_pm_ops = { - .prepare = android_bat_suspend, - .complete = android_bat_resume, -}; - -static struct platform_driver android_bat_driver = { - .driver = { - .name = "android-battery", - .owner = THIS_MODULE, - .pm = &android_bat_pm_ops, - }, - .probe = android_bat_probe, - .remove = android_bat_remove, -}; - -static int __init android_bat_init(void) -{ - return platform_driver_register(&android_bat_driver); -} - -static void __exit android_bat_exit(void) -{ - platform_driver_unregister(&android_bat_driver); -} - -late_initcall(android_bat_init); -module_exit(android_bat_exit); - -MODULE_DESCRIPTION("Android battery driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/android_battery.h b/include/linux/platform_data/android_battery.h deleted file mode 100644 index f6c8298fd885..000000000000 --- a/include/linux/platform_data/android_battery.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * android_battery.h - * - * Copyright (C) 2012 Samsung Electronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _LINUX_ANDROID_BATTERY_H -#define _LINUX_ANDROID_BATTERY_H - -enum { - CHARGE_SOURCE_NONE = 0, - CHARGE_SOURCE_AC, - CHARGE_SOURCE_USB, -}; - -struct android_bat_callbacks { - void (*charge_source_changed) - (struct android_bat_callbacks *, int); - void (*battery_set_full)(struct android_bat_callbacks *); -}; - -struct android_bat_platform_data { - void (*register_callbacks)(struct android_bat_callbacks *); - void (*unregister_callbacks)(void); - void (*set_charging_current) (int); - void (*set_charging_enable) (int); - int (*poll_charge_source) (void); - int (*get_capacity) (void); - int (*get_temperature) (int *); - int (*get_voltage_now)(void); - int (*get_current_now)(int *); - - int temp_high_threshold; - int temp_high_recovery; - int temp_low_recovery; - int temp_low_threshold; - - unsigned long full_charging_time; - unsigned long recharging_time; - unsigned int recharging_voltage; -}; - -#endif From d3743a552928306dd8d3654c43ab264b3b56aca6 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 26 Jun 2013 17:26:01 -0700 Subject: [PATCH 274/358] mm: add a field to store names for private anonymous memory Userspace processes often have multiple allocators that each do anonymous mmaps to get memory. When examining memory usage of individual processes or systems as a whole, it is useful to be able to break down the various heaps that were allocated by each layer and examine their size, RSS, and physical memory usage. This patch adds a user pointer to the shared union in vm_area_struct that points to a null terminated string inside the user process containing a name for the vma. vmas that point to the same address will be merged, but vmas that point to equivalent strings at different addresses will not be merged. Userspace can set the name for a region of memory by calling prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start, len, (unsigned long)name); Setting the name to NULL clears it. The names of named anonymous vmas are shown in /proc/pid/maps as [anon:] and in /proc/pid/smaps in a new "Name" field that is only present for named vmas. If the userspace pointer is no longer valid all or part of the name will be replaced with "". The idea to store a userspace pointer to reduce the complexity within mm (at the expense of the complexity of reading /proc/pid/mem) came from Dave Hansen. This results in no runtime overhead in the mm subsystem other than comparing the anon_name pointers when considering vma merging. The pointer is stored in a union with fieds that are only used on file-backed mappings, so it does not increase memory usage. Change-Id: Ie2ffc0967d4ffe7ee4c70781313c7b00cf7e3092 Signed-off-by: Colin Cross --- Documentation/filesystems/proc.txt | 6 ++ fs/proc/task_mmu.c | 62 ++++++++++++ include/linux/mm.h | 2 +- include/linux/mm_types.h | 14 +++ include/uapi/linux/prctl.h | 3 + kernel/sys.c | 152 +++++++++++++++++++++++++++++ mm/madvise.c | 3 +- mm/mempolicy.c | 2 +- mm/mlock.c | 3 +- mm/mmap.c | 44 +++++---- mm/mprotect.c | 3 +- 11 files changed, 271 insertions(+), 23 deletions(-) diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index f00bee144add..90adc374351d 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -369,6 +369,8 @@ is not associated with a file: [stack:1001] = the stack of the thread with tid 1001 [vdso] = the "virtual dynamic shared object", the kernel system call handler + [anon:] = an anonymous mapping that has been + named by userspace or if empty, the mapping is anonymous. @@ -419,6 +421,7 @@ KernelPageSize: 4 kB MMUPageSize: 4 kB Locked: 374 kB VmFlags: rd ex mr mw me de +Name: name from userspace the first of these lines shows the same information as is displayed for the mapping in /proc/PID/maps. The remaining lines show the size of the mapping @@ -470,6 +473,9 @@ Note that there is no guarantee that every flag and associated mnemonic will be present in all further kernel releases. Things get changed, the flags may be vanished or the reverse -- new added. +The "Name" field will only be present on a mapping that has been named by +userspace, and will show the name passed in by userspace. + This file is only present if the CONFIG_MMU kernel configuration option is enabled. diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index fb52b548080d..d54d00fb6b4c 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -128,6 +128,56 @@ static void release_task_mempolicy(struct proc_maps_private *priv) } #endif +static void seq_print_vma_name(struct seq_file *m, struct vm_area_struct *vma) +{ + const char __user *name = vma_get_anon_name(vma); + struct mm_struct *mm = vma->vm_mm; + + unsigned long page_start_vaddr; + unsigned long page_offset; + unsigned long num_pages; + unsigned long max_len = NAME_MAX; + int i; + + page_start_vaddr = (unsigned long)name & PAGE_MASK; + page_offset = (unsigned long)name - page_start_vaddr; + num_pages = DIV_ROUND_UP(page_offset + max_len, PAGE_SIZE); + + seq_puts(m, "[anon:"); + + for (i = 0; i < num_pages; i++) { + int len; + int write_len; + const char *kaddr; + long pages_pinned; + struct page *page; + + pages_pinned = get_user_pages(current, mm, page_start_vaddr, + 1, 0, 0, &page, NULL); + if (pages_pinned < 1) { + seq_puts(m, "]"); + return; + } + + kaddr = (const char *)kmap(page); + len = min(max_len, PAGE_SIZE - page_offset); + write_len = strnlen(kaddr + page_offset, len); + seq_write(m, kaddr + page_offset, write_len); + kunmap(page); + put_page(page); + + /* if strnlen hit a null terminator then we're done */ + if (write_len != len) + break; + + max_len -= len; + page_offset = 0; + page_start_vaddr += PAGE_SIZE; + } + + seq_putc(m, ']'); +} + static void vma_stop(struct proc_maps_private *priv, struct vm_area_struct *vma) { if (vma && vma != priv->tail_vma) { @@ -329,6 +379,12 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) seq_pad(m, ' '); seq_printf(m, "[stack:%d]", tid); } + goto done; + } + + if (vma_get_anon_name(vma)) { + seq_pad(m, ' '); + seq_print_vma_name(m, vma); } } @@ -631,6 +687,12 @@ static int show_smap(struct seq_file *m, void *v, int is_pid) show_smap_vma_flags(m, vma); + if (vma_get_anon_name(vma)) { + seq_puts(m, "Name: "); + seq_print_vma_name(m, vma); + seq_putc(m, '\n'); + } + if (m->count < m->size) /* vma is copied successfully */ m->version = (vma != get_gate_vma(task->mm)) ? vma->vm_start : 0; diff --git a/include/linux/mm.h b/include/linux/mm.h index dc25be363fff..5352fa9f4ded 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1731,7 +1731,7 @@ extern int vma_adjust(struct vm_area_struct *vma, unsigned long start, extern struct vm_area_struct *vma_merge(struct mm_struct *, struct vm_area_struct *prev, unsigned long addr, unsigned long end, unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t, - struct mempolicy *); + struct mempolicy *, const char __user *); extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *); extern int split_vma(struct mm_struct *, struct vm_area_struct *, unsigned long addr, int new_below); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 290901a8c1de..92b8131059df 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -273,6 +273,10 @@ struct vm_area_struct { * For areas with an address space and backing store, * linkage into the address_space->i_mmap interval tree, or * linkage of vma in the address_space->i_mmap_nonlinear list. + * + * For private anonymous mappings, a pointer to a null terminated string + * in the user process containing the name given to the vma, or NULL + * if unnamed. */ union { struct { @@ -280,6 +284,7 @@ struct vm_area_struct { unsigned long rb_subtree_last; } linear; struct list_head nonlinear; + const char __user *anon_name; } shared; /* @@ -509,4 +514,13 @@ static inline void clear_tlb_flush_pending(struct mm_struct *mm) } #endif +/* Return the name for an anonymous mapping or NULL for a file-backed mapping */ +static inline const char __user *vma_get_anon_name(struct vm_area_struct *vma) +{ + if (vma->vm_file) + return NULL; + + return vma->shared.anon_name; +} + #endif /* _LINUX_MM_TYPES_H */ diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index 289760f424aa..253856a2a8ad 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -149,4 +149,7 @@ #define PR_GET_TID_ADDRESS 40 +#define PR_SET_VMA 0x53564d41 +# define PR_SET_VMA_ANON_NAME 0 + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/sys.c b/kernel/sys.c index c0a58be780a4..d0e9efaea8e3 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include @@ -1833,6 +1835,153 @@ static int prctl_get_tid_address(struct task_struct *me, int __user **tid_addr) } #endif +#ifdef CONFIG_MMU +static int prctl_update_vma_anon_name(struct vm_area_struct *vma, + struct vm_area_struct **prev, + unsigned long start, unsigned long end, + const char __user *name_addr) +{ + struct mm_struct * mm = vma->vm_mm; + int error = 0; + pgoff_t pgoff; + + if (name_addr == vma_get_anon_name(vma)) { + *prev = vma; + goto out; + } + + pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); + *prev = vma_merge(mm, *prev, start, end, vma->vm_flags, vma->anon_vma, + vma->vm_file, pgoff, vma_policy(vma), + name_addr); + if (*prev) { + vma = *prev; + goto success; + } + + *prev = vma; + + if (start != vma->vm_start) { + error = split_vma(mm, vma, start, 1); + if (error) + goto out; + } + + if (end != vma->vm_end) { + error = split_vma(mm, vma, end, 0); + if (error) + goto out; + } + +success: + if (!vma->vm_file) + vma->shared.anon_name = name_addr; + +out: + if (error == -ENOMEM) + error = -EAGAIN; + return error; +} + +static int prctl_set_vma_anon_name(unsigned long start, unsigned long end, + unsigned long arg) +{ + unsigned long tmp; + struct vm_area_struct * vma, *prev; + int unmapped_error = 0; + int error = -EINVAL; + + /* + * If the interval [start,end) covers some unmapped address + * ranges, just ignore them, but return -ENOMEM at the end. + * - this matches the handling in madvise. + */ + vma = find_vma_prev(current->mm, start, &prev); + if (vma && start > vma->vm_start) + prev = vma; + + for (;;) { + /* Still start < end. */ + error = -ENOMEM; + if (!vma) + return error; + + /* Here start < (end|vma->vm_end). */ + if (start < vma->vm_start) { + unmapped_error = -ENOMEM; + start = vma->vm_start; + if (start >= end) + return error; + } + + /* Here vma->vm_start <= start < (end|vma->vm_end) */ + tmp = vma->vm_end; + if (end < tmp) + tmp = end; + + /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */ + error = prctl_update_vma_anon_name(vma, &prev, start, end, + (const char __user *)arg); + if (error) + return error; + start = tmp; + if (prev && start < prev->vm_end) + start = prev->vm_end; + error = unmapped_error; + if (start >= end) + return error; + if (prev) + vma = prev->vm_next; + else /* madvise_remove dropped mmap_sem */ + vma = find_vma(current->mm, start); + } +} + +static int prctl_set_vma(unsigned long opt, unsigned long start, + unsigned long len_in, unsigned long arg) +{ + struct mm_struct *mm = current->mm; + int error; + unsigned long len; + unsigned long end; + + if (start & ~PAGE_MASK) + return -EINVAL; + len = (len_in + ~PAGE_MASK) & PAGE_MASK; + + /* Check to see whether len was rounded up from small -ve to zero */ + if (len_in && !len) + return -EINVAL; + + end = start + len; + if (end < start) + return -EINVAL; + + if (end == start) + return 0; + + down_write(&mm->mmap_sem); + + switch (opt) { + case PR_SET_VMA_ANON_NAME: + error = prctl_set_vma_anon_name(start, end, arg); + break; + default: + error = -EINVAL; + } + + up_write(&mm->mmap_sem); + + return error; +} +#else /* CONFIG_MMU */ +static int prctl_set_vma(unsigned long opt, unsigned long start, + unsigned long len_in, unsigned long arg) +{ + return -EINVAL; +} +#endif + SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5) { @@ -1996,6 +2145,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, if (arg2 || arg3 || arg4 || arg5) return -EINVAL; return current->no_new_privs ? 1 : 0; + case PR_SET_VMA: + error = prctl_set_vma(arg2, arg3, arg4, arg5); + break; default: error = -EINVAL; break; diff --git a/mm/madvise.c b/mm/madvise.c index 539eeb96b323..2590a5250660 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -102,7 +102,8 @@ static long madvise_behavior(struct vm_area_struct *vma, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, new_flags, vma->anon_vma, - vma->vm_file, pgoff, vma_policy(vma)); + vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (*prev) { vma = *prev; goto success; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index ae3c8f3595d4..a50d8a2dae7b 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -766,7 +766,7 @@ static int mbind_range(struct mm_struct *mm, unsigned long start, ((vmstart - vma->vm_start) >> PAGE_SHIFT); prev = vma_merge(mm, prev, vmstart, vmend, vma->vm_flags, vma->anon_vma, vma->vm_file, pgoff, - new_pol); + new_pol, vma_get_anon_name(vma)); if (prev) { vma = prev; next = vma->vm_next; diff --git a/mm/mlock.c b/mm/mlock.c index 4e1a68162285..7341db25dae5 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -557,7 +557,8 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma, - vma->vm_file, pgoff, vma_policy(vma)); + vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (*prev) { vma = *prev; goto success; diff --git a/mm/mmap.c b/mm/mmap.c index 20ff0c33274c..5f8401460ee5 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -892,7 +892,8 @@ again: remove_next = 1 + (end > next->vm_end); * per-vma resources, so we don't attempt to merge those. */ static inline int is_mergeable_vma(struct vm_area_struct *vma, - struct file *file, unsigned long vm_flags) + struct file *file, unsigned long vm_flags, + const char __user *anon_name) { /* * VM_SOFTDIRTY should not prevent from VMA merging, if we @@ -908,6 +909,8 @@ static inline int is_mergeable_vma(struct vm_area_struct *vma, return 0; if (vma->vm_ops && vma->vm_ops->close) return 0; + if (vma_get_anon_name(vma) != anon_name) + return 0; return 1; } @@ -938,9 +941,10 @@ static inline int is_mergeable_anon_vma(struct anon_vma *anon_vma1, */ static int can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, - struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) + struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, + const char __user *anon_name) { - if (is_mergeable_vma(vma, file, vm_flags) && + if (is_mergeable_vma(vma, file, vm_flags, anon_name) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { if (vma->vm_pgoff == vm_pgoff) return 1; @@ -957,9 +961,10 @@ can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, */ static int can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, - struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) + struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, + const char __user *anon_name) { - if (is_mergeable_vma(vma, file, vm_flags) && + if (is_mergeable_vma(vma, file, vm_flags, anon_name) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { pgoff_t vm_pglen; vm_pglen = vma_pages(vma); @@ -970,9 +975,9 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, } /* - * Given a mapping request (addr,end,vm_flags,file,pgoff), figure out - * whether that can be merged with its predecessor or its successor. - * Or both (it neatly fills a hole). + * Given a mapping request (addr,end,vm_flags,file,pgoff,anon_name), + * figure out whether that can be merged with its predecessor or its + * successor. Or both (it neatly fills a hole). * * In most cases - when called for mmap, brk or mremap - [addr,end) is * certain not to be mapped by the time vma_merge is called; but when @@ -1002,7 +1007,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, struct vm_area_struct *prev, unsigned long addr, unsigned long end, unsigned long vm_flags, struct anon_vma *anon_vma, struct file *file, - pgoff_t pgoff, struct mempolicy *policy) + pgoff_t pgoff, struct mempolicy *policy, + const char __user *anon_name) { pgoff_t pglen = (end - addr) >> PAGE_SHIFT; struct vm_area_struct *area, *next; @@ -1028,15 +1034,15 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, */ if (prev && prev->vm_end == addr && mpol_equal(vma_policy(prev), policy) && - can_vma_merge_after(prev, vm_flags, - anon_vma, file, pgoff)) { + can_vma_merge_after(prev, vm_flags, anon_vma, + file, pgoff, anon_name)) { /* * OK, it can. Can we now merge in the successor as well? */ if (next && end == next->vm_start && mpol_equal(policy, vma_policy(next)) && - can_vma_merge_before(next, vm_flags, - anon_vma, file, pgoff+pglen) && + can_vma_merge_before(next, vm_flags, anon_vma, + file, pgoff+pglen, anon_name) && is_mergeable_anon_vma(prev->anon_vma, next->anon_vma, NULL)) { /* cases 1, 6 */ @@ -1056,8 +1062,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, */ if (next && end == next->vm_start && mpol_equal(policy, vma_policy(next)) && - can_vma_merge_before(next, vm_flags, - anon_vma, file, pgoff+pglen)) { + can_vma_merge_before(next, vm_flags, anon_vma, + file, pgoff+pglen, anon_name)) { if (prev && addr < prev->vm_end) /* case 4 */ err = vma_adjust(prev, prev->vm_start, addr, prev->vm_pgoff, NULL); @@ -1539,7 +1545,8 @@ munmap_back: /* * Can we just expand an old mapping? */ - vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, NULL); + vma = vma_merge(mm, prev, addr, addr + len, vm_flags, NULL, file, pgoff, + NULL, NULL); if (vma) goto out; @@ -2642,7 +2649,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len) /* Can we just expand an old private anonymous mapping? */ vma = vma_merge(mm, prev, addr, addr + len, flags, - NULL, NULL, pgoff, NULL); + NULL, NULL, pgoff, NULL, NULL); if (vma) goto out; @@ -2801,7 +2808,8 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, if (find_vma_links(mm, addr, addr + len, &prev, &rb_link, &rb_parent)) return NULL; /* should never get here */ new_vma = vma_merge(mm, prev, addr, addr + len, vma->vm_flags, - vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma)); + vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (new_vma) { /* * Source vma may have been merged into new_vma diff --git a/mm/mprotect.c b/mm/mprotect.c index 769a67a15803..80b9bca07ef7 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -252,7 +252,8 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, */ pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *pprev = vma_merge(mm, *pprev, start, end, newflags, - vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma)); + vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), + vma_get_anon_name(vma)); if (*pprev) { vma = *pprev; goto success; From a79ae42ee77d005c244a2d2882b7da847748b099 Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Sun, 25 Aug 2013 19:23:34 +0900 Subject: [PATCH 275/358] cpufreq: interactive: fix show_target_loads and show_above_hispeed_delay Remove a trailing whitespace from target_loads and above_hispeed_delay. Problem happens when user-space program tried to restore parameters that saved before changing parameters. In this case was returned error(EINVAL). Change-Id: I5a74e3824602cd6f2b74651adda5ec1b627e61e9 Signed-off-by: Minsung Kim --- drivers/cpufreq/cpufreq_interactive.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 90958fdd64dc..a66748e13b38 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -742,7 +742,7 @@ static ssize_t show_target_loads( ret += sprintf(buf + ret, "%u%s", target_loads[i], i & 0x1 ? ":" : " "); - ret += sprintf(buf + ret, "\n"); + ret += sprintf(buf + --ret, "\n"); spin_unlock_irqrestore(&target_loads_lock, flags); return ret; } @@ -785,7 +785,7 @@ static ssize_t show_above_hispeed_delay( ret += sprintf(buf + ret, "%u%s", above_hispeed_delay[i], i & 0x1 ? ":" : " "); - ret += sprintf(buf + ret, "\n"); + ret += sprintf(buf + --ret, "\n"); spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); return ret; } From e70246a62be37ca90dd65b6bbafc8cf7d4e5dac9 Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Thu, 12 Sep 2013 01:42:18 +0000 Subject: [PATCH 276/358] USB: remove duplicate out endpoint creation in MTP mode Android MTP gadget uses 3 endpoints which are 1 in endpoint, 1 out endpoint, and 1 interrupt endpoint. However when MTP gadget creates its endpoints, it creates the out endpoint twice and overwrites the first created out endpoint with the second one, so that it causes a leak of endpoint resources. Change-Id: Iba82950095610b26b362f4b10a67cedfb1fee366 Signed-off-by: Peter Oh Reviewed-on: http://mps-gerrit.broadcom.com/37744 Reviewed-by: Graham Williams Reviewed-by: John Garry Branch-Open: Branch Status Reviewed-by: Checkpatch Status Reviewed-by: Joyjit Nath Tested-by: AutoSubmit Status --- drivers/usb/gadget/f_mtp.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 9ab94697c196..12fb818ab147 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -410,15 +410,6 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev, ep->driver_data = dev; /* claim the endpoint */ dev->ep_out = ep; - ep = usb_ep_autoconfig(cdev->gadget, out_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for mtp ep_out got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_out = ep; - ep = usb_ep_autoconfig(cdev->gadget, intr_desc); if (!ep) { DBG(cdev, "usb_ep_autoconfig for ep_intr failed\n"); From 8d2659b84a2405aac6c20651bc70d7bf153ee3a5 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Fri, 11 Oct 2013 13:54:41 -0700 Subject: [PATCH 277/358] staging: sw_sync: add stubs for kernels without CONFIG_SW_SYNC Change-Id: I7a12679eb3c10b1beaf36c6612cf2e3f6662a034 Signed-off-by: Greg Hackmann --- drivers/staging/android/sw_sync.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/android/sw_sync.h b/drivers/staging/android/sw_sync.h index 5aaf71d6974b..aba25cbb0382 100644 --- a/drivers/staging/android/sw_sync.h +++ b/drivers/staging/android/sw_sync.h @@ -21,6 +21,7 @@ #ifdef __KERNEL__ +#include #include "sync.h" struct sw_sync_timeline { From 4f2bff4dca593c879b83065acd240f0215a56532 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Wed, 22 May 2013 14:23:10 -0700 Subject: [PATCH 278/358] video: add atomic display framework Change-Id: I693257e269a99012cd0dbb57576ac222869cf4c7 Signed-off-by: Greg Hackmann --- drivers/video/Kconfig | 1 + drivers/video/Makefile | 1 + drivers/video/adf/Kconfig | 4 + drivers/video/adf/Makefile | 11 + drivers/video/adf/adf.c | 1066 ++++++++++++++++++++++++++++++++ drivers/video/adf/adf.h | 74 +++ drivers/video/adf/adf_client.c | 764 +++++++++++++++++++++++ drivers/video/adf/adf_fops.c | 843 +++++++++++++++++++++++++ drivers/video/adf/adf_fops.h | 37 ++ drivers/video/adf/adf_fops32.c | 208 +++++++ drivers/video/adf/adf_fops32.h | 74 +++ drivers/video/adf/adf_format.c | 280 +++++++++ drivers/video/adf/adf_sysfs.c | 284 +++++++++ drivers/video/adf/adf_sysfs.h | 33 + drivers/video/adf/adf_trace.h | 93 +++ include/uapi/video/adf.h | 254 ++++++++ include/video/adf.h | 439 +++++++++++++ include/video/adf_client.h | 57 ++ include/video/adf_format.h | 26 + 19 files changed, 4549 insertions(+) create mode 100644 drivers/video/adf/Kconfig create mode 100644 drivers/video/adf/Makefile create mode 100644 drivers/video/adf/adf.c create mode 100644 drivers/video/adf/adf.h create mode 100644 drivers/video/adf/adf_client.c create mode 100644 drivers/video/adf/adf_fops.c create mode 100644 drivers/video/adf/adf_fops.h create mode 100644 drivers/video/adf/adf_fops32.c create mode 100644 drivers/video/adf/adf_fops32.h create mode 100644 drivers/video/adf/adf_format.c create mode 100644 drivers/video/adf/adf_sysfs.c create mode 100644 drivers/video/adf/adf_sysfs.h create mode 100644 drivers/video/adf/adf_trace.h create mode 100644 include/uapi/video/adf.h create mode 100644 include/video/adf.h create mode 100644 include/video/adf_client.h create mode 100644 include/video/adf_format.h diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index dade5b7699bc..e90061f56128 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2490,6 +2490,7 @@ source "drivers/video/omap2/Kconfig" source "drivers/video/exynos/Kconfig" source "drivers/video/mmp/Kconfig" source "drivers/video/backlight/Kconfig" +source "drivers/video/adf/Kconfig" if VT source "drivers/video/console/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ae17ddf49a00..accf67ae97b8 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -12,6 +12,7 @@ fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ modedb.o fbcvt.o fb-objs := $(fb-y) +obj-$(CONFIG_ADF) += adf/ obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ diff --git a/drivers/video/adf/Kconfig b/drivers/video/adf/Kconfig new file mode 100644 index 000000000000..0131dfb940d5 --- /dev/null +++ b/drivers/video/adf/Kconfig @@ -0,0 +1,4 @@ +menuconfig ADF + depends on SYNC + depends on DMA_SHARED_BUFFER + tristate "Atomic Display Framework" diff --git a/drivers/video/adf/Makefile b/drivers/video/adf/Makefile new file mode 100644 index 000000000000..62679a0da604 --- /dev/null +++ b/drivers/video/adf/Makefile @@ -0,0 +1,11 @@ +ccflags-y := -Idrivers/staging/android + +CFLAGS_adf.o := -I$(src) + +obj-$(CONFIG_ADF) += adf.o \ + adf_client.o \ + adf_fops.o \ + adf_format.o \ + adf_sysfs.o + +obj-$(CONFIG_COMPAT) += adf_fops32.o diff --git a/drivers/video/adf/adf.c b/drivers/video/adf/adf.c new file mode 100644 index 000000000000..4e359602748f --- /dev/null +++ b/drivers/video/adf/adf.c @@ -0,0 +1,1066 @@ +/* + * Copyright (C) 2013 Google, Inc. + * adf_modeinfo_{set_name,set_vrefresh} modified from + * drivers/gpu/drm/drm_modes.c + * + * 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 +#include +#include +#include +#include +#include +#include + +#include