From d61bb917f7e937b79b75fdff97e595999d62e9f1 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Thu, 22 Jan 2009 12:23:32 -0800 Subject: [PATCH 001/475] 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 a3bee5f1455e5249f3e28313e8b25524637a55c5 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 002/475] 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 | 4 +++ 2 files changed, 71 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 02e8dfaa1ce2..deb2df6a7129 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -28,6 +28,10 @@ config SUSPEND_SKIP_SYNC of suspend, or they are content with invoking sync() from user-space before invoking suspend. Say Y if that's your case. +config WAKELOCK + bool + default y + config HIBERNATE_CALLBACKS bool From ae3d36fac1b1827cfaf3d9ebd874e0dd1c0e48aa Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 15 Dec 2011 18:51:04 -0800 Subject: [PATCH 003/475] 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 3f2a3d611e4b..ae0166ff667f 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 00bad7793788..e976e180e873 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1059,6 +1059,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 *); #ifdef CONFIG_SHMEM bool shmem_mapping(struct address_space *mapping); diff --git a/mm/shmem.c b/mm/shmem.c index 2afcdbbdb685..40d1d3b0ea2f 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3404,6 +3404,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 @@ -3423,10 +3431,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 c99ba6d0dcc1bc69b41bd3529931f7c955e7d026 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Fri, 24 Jan 2014 15:19:49 -0800 Subject: [PATCH 004/475] staging: android: ashmem: Avoid deadlock with mmap/shrink Both ashmem_mmap and ashmem_shrink take the ashmem_lock. It may be possible for ashmem_mmap to invoke ashmem_shrink: -000|mutex_lock(lock = 0x0) -001|ashmem_shrink(?, sc = 0x0) <--- try to take ashmem_mutex again -002|shrink_slab(shrink = 0xDA5F1CC0, nr_pages_scanned = 0, lru_pages -002|= -002|124) -003|try_to_free_pages(zonelist = 0x0, ?, ?, ?) -004|__alloc_pages_nodemask(gfp_mask = 21200, order = 1, zonelist = -004|0xC11D0940, -005|new_slab(s = 0xE4841E80, ?, node = -1) -006|__slab_alloc.isra.43.constprop.50(s = 0xE4841E80, gfpflags = -006|2148925462, ad -007|kmem_cache_alloc(s = 0xE4841E80, gfpflags = 208) -008|shmem_alloc_inode(?) -009|alloc_inode(sb = 0xE480E800) -010|new_inode_pseudo(?) -011|new_inode(?) -012|shmem_get_inode(sb = 0xE480E800, dir = 0x0, ?, dev = 0, flags = -012|187) -013|shmem_file_setup(?, ?, flags = 187) -014|ashmem_mmap(?, vma = 0xC5D64210) <---- Acquire ashmem_mutex -015|mmap_region(file = 0xDF8E2C00, addr = 1772974080, len = 233472, -015|flags = 57, -016|sys_mmap_pgoff(addr = 0, len = 230400, prot = 3, flags = 1, fd = -016|157, pgoff -017|ret_fast_syscall(asm) -->|exception -018|NUR:0x40097508(asm) ---|end of frame Avoid this deadlock by using mutex_trylock in ashmem_shrink; if the mutex is already held, do not attempt to shrink. Change-Id: I222bbf55856d5849da813b730de0636c80966c8e Reported-by: Matt Wagantall Reported-by: Syed Rameez Mustafa Reported-by: Osvaldo Banuelos Reported-by: Subbaraman Narayanamurthy Signed-off-by: Laura Abbott --- drivers/staging/android/ashmem.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index ae0166ff667f..1830548e29a2 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -433,7 +433,9 @@ ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) if (!(sc->gfp_mask & __GFP_FS)) return SHRINK_STOP; - mutex_lock(&ashmem_mutex); + if (!mutex_trylock(&ashmem_mutex)) + return -1; + list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) { loff_t start = range->pgstart * PAGE_SIZE; loff_t end = (range->pgend + 1) * PAGE_SIZE; From bedf47303d316c12865d44714bfdf74cbcd10515 Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Sat, 24 Jan 2015 15:08:42 -0800 Subject: [PATCH 005/475] staging: android: ashmem: add missing include Include into ashmem.h to ensure referenced types are defined Signed-off-by: Rom Lemarchand Change-Id: Iac18ed86dd47296d02a269607b1514724aaa9958 (cherry picked from commit 0d7c7ba9f6428e694130a40e270516618f27ce14) --- drivers/staging/android/uapi/ashmem.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/android/uapi/ashmem.h b/drivers/staging/android/uapi/ashmem.h index ba4743c71d6b..13df42d200b7 100644 --- a/drivers/staging/android/uapi/ashmem.h +++ b/drivers/staging/android/uapi/ashmem.h @@ -13,6 +13,7 @@ #define _UAPI_LINUX_ASHMEM_H #include +#include #define ASHMEM_NAME_LEN 256 From 9cba14efb869b58f55e248012d4f4ea73f02dd8d Mon Sep 17 00:00:00 2001 From: Tobias Lindskog Date: Mon, 9 Feb 2015 08:10:39 +0100 Subject: [PATCH 006/475] Shrink ashmem directly through shmem_fallocate When ashmem_shrink is called from direct reclaim on a user thread, a call to do_fallocate will check for permissions against the security policy of that user thread. It can thus fail by chance if called on a thread that isn't permitted to modify the relevant ashmem areas. Because we know that we have a shmem file underneath, call the shmem implementation of fallocate directly instead of going through the user-space interface for fallocate. FIX=DMS06243560 Area: Kernel/Linux Kernel Bug: 21951515 Change-Id: Ie98fff18a2bdeb535cd24d4fbdd13677e12681a7 Signed-off-by: Jeff Vander Stoep --- drivers/staging/android/ashmem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 1830548e29a2..3f1133230a1a 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -440,9 +440,9 @@ ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) loff_t start = range->pgstart * PAGE_SIZE; loff_t end = (range->pgend + 1) * PAGE_SIZE; - vfs_fallocate(range->asma->file, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - start, end - start); + range->asma->file->f_op->fallocate(range->asma->file, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + start, end - start); range->purged = ASHMEM_WAS_PURGED; lru_del(range); From 59719f7ffbb7f1663b0f5270c7711d2fe211d244 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 007/475] 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 42b15126aa06..6e8557fc2200 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -38,6 +38,15 @@ config ANDROID_LOW_MEMORY_KILLER scripts (/init.rc), and it defines priority values with minimum free memory size for each priority. +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 SYNC bool "Synchronization framework" default n diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index e679d8432810..9c33d5d95b2c 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -193,13 +193,98 @@ static int __init lowmem_init(void) } device_initcall(lowmem_init); +#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 + /* * not really modular, but the easiest way to keep compat with existing * bootargs behaviour is to continue using module_param here. */ 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 758a1cb1f644837d2099f3900172e8814dcef3ab Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 3 May 2013 14:57:29 -0700 Subject: [PATCH 008/475] 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 9c33d5d95b2c..8a23d37428bb 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -84,6 +84,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); @@ -97,8 +98,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; } @@ -153,8 +154,8 @@ 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) { task_lock(selected); @@ -167,9 +168,18 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (selected->mm) mark_oom_victim(selected); task_unlock(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; rem += selected_tasksize; } From 29d59f115249b8b73e115caef343ec22d711ca14 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Tue, 26 May 2015 11:28:47 +0200 Subject: [PATCH 009/475] lowmemorykiller: trace kill events. Allows for capturing lmk kill events and their rationale. Change-Id: Ibe215db5bb9806fc550c72c0b9832c85cbd56bf6 Signed-off-by: Martijn Coenen --- drivers/staging/android/lowmemorykiller.c | 12 ++++-- .../staging/android/trace/lowmemorykiller.h | 41 +++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 drivers/staging/android/trace/lowmemorykiller.h diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 8a23d37428bb..c62d951ada50 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -43,6 +43,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include "trace/lowmemorykiller.h" + static uint32_t lowmem_debug_level = 1; static short lowmem_adj[6] = { 0, @@ -168,6 +171,10 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) if (selected->mm) mark_oom_victim(selected); task_unlock(selected); + long cache_size = other_file * (long)(PAGE_SIZE / 1024); + long cache_limit = minfree * (long)(PAGE_SIZE / 1024); + long free = other_free * (long)(PAGE_SIZE / 1024); + trace_lowmemory_kill(selected, cache_size, cache_limit, free); 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" \ @@ -176,10 +183,9 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) 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), + cache_size, cache_limit, min_score_adj, - other_free * (long)(PAGE_SIZE / 1024)); + free); lowmem_deathpending_timeout = jiffies + HZ; rem += selected_tasksize; } diff --git a/drivers/staging/android/trace/lowmemorykiller.h b/drivers/staging/android/trace/lowmemorykiller.h new file mode 100644 index 000000000000..f43d3fae75ee --- /dev/null +++ b/drivers/staging/android/trace/lowmemorykiller.h @@ -0,0 +1,41 @@ +#undef TRACE_SYSTEM +#define TRACE_INCLUDE_PATH ../../drivers/staging/android/trace +#define TRACE_SYSTEM lowmemorykiller + +#if !defined(_TRACE_LOWMEMORYKILLER_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_LOWMEMORYKILLER_H + +#include + +TRACE_EVENT(lowmemory_kill, + TP_PROTO(struct task_struct *killed_task, long cache_size, \ + long cache_limit, long free), + + TP_ARGS(killed_task, cache_size, cache_limit, free), + + TP_STRUCT__entry( + __array(char, comm, TASK_COMM_LEN) + __field(pid_t, pid) + __field(long, pagecache_size) + __field(long, pagecache_limit) + __field(long, free) + ), + + TP_fast_assign( + memcpy(__entry->comm, killed_task->comm, TASK_COMM_LEN); + __entry->pid = killed_task->pid; + __entry->pagecache_size = cache_size; + __entry->pagecache_limit = cache_limit; + __entry->free = free; + ), + + TP_printk("%s (%d), page cache %ldkB (limit %ldkB), free %ldKb", + __entry->comm, __entry->pid, __entry->pagecache_size, + __entry->pagecache_limit, __entry->free) +); + + +#endif /* if !defined(_TRACE_LOWMEMORYKILLER_H) || defined(TRACE_HEADER_MULTI_READ) */ + +/* This part must be outside protection */ +#include From fdaa05e593dcf385b2fe2db0b7d0cf7785a63837 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 30 Oct 2015 01:16:29 +0530 Subject: [PATCH 010/475] lowmemorykiller: use module_param_cb instead of __module_param_call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use module_param_cb helper routine instead of __module_param_call otherwise we run into following build error: CC drivers/staging/android/lowmemorykiller.o drivers/staging/android/lowmemorykiller.c:293:28: error: macro "__module_param_call" requires 7 arguments, but only 6 given S_IRUGO | S_IWUSR, -1); ^ drivers/staging/android/lowmemorykiller.c:290:1: warning: data definition has no type or storage class [enabled by default] __module_param_call(MODULE_PARAM_PREFIX, adj, ^ drivers/staging/android/lowmemorykiller.c:290:1: error: type defaults to ‘int’ in declaration of ‘__module_param_call’ [-Werror=implicit-int] drivers/staging/android/lowmemorykiller.c:273:32: warning: ‘lowmem_adj_array_ops’ defined but not used [-Wunused-variable] static struct kernel_param_ops lowmem_adj_array_ops = { ^ cc1: some warnings being treated as errors make[3]: *** [drivers/staging/android/lowmemorykiller.o] Error 1 Signed-off-by: Amit Pundir --- drivers/staging/android/lowmemorykiller.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index c62d951ada50..4cc3d02eacfb 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -292,10 +292,9 @@ static const struct kparam_array __param_arr_adj = { */ 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_param_cb(adj, &lowmem_adj_array_ops, + .arr = &__param_arr_adj, + S_IRUGO | S_IWUSR); __MODULE_PARM_TYPE(adj, "array of short"); #else module_param_array_named(adj, lowmem_adj, short, &lowmem_adj_size, From e4b8e66e0ae2e78e913d7b86f2507fdb0aa731b4 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 14 Oct 2008 12:50:16 -0400 Subject: [PATCH 011/475] 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 d2ac339de85f..3c363e559864 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -104,6 +104,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 795d0ca714bf..8acfb530993d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -122,6 +122,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 73b87c5b9fb6266280496608c0fc5fc3826f82f7 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 012/475] 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 d6d16fa78281..0e6e43853c65 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -509,6 +509,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 0357a088c6a9..a5457f855243 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.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 7a23935f76e01156daf7c72a6caea513f7a8434d Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 1 Feb 2012 20:26:28 -0800 Subject: [PATCH 013/475] 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 823efe4bbd9b1c406ac1daa52cb08ad884c93e57 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 014/475] 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 a35532ec00e4..47a5143096e4 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -187,6 +187,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 0c9302ca9954..f3749d52a18d 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -26,3 +26,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 26a59a0fd85cb7af8c521579037e85b04e6dbbdb Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 15 Dec 2008 14:51:56 -0500 Subject: [PATCH 015/475] 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 0e6e43853c65..25ac47b9a180 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -341,6 +341,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 a5457f855243..66c3cc9f181c 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -38,6 +38,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 fda0dfcfde43a7b4e2e07cc9d4e41f4f2ac09b6b Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 5 Mar 2013 14:25:36 -0800 Subject: [PATCH 016/475] 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 a15b297cd1dec04d98320be646eb47b785ac0e4f Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 7 Nov 2013 12:46:33 -0800 Subject: [PATCH 017/475] input: misc: keychord: move header to uapi Move the entire contents of linux/keychord.h header to uapi, it only contains a userspace interface. Change-Id: If94f83328b19efb58c66391dce3bd8e927788d8d Signed-off-by: Colin Cross --- include/linux/keychord.h | 31 +-------------------- include/uapi/linux/keychord.h | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 30 deletions(-) create mode 100644 include/uapi/linux/keychord.h diff --git a/include/linux/keychord.h b/include/linux/keychord.h index 856a5850217b..08cf5402102c 100644 --- a/include/linux/keychord.h +++ b/include/linux/keychord.h @@ -18,35 +18,6 @@ #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[]; -}; +#include #endif /* __LINUX_KEYCHORD_H_ */ diff --git a/include/uapi/linux/keychord.h b/include/uapi/linux/keychord.h new file mode 100644 index 000000000000..ea7cf4d27bbd --- /dev/null +++ b/include/uapi/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 _UAPI_LINUX_KEYCHORD_H_ +#define _UAPI_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 /* _UAPI_LINUX_KEYCHORD_H_ */ From f0a8adb404f8be85a4958f0fad8e22fbcd49dabb Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 7 May 2014 16:52:10 -0700 Subject: [PATCH 018/475] input: add keycombo, a general key combo driver. Keycombo lets you provide a key up and key down function, and an optional time delay for key down. The driver will call the key down function after the specified key combo has been held for the speicified time delay. After you release the combo, if the key down has happened, it calls key up. Change-Id: I6a9a94e96a8f58fadd908fd1dc7944b9102a089f Signed-off-by: Daniel Rosenberg --- drivers/input/Kconfig | 9 ++ drivers/input/Makefile | 2 + drivers/input/keycombo.c | 261 +++++++++++++++++++++++++++++++++++++++ include/linux/keycombo.h | 36 ++++++ 4 files changed, 308 insertions(+) create mode 100644 drivers/input/keycombo.c create mode 100644 include/linux/keycombo.h diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 47a5143096e4..8bd1b2802a99 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -196,6 +196,15 @@ config INPUT_KEYRESET To compile this driver as a module, choose M here: the module will be called keyreset. +config INPUT_KEYCOMBO + tristate "Key combo" + depends on INPUT + ---help--- + Say Y here if you want to take action when some keys are pressed; + + To compile this driver as a module, choose M here: the + module will be called keycombo. + comment "Input Device Drivers" source "drivers/input/keyboard/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index f3749d52a18d..2a6d05ab9170 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -27,3 +27,5 @@ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o +obj-$(CONFIG_INPUT_KEYCOMBO) += keycombo.o + diff --git a/drivers/input/keycombo.c b/drivers/input/keycombo.c new file mode 100644 index 000000000000..2fba451b91d5 --- /dev/null +++ b/drivers/input/keycombo.c @@ -0,0 +1,261 @@ +/* drivers/input/keycombo.c + * + * Copyright (C) 2014 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct keycombo_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; + struct workqueue_struct *wq; + int key_down_target; + int key_down; + int key_up; + struct delayed_work key_down_work; + int delay; + struct work_struct key_up_work; + void (*key_up_fn)(void *); + void (*key_down_fn)(void *); + void *priv; + int key_is_down; + struct wakeup_source combo_held_wake_source; + struct wakeup_source combo_up_wake_source; +}; + +static void do_key_down(struct work_struct *work) +{ + struct delayed_work *dwork = container_of(work, struct delayed_work, + work); + struct keycombo_state *state = container_of(dwork, + struct keycombo_state, key_down_work); + if (state->key_down_fn) + state->key_down_fn(state->priv); +} + +static void do_key_up(struct work_struct *work) +{ + struct keycombo_state *state = container_of(work, struct keycombo_state, + key_up_work); + if (state->key_up_fn) + state->key_up_fn(state->priv); + __pm_relax(&state->combo_up_wake_source); +} + +static void keycombo_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + unsigned long flags; + struct keycombo_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->key_up++; + else + state->key_up--; + } else { + if (value) + state->key_down++; + else + state->key_down--; + } + if (state->key_down == state->key_down_target && state->key_up == 0) { + __pm_stay_awake(&state->combo_held_wake_source); + state->key_is_down = 1; + if (queue_delayed_work(state->wq, &state->key_down_work, + state->delay)) + pr_debug("Key down work already queued!"); + } else if (state->key_is_down) { + if (!cancel_delayed_work(&state->key_down_work)) { + __pm_stay_awake(&state->combo_up_wake_source); + queue_work(state->wq, &state->key_up_work); + } + __pm_relax(&state->combo_held_wake_source); + state->key_is_down = 0; + } +done: + spin_unlock_irqrestore(&state->lock, flags); +} + +static int keycombo_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i; + int ret; + struct input_handle *handle; + struct keycombo_state *state = + container_of(handler, struct keycombo_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 = KEYCOMBO_NAME; + 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; + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keycombo_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id keycombo_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, keycombo_ids); + +static int keycombo_probe(struct platform_device *pdev) +{ + int ret; + int key, *keyp; + struct keycombo_state *state; + struct keycombo_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); + } + } + + state->wq = alloc_ordered_workqueue("keycombo", 0); + if (!state->wq) + return -ENOMEM; + + state->priv = pdata->priv; + + if (pdata->key_down_fn) + state->key_down_fn = pdata->key_down_fn; + INIT_DELAYED_WORK(&state->key_down_work, do_key_down); + + if (pdata->key_up_fn) + state->key_up_fn = pdata->key_up_fn; + INIT_WORK(&state->key_up_work, do_key_up); + + wakeup_source_init(&state->combo_held_wake_source, "key combo"); + wakeup_source_init(&state->combo_up_wake_source, "key combo up"); + state->delay = msecs_to_jiffies(pdata->key_down_delay); + + state->input_handler.event = keycombo_event; + state->input_handler.connect = keycombo_connect; + state->input_handler.disconnect = keycombo_disconnect; + state->input_handler.name = KEYCOMBO_NAME; + state->input_handler.id_table = keycombo_ids; + ret = input_register_handler(&state->input_handler); + if (ret) { + kfree(state); + return ret; + } + platform_set_drvdata(pdev, state); + return 0; +} + +int keycombo_remove(struct platform_device *pdev) +{ + struct keycombo_state *state = platform_get_drvdata(pdev); + input_unregister_handler(&state->input_handler); + destroy_workqueue(state->wq); + kfree(state); + return 0; +} + + +struct platform_driver keycombo_driver = { + .driver.name = KEYCOMBO_NAME, + .probe = keycombo_probe, + .remove = keycombo_remove, +}; + +static int __init keycombo_init(void) +{ + return platform_driver_register(&keycombo_driver); +} + +static void __exit keycombo_exit(void) +{ + return platform_driver_unregister(&keycombo_driver); +} + +module_init(keycombo_init); +module_exit(keycombo_exit); diff --git a/include/linux/keycombo.h b/include/linux/keycombo.h new file mode 100644 index 000000000000..c6db2626b0d3 --- /dev/null +++ b/include/linux/keycombo.h @@ -0,0 +1,36 @@ +/* + * include/linux/keycombo.h - platform data structure for keycombo driver + * + * Copyright (C) 2014 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_KEYCOMBO_H +#define _LINUX_KEYCOMBO_H + +#define KEYCOMBO_NAME "keycombo" + +/* + * if key_down_fn and key_up_fn are both present, you are guaranteed that + * key_down_fn will return before key_up_fn is called, and that key_up_fn + * is called iff key_down_fn is called. + */ +struct keycombo_platform_data { + void (*key_down_fn)(void *); + void (*key_up_fn)(void *); + void *priv; + int key_down_delay; /* Time in ms */ + int *keys_up; + int keys_down[]; /* 0 terminated */ +}; + +#endif /* _LINUX_KEYCOMBO_H */ From 3b79b73e8ef5bcf0af7259747e9158001f030e7f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 7 May 2014 14:17:47 -0700 Subject: [PATCH 019/475] input: Changed keyreset to act as a wrapper for keycombo. keyreset now registers a keycombo driver that acts as the old keyreset driver acted. Change-Id: I08f5279e3a33b267571b699697f9f54508868983 Signed-off-by: Daniel Rosenberg --- drivers/input/Kconfig | 1 + drivers/input/keyreset.c | 206 +++++++++++---------------------------- include/linux/keyreset.h | 3 +- 3 files changed, 58 insertions(+), 152 deletions(-) diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 8bd1b2802a99..4c1e2a74276c 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -190,6 +190,7 @@ config INPUT_APMPOWER config INPUT_KEYRESET tristate "Reset key" depends on INPUT + select INPUT_KEYCOMBO ---help--- Say Y here if you want to reboot when some keys are pressed; diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c index 36208fe0baae..eaaccde82210 100644 --- a/drivers/input/keyreset.c +++ b/drivers/input/keyreset.c @@ -1,6 +1,6 @@ /* drivers/input/keyreset.c * - * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2014 Google, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -21,200 +21,104 @@ #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 restart_requested; int (*reset_fn)(void); + struct platform_device *pdev_child; }; -int restart_requested; -static void deferred_restart(struct work_struct *dummy) +static void do_restart(void) { - 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) +static void do_reset_fn(void *priv) { - 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--; + struct keyreset_state *state = priv; + if (state->restart_requested) + panic("keyboard reset failed, %d", state->restart_requested); + if (state->reset_fn) { + state->restart_requested = state->reset_fn(); } else { - if (value) - state->key_down++; - else - state->key_down--; + pr_info("keyboard reset\n"); + do_restart(); + state->restart_requested = 1; } - 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 ret = -ENOMEM; + struct keycombo_platform_data *pdata_child; + struct keyreset_platform_data *pdata = pdev->dev.platform_data; + int up_size = 0, down_size = 0, size; 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); + state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; - spin_lock_init(&state->lock); + state->pdev_child = platform_device_alloc(KEYCOMBO_NAME, + PLATFORM_DEVID_AUTO); + if (!state->pdev_child) + return -ENOMEM; + state->pdev_child->dev.parent = &pdev->dev; + keyp = pdata->keys_down; while ((key = *keyp++)) { if (key >= KEY_MAX) continue; - state->key_down_target++; - __set_bit(key, state->keybit); + down_size++; } 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); + up_size++; } } - - 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; + size = sizeof(struct keycombo_platform_data) + + sizeof(int) * (down_size + 1); + pdata_child = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!pdata_child) + goto error; + memcpy(pdata_child->keys_down, pdata->keys_down, + sizeof(int) * down_size); + if (up_size > 0) { + pdata_child->keys_up = devm_kzalloc(&pdev->dev, up_size + 1, + GFP_KERNEL); + if (!pdata_child->keys_up) + goto error; + memcpy(pdata_child->keys_up, pdata->keys_up, + sizeof(int) * up_size); + if (!pdata_child->keys_up) + goto error; } + state->reset_fn = pdata->reset_fn; + pdata_child->key_down_fn = do_reset_fn; + pdata_child->priv = state; + pdata_child->key_down_delay = pdata->key_down_delay; + ret = platform_device_add_data(state->pdev_child, pdata_child, size); + if (ret) + goto error; platform_set_drvdata(pdev, state); - return 0; + return platform_device_add(state->pdev_child); +error: + platform_device_put(state->pdev_child); + return ret; } int keyreset_remove(struct platform_device *pdev) { struct keyreset_state *state = platform_get_drvdata(pdev); - input_unregister_handler(&state->input_handler); - kfree(state); + platform_device_put(state->pdev_child); return 0; } diff --git a/include/linux/keyreset.h b/include/linux/keyreset.h index a2ac49e5b684..2e34afab65e4 100644 --- a/include/linux/keyreset.h +++ b/include/linux/keyreset.h @@ -1,7 +1,7 @@ /* * include/linux/keyreset.h - platform data structure for resetkeys driver * - * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2014 Google, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -21,6 +21,7 @@ struct keyreset_platform_data { int (*reset_fn)(void); + int key_down_delay; int *keys_up; int keys_down[]; /* 0 terminated */ }; From 068da28761a7d684d9bd68b6b06f2a69266526bf Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 27 Jun 2014 16:39:35 -0700 Subject: [PATCH 020/475] input: Made keyreset more robust Switched do_restart to run in a seperate workqueue to handle cases where kernel_restart hangs. Change-Id: I1ecd61f8d0859f1a86d37c692351d644b5db9c69 Signed-off-by: Daniel Rosenberg --- drivers/input/keyreset.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c index eaaccde82210..7fbf7247e65f 100644 --- a/drivers/input/keyreset.c +++ b/drivers/input/keyreset.c @@ -27,9 +27,10 @@ struct keyreset_state { int restart_requested; int (*reset_fn)(void); struct platform_device *pdev_child; + struct work_struct restart_work; }; -static void do_restart(void) +static void do_restart(struct work_struct *unused) { sys_sync(); kernel_restart(NULL); @@ -44,7 +45,7 @@ static void do_reset_fn(void *priv) state->restart_requested = state->reset_fn(); } else { pr_info("keyboard reset\n"); - do_restart(); + schedule_work(&state->restart_work); state->restart_requested = 1; } } @@ -69,6 +70,7 @@ static int keyreset_probe(struct platform_device *pdev) if (!state->pdev_child) return -ENOMEM; state->pdev_child->dev.parent = &pdev->dev; + INIT_WORK(&state->restart_work, do_restart); keyp = pdata->keys_down; while ((key = *keyp++)) { From 90037b2720acffa6da2269a10ecf24ec2dace89b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 11 Sep 2013 21:56:53 +0200 Subject: [PATCH 021/475] HID: steelseries: validate output report details A HID device could send a malicious output report that would cause the steelseries HID driver to write beyond the output report allocation during initialization, causing a heap overflow: [ 167.981534] usb 1-1: New USB device found, idVendor=1038, idProduct=1410 ... [ 182.050547] BUG kmalloc-256 (Tainted: G W ): Redzone overwritten CVE-2013-2891 Signed-off-by: Kees Cook Cc: stable@vger.kernel.org Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-steelseries.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 3edd4ac36494..93ddc1c65b4c 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -253,6 +253,11 @@ static int steelseries_srws1_probe(struct hid_device *hdev, goto err_free; } + if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) { + ret = -ENODEV; + goto err_free; + } + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); From 0840b80cb9626906b57df54e7229db60f9aea4f2 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Sun, 24 Jul 2011 14:31:14 -0700 Subject: [PATCH 022/475] 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 3d664d01305e..96759b270360 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -434,6 +434,16 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) td->buttons_count++; + /* 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 8e180508c265026cc4653b68ca216db5e9b709fc Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 19 Jun 2012 21:06:47 -0700 Subject: [PATCH 023/475] 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 dc66dee02dcd6ea774e3ed4ae32e88b0f3b4bee7 Mon Sep 17 00:00:00 2001 From: Qiao Zhou Date: Thu, 31 Oct 2013 09:36:50 +0800 Subject: [PATCH 024/475] drivers: switch: remove S_IWUSR from dev_attr it doesn't need S_IWUSR attribute since xxx_store API is un-needed. otherwise the WARN check in device_create_file will alert. Change-Id: I6360bf022dcd659bfb3f41c84624f954d5d15ea5 Signed-off-by: Qiao Zhou --- drivers/switch/switch_class.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c index e05fc2591147..e373b625806e 100644 --- a/drivers/switch/switch_class.c +++ b/drivers/switch/switch_class.c @@ -54,8 +54,8 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, 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); +static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); +static DEVICE_ATTR(name, S_IRUGO, name_show, NULL); void switch_set_state(struct switch_dev *sdev, int state) { From 3ad3ce2c5a30cb57f5a1356d5dc802ef822142a7 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 4 Dec 2008 12:19:57 -0800 Subject: [PATCH 025/475] 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 289664089cf3..093fd917274f 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 @@ -108,9 +115,6 @@ config MTD_NAND_OMAP_BCH config MTD_NAND_OMAP_BCH_BUILD def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH -config MTD_NAND_IDS - tristate - config MTD_NAND_RICOH tristate "Ricoh xD card reader" default n From 2643e5366d9d2073c8a2d5af52997cc27b7c4ff0 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 3 Sep 2010 12:41:21 -0700 Subject: [PATCH 026/475] 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 d8486168415a..43d0044ac8fe 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -171,11 +171,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; } @@ -2244,6 +2240,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 | MMC_BLK_DATA_AREA_BOOT)) md->disk->flags |= GENHD_FL_NO_PART_SCAN; From 91fa97e1e5c001d52f6c993d37be08d1e84f47b7 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 10 Nov 2008 16:29:50 -0800 Subject: [PATCH 027/475] mmc: Add status IRQ and status callback function to mmc platform data Signed-off-by: San Mehat --- include/linux/amba/mmci.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 8c98113069ce..0fbd47eddf20 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -31,6 +31,9 @@ struct mmci_platform_data { int gpio_wp; int gpio_cd; bool cd_invert; + unsigned int status_irq; + int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); + }; #endif From 94d5306d03e52653ebc18136166ad142845f99de Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 11 Nov 2008 09:35:36 -0800 Subject: [PATCH 028/475] 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 4c33d7690f2f..319c68d7b0f4 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -1,3 +1,11 @@ # # MMC core configuration # + +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 141eaa923e18..db828959d6d1 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -809,6 +809,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) { /* @@ -835,7 +838,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 f1da17ac8a32e862a25de3313b6d07c6ca14be68 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 1 Dec 2008 08:52:34 -0800 Subject: [PATCH 029/475] 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 db828959d6d1..e41400bbe42f 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1127,6 +1127,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); @@ -1137,7 +1140,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 2f5481a462b73d00b9bd795046236812b6bbb929 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 4 Dec 2008 11:18:00 -0800 Subject: [PATCH 030/475] 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 e41400bbe42f..4ac721fea4ef 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1055,7 +1055,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); @@ -1065,7 +1068,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); @@ -1242,6 +1261,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); @@ -1278,9 +1300,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 707392fd2c805ec098847825eeaf49add924a17d Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 14 Apr 2008 15:22:49 -0700 Subject: [PATCH 031/475] 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 Change-Id: Ibff2e3e991e5522f55ec8c6edc25ed09f2553736 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 319c68d7b0f4..a10e583950ee 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -2,8 +2,17 @@ # MMC core configuration # +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 5ae89e48fd85..1e2e1a8f8872 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2832,6 +2832,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 16d838e6d623..444a6cbb55b9 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; @@ -699,19 +703,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 && @@ -1120,14 +1140,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 7e327a6dd53d..e32ed3d28b06 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -28,6 +28,10 @@ #include "sdio_cis.h" #include "sdio_bus.h" +#ifdef CONFIG_MMC_EMBEDDED_SDIO +#include +#endif + #define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) /* show configuration fields */ @@ -263,7 +267,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 0fbd47eddf20..8bfd21c13d59 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; +}; /** * struct mmci_platform_data - platform configuration for the MMCI @@ -32,6 +41,7 @@ struct mmci_platform_data { int gpio_cd; bool cd_invert; 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 8673ffe3d86e..40025b28c1fb 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -370,6 +370,15 @@ struct mmc_host { int dsr_req; /* DSR value is valid */ u32 dsr; /* optional driver stage (DSR) value */ +#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; }; @@ -379,6 +388,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 aab032a6ae61..2e5e4baaf5ac 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 bec7bcbb707d10b80d450f6f02384efeff294799 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 23 Mar 2009 12:20:37 -0700 Subject: [PATCH 032/475] 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 1e2e1a8f8872..1b34aeb81fec 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,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 }; /* @@ -72,6 +74,7 @@ module_param(use_spi_crc, bool, 0); 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); } @@ -2566,6 +2569,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->trigger_card_event && host->ops->card_event) { host->ops->card_event(host); @@ -2621,14 +2625,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); } @@ -2856,6 +2866,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; @@ -2876,6 +2889,7 @@ unregister_bus: mmc_unregister_bus(); destroy_workqueue: destroy_workqueue(workqueue); + wake_lock_destroy(&mmc_delayed_work_wake_lock); return ret; } @@ -2886,6 +2900,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 c38a74bdb5be68b268ac06dd793bc7259f6aecbd Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 4 Dec 2015 12:13:13 -0800 Subject: [PATCH 033/475] mmc: Extend wakelock if bus is dead This patch sets extend_wakelock if the host's bus is marked dead due to card removal. This change was originally made in bc4dc52f178828 (mmc: mmcblk: Add support for deferred SD bus resume), which was reverted due to the majority of the patch no longer building or making sense. However one small part of that patch ought to be saved. Signed-off-by: John Stultz --- drivers/mmc/core/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 1b34aeb81fec..9c9bd3c90637 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2596,6 +2596,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 c984934ee3fe4388e60fccd16c0b859e6d4770ce Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 11 Nov 2008 11:22:38 -0800 Subject: [PATCH 034/475] mmc: Add sdio_readb_ext() function Change-Id: I9b410c8a13724795b23764012fd3be8f53747299 --- drivers/mmc/core/sdio_io.c | 33 +++++++++++++++++++++++++++++++++ include/linux/mmc/sdio_func.h | 2 ++ 2 files changed, 35 insertions(+) diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index 78cb4d5d9d58..8fdeb07723a6 100644 --- 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 index 2e5e4baaf5ac..d0a69e71b8ab 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -136,6 +136,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 b9d57175c19be8f0d9fe48cd544152cd6c574dd3 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 15 May 2008 09:15:37 -0700 Subject: [PATCH 035/475] 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 444a6cbb55b9..b90dd9e62999 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1217,3 +1217,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 3e5b5b47e1b93761146d0145766b1bdc3bcd2967 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 29 Jul 2009 10:22:03 -0700 Subject: [PATCH 036/475] 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 b90dd9e62999..a4e3733f61e0 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -1224,6 +1225,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); @@ -1264,13 +1267,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 8d2effe1cf5f782ded297d1a32a96423fe069748 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Wed, 9 Dec 2009 09:45:36 -0800 Subject: [PATCH 037/475] 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 a4e3733f61e0..dbee1606b2fb 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1263,7 +1263,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 7923f784f327c8ef9c222e090de81fc6034c069d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 6 Oct 2010 17:25:02 -0700 Subject: [PATCH 038/475] 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 dbee1606b2fb..915ff424669a 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1248,7 +1248,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)) { @@ -1267,27 +1267,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 c284cce930c8173e1856f4e546dfcf1001dbfd55 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 19 Mar 2014 12:46:49 -0700 Subject: [PATCH 039/475] mmc: sdio: fix sdio_reset_comm() voltage selection Change-Id: I2fa35ee9291c4c60e55fc11d923ae686a8f81920 Signed-off-by: Dmitry Shmidt --- drivers/mmc/core/sdio.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 915ff424669a..231c97248add 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1222,6 +1222,7 @@ int sdio_reset_comm(struct mmc_card *card) { struct mmc_host *host = card->host; u32 ocr; + u32 rocr; int err; printk("%s():\n", __func__); @@ -1235,13 +1236,13 @@ int sdio_reset_comm(struct mmc_card *card) if (err) goto err; - host->ocr = mmc_select_voltage(host, ocr); - if (!host->ocr) { + rocr = mmc_select_voltage(host, ocr); + if (!rocr) { err = -EINVAL; goto err; } - err = mmc_send_io_op_cond(host, host->ocr, &ocr); + err = mmc_sdio_init_card(host, rocr, card, 0); if (err) goto err; From ff4a11fdd0f8862f98d2cbc72fdb4fa30172a4d0 Mon Sep 17 00:00:00 2001 From: Hosung Kim Date: Mon, 23 Jul 2012 17:33:17 +0900 Subject: [PATCH 040/475] 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 | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 231c97248add..cc93152f82c6 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1246,44 +1246,6 @@ int sdio_reset_comm(struct mmc_card *card) 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 e910f2063e032916cd13cc360e8b509acfd4679d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 7 Oct 2010 14:39:16 -0700 Subject: [PATCH 041/475] 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 da950c44204d..fcf7829c759e 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -395,7 +395,8 @@ int mmc_add_host(struct mmc_host *host) #endif 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; } @@ -412,7 +413,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 5b42ae3edab6c39c1337d36881d29350bb36dcff Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 8 Apr 2011 22:20:53 -0700 Subject: [PATCH 042/475] 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 541632275e983573b8250fcd4402f772d7bd1e6f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 3 May 2011 11:05:04 -0700 Subject: [PATCH 043/475] 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 94f639e7e33e92ab4b1f389f5a507abe236a0f66 Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Tue, 25 Oct 2011 18:16:58 -0700 Subject: [PATCH 044/475] 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 43d0044ac8fe..e5b749822acc 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -940,18 +940,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 321e98d9e0baf7554f93adb70bbf6a0634f0009b Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 18 Mar 2013 11:57:28 -0700 Subject: [PATCH 045/475] 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 a10e583950ee..87cc07dedd9f 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -4,7 +4,6 @@ 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 @@ -12,7 +11,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 bf9c9b48f1617f6653483483b231b27b0683177d Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 11 Feb 2016 16:54:11 +0100 Subject: [PATCH 046/475] Revert "mmc: block: don't use parameter prefix if built as module" This reverts commit 829b6962f7e3cfc06f7c5c26269fd47ad48cf503. Revert this change as it causes a sysfs path to change and therefore introduces and ABI regression. More precisely Android's vold is not being able to access /sys/module/mmcblk/parameters/perdev_minors any more, since the path becomes changed to: "/sys/module/mmc_block/..." Fixes: 829b6962f7e3 ("mmc: block: don't use parameter prefix if built as module") Reported-by: John Stultz Cc: Andy Shevchenko Signed-off-by: Ulf Hansson Signed-off-by: John Stultz --- drivers/mmc/card/block.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index e5b749822acc..daffad24e8e5 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -47,13 +47,10 @@ #include "queue.h" MODULE_ALIAS("mmc:block"); - -#ifdef KERNEL #ifdef MODULE_PARAM_PREFIX #undef MODULE_PARAM_PREFIX #endif #define MODULE_PARAM_PREFIX "mmcblk." -#endif #define INAND_CMD38_ARG_EXT_CSD 113 #define INAND_CMD38_ARG_ERASE 0x00 From b7b1108e17e89db71936fafb42e2866653af72a5 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 21 Mar 2009 18:48:54 -0700 Subject: [PATCH 047/475] 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 023f6a1f23cd..28cd6508f4aa 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -2015,7 +2015,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) (dirtytime && (inode->i_state & I_DIRTY_INODE))) return; - if (unlikely(block_dump)) + if (unlikely(block_dump > 1)) block_dump___mark_inode_dirty(inode); spin_lock(&inode->i_lock); From 00074131acec43c74d8e4bf59b9e801eb1c1d75a Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 10 Oct 2009 09:35:24 -0700 Subject: [PATCH 048/475] 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 e5cafa51567c..817c67c9e45e 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1117,6 +1117,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", }; @@ -1136,6 +1152,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 746935a5973c..ae95e963c5bb 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 76fb6835f4fda6775a5982dd9fd6c71f82fae2cf Mon Sep 17 00:00:00 2001 From: Robert Love Date: Wed, 15 Oct 2008 15:34:49 -0400 Subject: [PATCH 049/475] 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 4b96f2c640f2533a6b1d4080a959d33617844ae6 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Wed, 15 Oct 2008 15:35:44 -0400 Subject: [PATCH 050/475] 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 127da94ae25e..ce9585cf343a 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -86,6 +86,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 70306cc9d814..709ce9fb15f3 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -106,11 +106,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 5c5db6636704..ac822c7bd55d 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -121,6 +121,9 @@ #endif #include +#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. */ @@ -260,6 +286,9 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, if (protocol < 0 || protocol >= IPPROTO_MAX) return -EINVAL; + if (!current_has_network()) + return -EACCES; + sock->state = SS_UNCONNECTED; /* Look for the requested type/protocol pair. */ @@ -309,7 +338,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 9f5137cd604e..b6d7882fbb66 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) { @@ -112,6 +139,9 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, if (protocol < 0 || protocol >= IPPROTO_MAX) return -EINVAL; + if (!current_has_network()) + return -EACCES; + /* Look for the requested type/protocol pair. */ lookup_protocol: err = -ESOCKTNOSUPPORT; @@ -159,7 +189,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 9a5e2c00592e2dcb648a1ae2ad7906b56b93448d Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 19 Jun 2009 07:15:05 +0800 Subject: [PATCH 051/475] 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 1832cf701c3d..978bd9f852f7 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. @@ -73,6 +77,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 cc04396b8462a780754bce481236b81a21e3ac0a Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Tue, 30 Jun 2009 11:23:04 +0800 Subject: [PATCH 052/475] 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 ac822c7bd55d..eb12bd0ff9d3 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -123,6 +123,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. */ @@ -337,8 +324,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 b6d7882fbb66..9dbfacb6e0d9 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) { @@ -188,8 +175,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 6eca735acf6eea1371555b66acb953a82f234e73 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 15 Jul 2011 15:32:57 -0700 Subject: [PATCH 053/475] 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 f0db770e8b2f..2228a539184a 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1886,6 +1886,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, int le; 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 6d4380a19887aad1e9bf974310f2828d2c8e59df Mon Sep 17 00:00:00 2001 From: Robert Love Date: Thu, 31 Jul 2008 11:12:44 -0400 Subject: [PATCH 054/475] 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 c29809f765dc..854c4bfd6eea 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -16,6 +16,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 38f0ec724f5306c81130ca9343c856aa37a76d54 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Mon, 12 May 2008 17:08:29 -0400 Subject: [PATCH 055/475] 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 f80e74c5ad18..0020825157e2 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1678,6 +1678,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 eb12bd0ff9d3..671eb0092915 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -886,6 +886,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 cebd9d31e65a..3792624af316 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -59,6 +59,7 @@ #include #include +#include #include #include #include @@ -964,6 +965,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; @@ -1015,7 +1017,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) { @@ -1142,6 +1145,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 c82cca18c90f..f94bc2cf50d3 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -275,6 +275,9 @@ #include #include #include +#include +#include +#include #include #include @@ -3187,3 +3190,107 @@ void __init tcp_init(void) BUG_ON(tcp_register_congestion_control(&tcp_reno) != 0); 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 9dbfacb6e0d9..036221f89509 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -495,6 +495,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; @@ -518,6 +533,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 4747299b2c8e8778927b3df0501023d76fe4f2d5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Jul 2015 17:17:57 -0700 Subject: [PATCH 056/475] net: fix iterating over hashtable in tcp_nuke_addr() The actual size of the tcp hashinfo table is tcp_hashinfo.ehash_mask + 1 so we need to adjust the loop accordingly to get the sockets hashed into the last bucket. Change-Id: I796b3c7b4a1a7fa35fba9e5192a4a403eb6e17de Signed-off-by: Dmitry Torokhov --- net/ipv4/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index f94bc2cf50d3..c8cfe784c79a 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3231,7 +3231,7 @@ int tcp_nuke_addr(struct net *net, struct sockaddr *addr) return -EAFNOSUPPORT; } - for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) { + 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); From 08f7c4280cd5efe9e274240c42177f459431bac2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 3 Sep 2015 13:08:37 -0700 Subject: [PATCH 057/475] net: fix crash in tcp_nuke_addr() When iterating through sockets we need to skip sockets in TIME_WAIT state as they use lightweight structure inet_timewait_sock that does not have sk_lock member, and if we try to lock them we'll crash thusly: [ 89.376383] BUG: spinlock lockup suspected on CPU#0, netd/431 [ 89.382139] lock: 0xffffffc039d05070, .magic: 66d30606, .owner: /-1682098992, .owner_cpu: 0 [ 89.390598] CPU: 0 PID: 431 Comm: netd Tainted: G U W 3.18.0 #5 [ 89.397389] Hardware name: Google Tegra210 Smaug Rev 1+ (DT) [ 89.403049] Call trace: [ 89.405501] [] dump_backtrace+0x0/0x10c [ 89.410918] [] show_stack+0x10/0x1c [ 89.415971] [] dump_stack+0x74/0x94 [ 89.421018] [] spin_dump+0x78/0x88 [ 89.425984] [] do_raw_spin_lock+0xfc/0x158 [ 89.431666] [] _raw_spin_lock+0x34/0x44 [ 89.437059] [] tcp_nuke_addr+0x1fc/0x29c [ 89.442548] [] devinet_ioctl+0x288/0x680 [ 89.448053] [] inet_ioctl+0xc4/0xf4 [ 89.453103] [] sock_do_ioctl+0x2c/0x5c [ 89.458408] [] sock_ioctl+0x210/0x230 [ 89.463633] [] do_vfs_ioctl+0x4ac/0x590 [ 89.469049] [] SyS_ioctl+0x5c/0x88 (or with NULL pointer dereference if lockdep is still working). Change-Id: I07c70d9a60b125b1070ff05c4eec27daee1a3e90 Signed-off-by: Dmitry Torokhov --- net/ipv4/tcp.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c8cfe784c79a..47b147ca69d1 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3241,8 +3241,19 @@ restart: sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { struct inet_sock *inet = inet_sk(sk); + if (sk->sk_state == TCP_TIME_WAIT) { + /* + * Sockets that are in TIME_WAIT state are + * instances of lightweight inet_timewait_sock, + * we should simply skip them (or we'll try to + * access non-existing fields and crash). + */ + continue; + } + if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) continue; + if (sock_flag(sk, SOCK_DEAD)) continue; From 6b6d5fbf9ae567aefb58099a30bbb6d25fa8925b Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Wed, 7 Jan 2009 11:40:42 -0800 Subject: [PATCH 058/475] 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 22892c701c63..2e31b1d35890 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -412,6 +412,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 VMWARE_VMCI && X86 && HYPERVISOR_GUEST diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 537d7f3b78da..21522435a2b5 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 47b147ca69d1..cab28756a96b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -269,6 +269,7 @@ #include #include #include +#include #include #include @@ -1286,6 +1287,9 @@ out: tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); out_nopush: release_sock(sk); + + if (copied + copied_syn) + uid_stat_tcp_snd(current_uid(), copied + copied_syn); return copied + copied_syn; do_fault: @@ -1560,6 +1564,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; } @@ -1893,6 +1898,9 @@ skip_copy: tcp_cleanup_rbuf(sk, copied); release_sock(sk); + + if (copied > 0) + uid_stat_tcp_rcv(current_uid(), copied); return copied; out: @@ -1901,6 +1909,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 224e3d846f972c5d3a48afff2c593bd944405a7c Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 8 May 2009 04:02:40 +0800 Subject: [PATCH 059/475] 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 b49cf923becc..18205d2d8d44 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -43,6 +43,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 { @@ -53,6 +61,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 4dee9b0d4bfe228d87d6a5c96af771b81fe752a5 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 12 Jun 2009 01:09:30 +0800 Subject: [PATCH 060/475] 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 18205d2d8d44..bf0fd2a68c85 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -51,6 +51,12 @@ struct pppolac_opt { __u8 sequencing; }; +struct pppopns_opt { + __u16 local; + __u16 remote; + __u32 sequence; +}; + #include struct pppox_sock { @@ -62,6 +68,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 3014a5d3ebb9a2369405350d5ea611c755eab1e7 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Sat, 13 Jun 2009 02:29:04 +0800 Subject: [PATCH 061/475] 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 bf0fd2a68c85..1251169e21a2 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -49,12 +49,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 02649604a0ec84e1a9ead0f6742e0f458aa70a0c Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 15 Apr 2011 15:22:09 -0700 Subject: [PATCH 062/475] 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 1251169e21a2..a487f87c2f09 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -45,19 +45,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 681559fbade4510651ce6da45b2aa39c4bdd0f71 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 20 Sep 2012 16:34:10 -0700 Subject: [PATCH 063/475] 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 11a5343eebba995cb8cc73a965ef972512eabfcc Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Fri, 23 Mar 2012 21:48:02 +0400 Subject: [PATCH 064/475] 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 c91e8c503c720f120b91b1f7dab504b92c373800 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 7 Nov 2013 13:19:34 -0800 Subject: [PATCH 065/475] net: move PPPoLAC and PPPoPNS headers to uapi Move the entire contents of the linux/if_pppolac.h and linux/if_pppopns.h headers to uapi, they only contain userspace interfaces. Change-Id: I3cfed7f2ae400b53269a1f59144aa3dbc30ae0b5 Signed-off-by: Colin Cross --- include/linux/if_pppolac.h | 12 +----------- include/linux/if_pppopns.h | 11 +---------- include/uapi/linux/if_pppolac.h | 33 +++++++++++++++++++++++++++++++++ include/uapi/linux/if_pppopns.h | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 21 deletions(-) create mode 100644 include/uapi/linux/if_pppolac.h create mode 100644 include/uapi/linux/if_pppopns.h diff --git a/include/linux/if_pppolac.h b/include/linux/if_pppolac.h index c06bd6c8ba26..e40aa1075a30 100644 --- a/include/linux/if_pppolac.h +++ b/include/linux/if_pppolac.h @@ -18,16 +18,6 @@ #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)); +#include #endif /* __LINUX_IF_PPPOLAC_H */ diff --git a/include/linux/if_pppopns.h b/include/linux/if_pppopns.h index 0cf34b4d551f..4ac621a9ce7c 100644 --- a/include/linux/if_pppopns.h +++ b/include/linux/if_pppopns.h @@ -18,15 +18,6 @@ #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)); +#include #endif /* __LINUX_IF_PPPOPNS_H */ diff --git a/include/uapi/linux/if_pppolac.h b/include/uapi/linux/if_pppolac.h new file mode 100644 index 000000000000..b7eb8153ef66 --- /dev/null +++ b/include/uapi/linux/if_pppolac.h @@ -0,0 +1,33 @@ +/* include/uapi/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 _UAPI_LINUX_IF_PPPOLAC_H +#define _UAPI_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 /* _UAPI_LINUX_IF_PPPOLAC_H */ diff --git a/include/uapi/linux/if_pppopns.h b/include/uapi/linux/if_pppopns.h new file mode 100644 index 000000000000..a392b52ea6ec --- /dev/null +++ b/include/uapi/linux/if_pppopns.h @@ -0,0 +1,32 @@ +/* include/uapi/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 _UAPI_LINUX_IF_PPPOPNS_H +#define _UAPI_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 /* _UAPI_LINUX_IF_PPPOPNS_H */ From fde6abf2f024b761ee8bd1b7ba609fd414491f7a Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Wed, 19 Aug 2015 13:43:16 +0100 Subject: [PATCH 066/475] net: PPPoPNS: Remove length argument from data_ready The argument was removed by commit 676d23690fb6 ("net: Fix use after free by removing length arg from sk_data_ready callbacks") and it's presence causes warnings like: drivers/net/ppp/pppopns.c:296:27: warning: assignment from incompatible pointer type po->proto.pns.data_ready = sk_raw->sk_data_ready; Signed-off-by: Jon Medhurst Signed-off-by: Amit Pundir --- drivers/net/ppp/pppopns.c | 2 +- include/linux/if_pppox.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c index 6016d29c0660..dc15f978c922 100644 --- a/drivers/net/ppp/pppopns.c +++ b/drivers/net/ppp/pppopns.c @@ -169,7 +169,7 @@ drop: return NET_RX_DROP; } -static void pppopns_recv(struct sock *sk_raw, int length) +static void pppopns_recv(struct sock *sk_raw) { struct sk_buff *skb; while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) { diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index a487f87c2f09..63828a5870f1 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -58,7 +58,7 @@ struct pppopns_opt { __u16 remote; __u32 recv_sequence; __u32 xmit_sequence; - void (*data_ready)(struct sock *sk_raw, int length); + void (*data_ready)(struct sock *sk_raw); int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb); }; From b359cd6e67151763c2fc1988b897153b49316d14 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 8 Dec 2015 18:26:39 +0530 Subject: [PATCH 067/475] net: pppopns: pppolac: fix sendmsg function calls Fix couple of sendmsg() calls to align with changes from upstream commit 1b784140474e "net: Remove iocb argument from sendmsg and recvmsg". Change-Id: I85bc46130af8decfa37abe65aec33053ed39f1b1 Signed-off-by: Amit Pundir Signed-off-by: Dmitry Shmidt --- 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 a5d3d634fd9a..a178c863f7cf 100644 --- a/drivers/net/ppp/pppolac.c +++ b/drivers/net/ppp/pppolac.c @@ -211,7 +211,7 @@ static void pppolac_xmit_core(struct work_struct *delivery_work) .msg_iovlen = 1, .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, }; - sk_udp->sk_prot->sendmsg(NULL, sk_udp, &msg, skb->len); + sk_udp->sk_prot->sendmsg(sk_udp, &msg, skb->len); kfree_skb(skb); } set_fs(old_fs); diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c index dc15f978c922..55f485a39b88 100644 --- a/drivers/net/ppp/pppopns.c +++ b/drivers/net/ppp/pppopns.c @@ -194,7 +194,7 @@ static void pppopns_xmit_core(struct work_struct *delivery_work) .msg_iovlen = 1, .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, }; - sk_raw->sk_prot->sendmsg(NULL, sk_raw, &msg, skb->len); + sk_raw->sk_prot->sendmsg(sk_raw, &msg, skb->len); kfree_skb(skb); } set_fs(old_fs); From 1af89c1ef3b64ef0b58c938e6bcc7d5e3415461b Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 8 Dec 2015 18:28:40 +0530 Subject: [PATCH 068/475] Hack: net: PPPoPNS and PPPoLAC build fixes for 4.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream commit c0371da6047a "put iov_iter into msghdr", added iov_iter and removed direct access to scatter/gather array elements in msghdr. It broke PPPoLAC and PPPoPNS. Lets restore the direct access to scatter/gather array in msghdr for the time being. Otherwise we run into following build failure: ---------- drivers/net/ppp/pppolac.c: In function ‘pppolac_xmit_core’: drivers/net/ppp/pppolac.c:210:4: error: unknown field ‘msg_iov’ specified in initializer .msg_iov = (struct iovec *)&iov, ^ drivers/net/ppp/pppolac.c:211:4: error: unknown field ‘msg_iovlen’ specified in initializer .msg_iovlen = 1, ^ make[3]: *** [drivers/net/ppp/pppolac.o] Error 1 ---------- Change-Id: I2a1245a156da6d93b49f5cfd10506381b0eff005 Signed-off-by: Amit Pundir Signed-off-by: Dmitry Shmidt --- include/linux/socket.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/socket.h b/include/linux/socket.h index 5bf59c8493b7..18a8337c8959 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -47,6 +47,10 @@ struct linger { struct msghdr { void *msg_name; /* ptr to socket address structure */ int msg_namelen; /* size of socket address structure */ +#if defined(CONFIG_PPPOLAC) || defined(CONFIG_PPPOPNS) + struct iovec *msg_iov; /* scatter/gather array */ + __kernel_size_t msg_iovlen; /* # elements in msg_iov */ +#endif struct iov_iter msg_iter; /* data */ void *msg_control; /* ancillary data */ __kernel_size_t msg_controllen; /* ancillary data buffer length */ From 4b5ef813e8da57362b18ac8a277ec4432a4e0bfe Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 8 Dec 2015 12:47:01 +0530 Subject: [PATCH 069/475] net: PPPoPNS and PPPoLAC build fixes for 4.4 Fix couple of sk_alloc() calls to align with mainline commit 11aa9c28b420 "net: Pass kern from net_proto_family.create to sk_alloc". Signed-off-by: Amit Pundir --- drivers/net/ppp/pppolac.c | 4 ++-- drivers/net/ppp/pppopns.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c index a178c863f7cf..1b8180cc1d4d 100644 --- a/drivers/net/ppp/pppolac.c +++ b/drivers/net/ppp/pppolac.c @@ -396,11 +396,11 @@ static struct proto_ops pppolac_proto_ops = { .mmap = sock_no_mmap, }; -static int pppolac_create(struct net *net, struct socket *sock) +static int pppolac_create(struct net *net, struct socket *sock, int kern) { struct sock *sk; - sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppolac_proto); + sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppolac_proto, kern); if (!sk) return -ENOMEM; diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c index 55f485a39b88..568bb45cfeac 100644 --- a/drivers/net/ppp/pppopns.c +++ b/drivers/net/ppp/pppopns.c @@ -375,11 +375,11 @@ static struct proto_ops pppopns_proto_ops = { .mmap = sock_no_mmap, }; -static int pppopns_create(struct net *net, struct socket *sock) +static int pppopns_create(struct net *net, struct socket *sock, int kern) { struct sock *sk; - sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto); + sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto, kern); if (!sk) return -ENOMEM; From 877ca0fe3a19ef0ceaae6e99c89860fa39119ef7 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 20 Jun 2011 12:41:46 -0700 Subject: [PATCH 070/475] 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 4692782b5280..c82852322cd5 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1278,6 +1278,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 @@ -1311,6 +1313,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 7638c36b498c..70a07dbf0413 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -156,6 +156,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 8577f158df19072bcc9bea7bdb2dfce333927205 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 21 Jun 2011 11:14:49 -0700 Subject: [PATCH 071/475] 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 6291fa48c9ccada9eaca0b128f66f7b6c1ad067a Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 12 Jul 2011 12:02:59 -0700 Subject: [PATCH 072/475] 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 c82852322cd5..273f26b67653 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1339,6 +1339,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 70a07dbf0413..ad6a8aa63b1f 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -158,6 +158,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 c2506e3c58ab22a238123aa9a4362a8ead4dbc45 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 13 Apr 2012 19:22:35 -0700 Subject: [PATCH 073/475] 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 d720a2d46eef7d685f942f64bc8f9e1eb9f57b75 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 17 Apr 2012 16:00:07 -0700 Subject: [PATCH 074/475] 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 56766b1fff76fe0c4770de848acfff9b524a8ac8 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 26 Apr 2012 23:28:35 -0700 Subject: [PATCH 075/475] 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 29d2c31f406c..fd1df37dbe30 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); } @@ -146,6 +186,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); @@ -169,14 +211,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; } @@ -185,8 +237,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"); @@ -205,8 +258,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); @@ -220,6 +281,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) } mutex_unlock(&list_mutex); + return 0; } @@ -241,7 +303,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); @@ -249,6 +311,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), @@ -314,3 +377,4 @@ MODULE_DESCRIPTION("Xtables: idle time monitor"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("ipt_IDLETIMER"); MODULE_ALIAS("ip6t_IDLETIMER"); +MODULE_ALIAS("arpt_IDLETIMER"); From bd8db05002753117d38ad9783d96f0b118f0f42b Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 27 Apr 2012 12:57:39 -0700 Subject: [PATCH 076/475] 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 712fffff9906abf6f7f54f83bff1bd3cdfc84e51 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 28 Aug 2012 16:53:32 -0700 Subject: [PATCH 077/475] 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 839c250642e19752a6b12b82f7edaeff2a6f4fe4 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 9 Oct 2012 20:38:21 -0700 Subject: [PATCH 078/475] 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 d948af4500d6620b8c1c4884378058ee3db166d6 Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Mon, 19 Nov 2012 11:44:51 -0800 Subject: [PATCH 079/475] 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 d39229d5c136abb749a58d44c292bd385716c3fa Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 4 Jan 2013 18:18:36 -0800 Subject: [PATCH 080/475] 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 0a0193303020bef11f46a2876a687acf3bce4fe0 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 28 Jan 2013 16:50:44 -0800 Subject: [PATCH 081/475] 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 f2bece1c3db64eea301b481da1a3f545f8ab061b Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 6 Feb 2013 17:40:07 -0800 Subject: [PATCH 082/475] 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 7abbf897f7fe4bf811191364996327a93124c024 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 8 Apr 2013 15:09:26 -0700 Subject: [PATCH 083/475] 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 eaeeab0d2636606ece33fed81062b986d811f404 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 084/475] 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 81ceb3855920820652c0a11ad8e4433fd3bfcda6 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 085/475] 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 4ec6d23876c5..435664135785 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); @@ -1934,43 +1837,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", @@ -1981,66 +1926,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; } /* @@ -2553,7 +2474,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]; @@ -2571,178 +2492,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; } /*------------------------------------------*/ @@ -2907,6 +2880,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) { @@ -2918,26 +2932,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 ad5d02a5860a84a56078e1684575b79978c2e6f8 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 20 Feb 2013 16:38:34 -0800 Subject: [PATCH 086/475] 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 435664135785..e476b88f9d68 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1597,14 +1597,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; @@ -1688,6 +1687,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 1319d42c6b7f0b25f7c44beb7e11a0516da90d83 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Wed, 4 Dec 2013 17:39:27 -0800 Subject: [PATCH 087/475] netfilter: xt_qtaguid: fix memory leak in seq_file handlers Change-Id: I15b21230d52479d008a00d9e2191dda020f00925 Signed-off-by: Greg Hackmann --- 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 e476b88f9d68..4a16829969a6 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1496,7 +1496,7 @@ 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, + .release = seq_release_private, }; static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) @@ -2904,7 +2904,7 @@ static const struct file_operations proc_qtaguid_ctrl_fops = { .read = seq_read, .write = qtaguid_ctrl_proc_write, .llseek = seq_lseek, - .release = seq_release, + .release = seq_release_private, }; static const struct seq_operations proc_qtaguid_stats_seqops = { From 564578ba4578682401bd4416fcf42555dc056434 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 24 Feb 2014 09:39:46 -0800 Subject: [PATCH 088/475] netfilter: xt_qtaguid: 64-bit warning fixes Change-Id: I2adc517c0c51050ed601992fa0ea4de8f1449414 Signed-off-by: Greg Hackmann --- net/netfilter/xt_qtaguid.c | 14 +++++++------- net/netfilter/xt_quota2.c | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 4a16829969a6..eee667987f7d 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -594,7 +594,7 @@ static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) } } -static int read_proc_u64(struct file *file, char __user *buf, +static ssize_t read_proc_u64(struct file *file, char __user *buf, size_t size, loff_t *ppos) { uint64_t *valuep = PDE_DATA(file_inode(file)); @@ -605,7 +605,7 @@ static int read_proc_u64(struct file *file, char __user *buf, return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } -static int read_proc_bool(struct file *file, char __user *buf, +static ssize_t read_proc_bool(struct file *file, char __user *buf, size_t size, loff_t *ppos) { bool *valuep = PDE_DATA(file_inode(file)); @@ -1488,7 +1488,7 @@ static int proc_iface_stat_fmt_open(struct inode *inode, struct file *file) if (!s) return -ENOMEM; - s->fmt = (int)PDE_DATA(inode); + s->fmt = (uintptr_t)PDE_DATA(inode); return 0; } @@ -2440,10 +2440,10 @@ err: return res; } -static int qtaguid_ctrl_parse(const char *input, int count) +static ssize_t qtaguid_ctrl_parse(const char *input, size_t count) { char cmd; - int res; + ssize_t res; CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", input, current->pid, current->tgid, current_fsuid()); @@ -2474,12 +2474,12 @@ static int qtaguid_ctrl_parse(const char *input, int count) if (!res) res = count; err: - CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl(%s): res=%zd\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, +static ssize_t qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *offp) { char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 44ebdcc75965..4328562572f6 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -122,7 +122,7 @@ static void quota2_log(unsigned int hooknum, } #endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ -static int quota_proc_read(struct file *file, char __user *buf, +static ssize_t quota_proc_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { struct xt_quota_counter *e = PDE_DATA(file_inode(file)); @@ -135,7 +135,7 @@ static int quota_proc_read(struct file *file, char __user *buf, return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } -static int quota_proc_write(struct file *file, const char __user *input, +static ssize_t quota_proc_write(struct file *file, const char __user *input, size_t size, loff_t *ppos) { struct xt_quota_counter *e = PDE_DATA(file_inode(file)); From 4a0851b8b57ec8c873e336d3ac24fb6593ab1c86 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 29 Sep 2011 15:36:49 -0700 Subject: [PATCH 089/475] 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 5c5d23e59da5..835ec57c233b 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 8a8ad1cf6587b127429ed180788cfe5137a53a23 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 28 Mar 2014 12:19:27 -0700 Subject: [PATCH 090/475] net: kuid/kguid build fixes Small build fixes for xt_quota2 and ipv4 changes Change-Id: Ib098768040c8875887b2081c3165a6c83b37e180 Signed-off-by: John Stultz --- net/ipv4/tcp.c | 18 ++++++++++-------- net/netfilter/xt_quota2.c | 7 ++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index cab28756a96b..ced1683b2f3b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1289,7 +1289,8 @@ out_nopush: release_sock(sk); if (copied + copied_syn) - uid_stat_tcp_snd(current_uid(), copied + copied_syn); + uid_stat_tcp_snd(from_kuid(&init_user_ns, current_uid()), + copied + copied_syn); return copied + copied_syn; do_fault: @@ -1564,7 +1565,8 @@ 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); + uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), + copied); } return copied; } @@ -1900,7 +1902,8 @@ skip_copy: release_sock(sk); if (copied > 0) - uid_stat_tcp_rcv(current_uid(), copied); + uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), + copied); return copied; out: @@ -1910,7 +1913,8 @@ out: recv_urg: err = tcp_recv_urg(sk, msg, len, flags); if (err > 0) - uid_stat_tcp_rcv(current_uid(), err); + uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), + err); goto out; recv_sndq: @@ -3229,7 +3233,7 @@ int tcp_nuke_addr(struct net *net, struct sockaddr *addr) struct in_addr *in; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - struct in6_addr *in6; + struct in6_addr *in6 = NULL; #endif if (family == AF_INET) { in = &((struct sockaddr_in *)addr)->sin_addr; @@ -3281,10 +3285,8 @@ restart: #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; + s6 = &sk->sk_v6_rcv_saddr; if (ipv6_addr_type(s6) == IPV6_ADDR_MAPPED) continue; diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 4328562572f6..99592ae56d9b 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -52,12 +52,9 @@ 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; +static kuid_t quota_list_uid = KUIDT_INIT(0); +static kgid_t quota_list_gid = KGIDT_INIT(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); - #ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG static void quota2_log(unsigned int hooknum, From a02cb7d6ceec2765e9978d3e7a2e5c704c2879dd Mon Sep 17 00:00:00 2001 From: "Jon Medhurst (Tixy)" Date: Mon, 14 Apr 2014 21:20:49 -0700 Subject: [PATCH 091/475] xt_qtaguid: Fix boot panic We need the change below because of mainline commit 351638e7de (net: pass info struct via netdevice notifier). Otherwise we panic. Change-Id: I7daf7513a733933fdcbaeebea7f8191f8b6a0432 Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index eee667987f7d..4f574a6fc1fb 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1375,7 +1375,7 @@ unlock: static int iface_netdev_event_handler(struct notifier_block *nb, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (unlikely(module_passive)) return NOTIFY_DONE; From d726599912465970bf19af891dfcbb9a01d10691 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Tue, 25 Mar 2014 16:43:28 -0700 Subject: [PATCH 092/475] nf: IDLETIMER: time-stamp and suspend/resume handling. Message notifications contains an additional timestamp field in nano seconds. The expiry time for the timers are modified during suspend/resume. If timer was supposed to expire while the system is suspended then a notification is sent when it resumes with the timestamp of the scheduled expiry. Removes the race condition for multiple work scheduled. Bug: 13247811 Change-Id: I752c5b00225fe7085482819f975cc0eb5af89bff Signed-off-by: Ruchi Kandoi --- net/netfilter/xt_IDLETIMER.c | 169 +++++++++++++++++++++++++++++++---- 1 file changed, 152 insertions(+), 17 deletions(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index fd1df37dbe30..e7694d0a4d4a 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -42,6 +42,11 @@ #include #include #include +#include +#include +#include +#include +#include #include struct idletimer_tg_attr { @@ -58,22 +63,65 @@ struct idletimer_tg { struct kobject *kobj; struct idletimer_tg_attr attr; + struct timespec delayed_timer_trigger; + struct timespec last_modified_timer; + struct timespec last_suspend_time; + struct notifier_block pm_nb; + + int timeout; unsigned int refcnt; + bool work_pending; bool send_nl_msg; bool active; }; static LIST_HEAD(idletimer_tg_list); static DEFINE_MUTEX(list_mutex); +static DEFINE_SPINLOCK(timestamp_lock); static struct kobject *idletimer_tg_kobj; +static bool check_for_delayed_trigger(struct idletimer_tg *timer, + struct timespec *ts) +{ + bool state; + struct timespec temp; + spin_lock_bh(×tamp_lock); + timer->work_pending = false; + if ((ts->tv_sec - timer->last_modified_timer.tv_sec) > timer->timeout || + timer->delayed_timer_trigger.tv_sec != 0) { + state = false; + temp.tv_sec = timer->timeout; + temp.tv_nsec = 0; + if (timer->delayed_timer_trigger.tv_sec != 0) { + temp = timespec_add(timer->delayed_timer_trigger, temp); + ts->tv_sec = temp.tv_sec; + ts->tv_nsec = temp.tv_nsec; + timer->delayed_timer_trigger.tv_sec = 0; + timer->work_pending = true; + schedule_work(&timer->work); + } else { + temp = timespec_add(timer->last_modified_timer, temp); + ts->tv_sec = temp.tv_sec; + ts->tv_nsec = temp.tv_nsec; + } + } else { + state = timer->active; + } + spin_unlock_bh(×tamp_lock); + return state; +} + 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 }; + char timestamp_msg[NLMSG_MAX_SIZE]; + char *envp[] = { iface_msg, state_msg, timestamp_msg, NULL }; int res; + struct timespec ts; + uint64_t time_ns; + bool state; res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s", iface); @@ -81,12 +129,24 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) pr_err("message too long (%d)", res); return; } + + get_monotonic_boottime(&ts); + state = check_for_delayed_trigger(timer, &ts); res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s", - timer->active ? "active" : "inactive"); + state ? "active" : "inactive"); + if (NLMSG_MAX_SIZE <= res) { pr_err("message too long (%d)", res); return; } + + time_ns = timespec_to_ns(&ts); + res = snprintf(timestamp_msg, NLMSG_MAX_SIZE, "TIME_NS=%llu", time_ns); + if (NLMSG_MAX_SIZE <= res) { + timestamp_msg[0] = '\0'; + pr_err("message too long (%d)", res); + } + pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); return; @@ -151,9 +211,55 @@ static void idletimer_tg_expired(unsigned long data) struct idletimer_tg *timer = (struct idletimer_tg *) data; pr_debug("timer %s expired\n", timer->attr.attr.name); - + spin_lock_bh(×tamp_lock); timer->active = false; + timer->work_pending = true; schedule_work(&timer->work); + spin_unlock_bh(×tamp_lock); +} + +static int idletimer_resume(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + struct timespec ts; + unsigned long time_diff, now = jiffies; + struct idletimer_tg *timer = container_of(notifier, + struct idletimer_tg, pm_nb); + if (!timer) + return NOTIFY_DONE; + switch (pm_event) { + case PM_SUSPEND_PREPARE: + get_monotonic_boottime(&timer->last_suspend_time); + break; + case PM_POST_SUSPEND: + spin_lock_bh(×tamp_lock); + if (!timer->active) { + spin_unlock_bh(×tamp_lock); + break; + } + /* since jiffies are not updated when suspended now represents + * the time it would have suspended */ + if (time_after(timer->timer.expires, now)) { + get_monotonic_boottime(&ts); + ts = timespec_sub(ts, timer->last_suspend_time); + time_diff = timespec_to_jiffies(&ts); + if (timer->timer.expires > (time_diff + now)) { + mod_timer_pending(&timer->timer, + (timer->timer.expires - time_diff)); + } else { + del_timer(&timer->timer); + timer->timer.expires = 0; + timer->active = false; + timer->work_pending = true; + schedule_work(&timer->work); + } + } + spin_unlock_bh(×tamp_lock); + break; + default: + break; + } + return NOTIFY_DONE; } static int idletimer_tg_create(struct idletimer_tg_info *info) @@ -188,6 +294,18 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) info->timer->refcnt = 1; info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true; info->timer->active = true; + info->timer->timeout = info->timeout; + + info->timer->delayed_timer_trigger.tv_sec = 0; + info->timer->delayed_timer_trigger.tv_nsec = 0; + info->timer->work_pending = false; + get_monotonic_boottime(&info->timer->last_modified_timer); + + info->timer->pm_nb.notifier_call = idletimer_resume; + ret = register_pm_notifier(&info->timer->pm_nb); + if (ret) + printk(KERN_WARNING "[%s] Failed to register pm notifier %d\n", + __func__, ret); mod_timer(&info->timer->timer, msecs_to_jiffies(info->timeout * 1000) + jiffies); @@ -204,6 +322,34 @@ out: return ret; } +static void reset_timer(const struct idletimer_tg_info *info) +{ + unsigned long now = jiffies; + struct idletimer_tg *timer = info->timer; + bool timer_prev; + + spin_lock_bh(×tamp_lock); + timer_prev = timer->active; + timer->active = true; + /* timer_prev is used to guard overflow problem in time_before*/ + if (!timer_prev || time_before(timer->timer.expires, now)) { + pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", + timer->timer.expires, now); + /* checks if there is a pending inactive notification*/ + if (timer->work_pending) + timer->delayed_timer_trigger = timer->last_modified_timer; + else { + timer->work_pending = true; + schedule_work(&timer->work); + } + } + + get_monotonic_boottime(&timer->last_modified_timer); + mod_timer(&timer->timer, + msecs_to_jiffies(info->timeout * 1000) + now); + spin_unlock_bh(×tamp_lock); +} + /* * The actual xt_tables plugin. */ @@ -227,9 +373,7 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, } /* TODO: Avoid modifying timers on each packet */ - mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + now); - + reset_timer(info); return XT_CONTINUE; } @@ -258,17 +402,7 @@ 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) + now); - + reset_timer(info); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); } else { @@ -299,6 +433,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) list_del(&info->timer->entry); del_timer_sync(&info->timer->timer); sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr); + unregister_pm_notifier(&info->timer->pm_nb); kfree(info->timer->attr.attr.name); kfree(info->timer); } else { From acbd8a50ab7d16f1f41e438dd6a0ecd7df5ac7f7 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Thu, 24 Apr 2014 14:07:53 -0700 Subject: [PATCH 093/475] nf: Remove compilation error caused by e8430cbed3ef15fdb1ac26cfd020e010aa5f1c35 Signed-off-by: Ruchi Kandoi --- net/netfilter/xt_IDLETIMER.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index e7694d0a4d4a..1722dd34fe26 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -381,7 +381,6 @@ 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); From 644623af529228fcfe0a87724d776689f5a24525 Mon Sep 17 00:00:00 2001 From: Sherman Yin Date: Thu, 12 Jun 2014 14:35:38 -0700 Subject: [PATCH 094/475] netfilter: fix seq_printf type mismatch warning The return type of atomic64_read() varies depending on arch. The arm64 version is being changed from long long to long in the mainline for v3.16, causing a seq_printf type mismatch (%llu) in guid_ctrl_proc_show(). This commit fixes the type mismatch by casting atomic64_read() to u64. Change-Id: Iae0a6bd4314f5686a9f4fecbe6203e94ec0870de Signed-off-by: Sherman Yin --- net/netfilter/xt_qtaguid.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 4f574a6fc1fb..7c9be1706533 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1950,18 +1950,18 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) "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)); + (u64)atomic64_read(&qtu_events.sockets_tagged), + (u64)atomic64_read(&qtu_events.sockets_untagged), + (u64)atomic64_read(&qtu_events.counter_set_changes), + (u64)atomic64_read(&qtu_events.delete_cmds), + (u64)atomic64_read(&qtu_events.iface_events), + (u64)atomic64_read(&qtu_events.match_calls), + (u64)atomic64_read(&qtu_events.match_calls_prepost), + (u64)atomic64_read(&qtu_events.match_found_sk), + (u64)atomic64_read(&qtu_events.match_found_sk_in_ct), + (u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct), + (u64)atomic64_read(&qtu_events.match_no_sk), + (u64)atomic64_read(&qtu_events.match_no_sk_file)); /* Count the following as part of the last item_index */ prdebug_full_state(0, "proc ctrl"); From 29d046ebd1ef5a532827502466ae127286ba1de8 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Thu, 23 Apr 2015 12:09:09 -0700 Subject: [PATCH 095/475] nf: IDLETIMER: Adds the uid field in the msg Message notifications contains an additional uid field. This field represents the uid that was responsible for waking the radio. And hence it is present only in notifications stating that the radio is now active. Change-Id: I18fc73eada512e370d7ab24fc9f890845037b729 Signed-off-by: Ruchi Kandoi Bug: 20264396 --- net/netfilter/xt_IDLETIMER.c | 37 +++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 1722dd34fe26..a618b89f0c2b 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -48,6 +48,7 @@ #include #include #include +#include struct idletimer_tg_attr { struct attribute attr; @@ -73,6 +74,7 @@ struct idletimer_tg { bool work_pending; bool send_nl_msg; bool active; + uid_t uid; }; static LIST_HEAD(idletimer_tg_list); @@ -117,7 +119,8 @@ 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 timestamp_msg[NLMSG_MAX_SIZE]; - char *envp[] = { iface_msg, state_msg, timestamp_msg, NULL }; + char uid_msg[NLMSG_MAX_SIZE]; + char *envp[] = { iface_msg, state_msg, timestamp_msg, uid_msg, NULL }; int res; struct timespec ts; uint64_t time_ns; @@ -140,6 +143,16 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) return; } + if (state) { + res = snprintf(uid_msg, NLMSG_MAX_SIZE, "UID=%u", timer->uid); + if (NLMSG_MAX_SIZE <= res) + pr_err("message too long (%d)", res); + } else { + res = snprintf(uid_msg, NLMSG_MAX_SIZE, "UID="); + if (NLMSG_MAX_SIZE <= res) + pr_err("message too long (%d)", res); + } + time_ns = timespec_to_ns(&ts); res = snprintf(timestamp_msg, NLMSG_MAX_SIZE, "TIME_NS=%llu", time_ns); if (NLMSG_MAX_SIZE <= res) { @@ -147,7 +160,8 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) pr_err("message too long (%d)", res); } - pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); + pr_debug("putting nlmsg: <%s> <%s> <%s> <%s>\n", iface_msg, state_msg, + timestamp_msg, uid_msg); kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); return; @@ -299,6 +313,7 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) info->timer->delayed_timer_trigger.tv_sec = 0; info->timer->delayed_timer_trigger.tv_nsec = 0; info->timer->work_pending = false; + info->timer->uid = 0; get_monotonic_boottime(&info->timer->last_modified_timer); info->timer->pm_nb.notifier_call = idletimer_resume; @@ -322,7 +337,8 @@ out: return ret; } -static void reset_timer(const struct idletimer_tg_info *info) +static void reset_timer(const struct idletimer_tg_info *info, + struct sk_buff *skb) { unsigned long now = jiffies; struct idletimer_tg *timer = info->timer; @@ -335,6 +351,17 @@ static void reset_timer(const struct idletimer_tg_info *info) if (!timer_prev || time_before(timer->timer.expires, now)) { pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", timer->timer.expires, now); + + /* Stores the uid resposible for waking up the radio */ + if (skb && (skb->sk)) { + struct sock *sk = skb->sk; + read_lock_bh(&sk->sk_callback_lock); + if ((sk->sk_socket) && (sk->sk_socket->file) && + (sk->sk_socket->file->f_cred)) + timer->uid = sk->sk_socket->file->f_cred->uid; + read_unlock_bh(&sk->sk_callback_lock); + } + /* checks if there is a pending inactive notification*/ if (timer->work_pending) timer->delayed_timer_trigger = timer->last_modified_timer; @@ -373,7 +400,7 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, } /* TODO: Avoid modifying timers on each packet */ - reset_timer(info); + reset_timer(info, skb); return XT_CONTINUE; } @@ -401,7 +428,7 @@ 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++; - reset_timer(info); + reset_timer(info, NULL); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); } else { From 3a6018a2c8b4f0ca5b162c758dc73f3cbc9f5f98 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 11 May 2015 14:39:59 +0530 Subject: [PATCH 096/475] nf: IDLETIMER: Fix broken uid field in the msg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create uid from kuid to fix the broken uid field in the message notifications introduced in Change-Id: I18fc73eada512e370d7ab24fc9f890845037b729, Otherwise we run into following build error: --------------- CC net/netfilter/xt_IDLETIMER.o net/netfilter/xt_IDLETIMER.c: In function ‘reset_timer’: net/netfilter/xt_IDLETIMER.c:360:16: error: incompatible types when assigning to type ‘uid_t’ from type ‘kuid_t’ make[2]: *** [net/netfilter/xt_IDLETIMER.o] Error 1 --------------- Signed-off-by: Amit Pundir (cherry picked from commit 706060ba3e1dee9ec3c4a4a1480d663b6cd71cad) Change-Id: Ifd66df45a58d1a5a60c3816c373ee3008292eee8 --- net/netfilter/xt_IDLETIMER.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index a618b89f0c2b..0975c993a94e 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -354,12 +354,8 @@ static void reset_timer(const struct idletimer_tg_info *info, /* Stores the uid resposible for waking up the radio */ if (skb && (skb->sk)) { - struct sock *sk = skb->sk; - read_lock_bh(&sk->sk_callback_lock); - if ((sk->sk_socket) && (sk->sk_socket->file) && - (sk->sk_socket->file->f_cred)) - timer->uid = sk->sk_socket->file->f_cred->uid; - read_unlock_bh(&sk->sk_callback_lock); + timer->uid = from_kuid_munged(current_user_ns(), + sock_i_uid(skb->sk)); } /* checks if there is a pending inactive notification*/ From 6dd69fdc001f7e50bb6facdd981365abe8e5f6dc Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 26 Mar 2014 19:35:41 +0900 Subject: [PATCH 097/475] net: ipv6: autoconf routes into per-device tables Currently, IPv6 router discovery always puts routes into RT6_TABLE_MAIN. This causes problems for connection managers that want to support multiple simultaneous network connections and want control over which one is used by default (e.g., wifi and wired). To work around this connection managers typically take the routes they prefer and copy them to static routes with low metrics in the main table. This puts the burden on the connection manager to watch netlink to see if the routes have changed, delete the routes when their lifetime expires, etc. Instead, this patch adds a per-interface sysctl to have the kernel put autoconf routes into different tables. This allows each interface to have its own autoconf table, and choosing the default interface (or using different interfaces at the same time for different types of traffic) can be done using appropriate ip rules. The sysctl behaves as follows: - = 0: default. Put routes into RT6_TABLE_MAIN as before. - > 0: manual. Put routes into the specified table. - < 0: automatic. Add the absolute value of the sysctl to the device's ifindex, and use that table. The automatic mode is most useful in conjunction with net.ipv6.conf.default.accept_ra_rt_table. A connection manager or distribution could set it to, say, -100 on boot, and thereafter just use IP rules. Change-Id: I82d16e3737d9cdfa6489e649e247894d0d60cbb1 Signed-off-by: Lorenzo Colitti --- include/linux/ipv6.h | 1 + include/net/addrconf.h | 2 ++ include/uapi/linux/ipv6.h | 1 + net/ipv6/addrconf.c | 39 +++++++++++++++++++-- net/ipv6/route.c | 72 ++++++++++++++++----------------------- 5 files changed, 70 insertions(+), 45 deletions(-) diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 402753bccafa..ce777260e9ea 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -39,6 +39,7 @@ struct ipv6_devconf { __s32 accept_ra_rt_info_max_plen; #endif #endif + __s32 accept_ra_rt_table; __s32 proxy_ndp; __s32 accept_source_route; __s32 accept_ra_from_local; diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 78003dfb8539..3275ddf9f00d 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -227,6 +227,8 @@ static inline bool ipv6_is_mld(struct sk_buff *skb, int nexthdr, int offset) void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao); +u32 addrconf_rt_table(const struct net_device *dev, u32 default_table); + /* * anycast prototypes (anycast.c) */ diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 38b4fef20219..2b1533859749 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -164,6 +164,7 @@ enum { DEVCONF_ACCEPT_DAD, DEVCONF_FORCE_TLLAO, DEVCONF_NDISC_NOTIFY, + DEVCONF_ACCEPT_RA_RT_TABLE, DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, DEVCONF_SUPPRESS_FRAG_NDISC, diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 1f21087accab..4f7d6e0189cc 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -205,6 +205,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_ra_rt_info_max_plen = 0, #endif #endif + .accept_ra_rt_table = 0, .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, @@ -249,6 +250,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_ra_rt_info_max_plen = 0, #endif #endif + .accept_ra_rt_table = 0, .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, @@ -2145,6 +2147,31 @@ static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmp __ipv6_regen_rndid(idev); } +u32 addrconf_rt_table(const struct net_device *dev, u32 default_table) { + /* Determines into what table to put autoconf PIO/RIO/default routes + * learned on this device. + * + * - If 0, use the same table for every device. This puts routes into + * one of RT_TABLE_{PREFIX,INFO,DFLT} depending on the type of route + * (but note that these three are currently all equal to + * RT6_TABLE_MAIN). + * - If > 0, use the specified table. + * - If < 0, put routes into table dev->ifindex + (-rt_table). + */ + struct inet6_dev *idev = in6_dev_get(dev); + u32 table; + int sysctl = idev->cnf.accept_ra_rt_table; + if (sysctl == 0) { + table = default_table; + } else if (sysctl > 0) { + table = (u32) sysctl; + } else { + table = (unsigned) dev->ifindex + (-sysctl); + } + in6_dev_put(idev); + return table; +} + /* * Add prefix route. */ @@ -2154,7 +2181,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev, unsigned long expires, u32 flags) { struct fib6_config cfg = { - .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX, + .fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_PREFIX), .fc_metric = IP6_RT_PRIO_ADDRCONF, .fc_ifindex = dev->ifindex, .fc_expires = expires, @@ -2187,7 +2214,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, struct fib6_node *fn; struct rt6_info *rt = NULL; struct fib6_table *table; - u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX; + u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_PREFIX); table = fib6_get_table(dev_net(dev), tb_id); if (!table) @@ -4660,6 +4687,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; #endif #endif + array[DEVCONF_ACCEPT_RA_RT_TABLE] = cnf->accept_ra_rt_table; array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp; array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD @@ -5626,6 +5654,13 @@ static struct addrconf_sysctl_table }, #endif #endif + { + .procname = "accept_ra_rt_table", + .data = &ipv6_devconf.accept_ra_rt_table, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "proxy_ndp", .data = &ipv6_devconf.proxy_ndp, diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 826e6aa44f8d..391a3be3cabc 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -99,13 +99,12 @@ static void rt6_dst_from_metrics_check(struct rt6_info *rt); static int rt6_score_route(struct rt6_info *rt, int oif, int strict); #ifdef CONFIG_IPV6_ROUTE_INFO -static struct rt6_info *rt6_add_route_info(struct net *net, +static struct rt6_info *rt6_add_route_info(struct net_device *dev, const struct in6_addr *prefix, int prefixlen, - const struct in6_addr *gwaddr, int ifindex, - unsigned int pref); -static struct rt6_info *rt6_get_route_info(struct net *net, + const struct in6_addr *gwaddr, unsigned int pref); +static struct rt6_info *rt6_get_route_info(struct net_device *dev, const struct in6_addr *prefix, int prefixlen, - const struct in6_addr *gwaddr, int ifindex); + const struct in6_addr *gwaddr); #endif struct uncached_list { @@ -755,7 +754,6 @@ static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt) int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, const struct in6_addr *gwaddr) { - struct net *net = dev_net(dev); struct route_info *rinfo = (struct route_info *) opt; struct in6_addr prefix_buf, *prefix; unsigned int pref; @@ -800,8 +798,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, if (rinfo->prefix_len == 0) rt = rt6_get_dflt_router(gwaddr, dev); else - rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, - gwaddr, dev->ifindex); + rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr); if (rt && !lifetime) { ip6_del_rt(rt); @@ -809,8 +806,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, } if (!rt && lifetime) - rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, - pref); + rt = rt6_add_route_info(dev, prefix, rinfo->prefix_len, gwaddr, pref); else if (rt) rt->rt6i_flags = RTF_ROUTEINFO | (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); @@ -2247,15 +2243,16 @@ static void ip6_rt_copy_init(struct rt6_info *rt, struct rt6_info *ort) } #ifdef CONFIG_IPV6_ROUTE_INFO -static struct rt6_info *rt6_get_route_info(struct net *net, +static struct rt6_info *rt6_get_route_info(struct net_device *dev, const struct in6_addr *prefix, int prefixlen, - const struct in6_addr *gwaddr, int ifindex) + const struct in6_addr *gwaddr) { struct fib6_node *fn; struct rt6_info *rt = NULL; struct fib6_table *table; - table = fib6_get_table(net, RT6_TABLE_INFO); + table = fib6_get_table(dev_net(dev), + addrconf_rt_table(dev, RT6_TABLE_INFO)); if (!table) return NULL; @@ -2265,7 +2262,7 @@ static struct rt6_info *rt6_get_route_info(struct net *net, goto out; for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { - if (rt->dst.dev->ifindex != ifindex) + if (rt->dst.dev->ifindex != dev->ifindex) continue; if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) continue; @@ -2279,23 +2276,22 @@ out: return rt; } -static struct rt6_info *rt6_add_route_info(struct net *net, +static struct rt6_info *rt6_add_route_info(struct net_device *dev, const struct in6_addr *prefix, int prefixlen, - const struct in6_addr *gwaddr, int ifindex, - unsigned int pref) + const struct in6_addr *gwaddr, unsigned int pref) { struct fib6_config cfg = { .fc_metric = IP6_RT_PRIO_USER, - .fc_ifindex = ifindex, + .fc_ifindex = dev->ifindex, .fc_dst_len = prefixlen, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | RTF_UP | RTF_PREF(pref), .fc_nlinfo.portid = 0, .fc_nlinfo.nlh = NULL, - .fc_nlinfo.nl_net = net, + .fc_nlinfo.nl_net = dev_net(dev), }; - cfg.fc_table = l3mdev_fib_table_by_index(net, ifindex) ? : RT6_TABLE_INFO; + cfg.fc_table = l3mdev_fib_table_by_index(dev_net(dev), dev->ifindex) ? : addrconf_rt_table(dev, RT6_TABLE_INFO); cfg.fc_dst = *prefix; cfg.fc_gateway = *gwaddr; @@ -2305,7 +2301,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net, ip6_route_add(&cfg); - return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); + return rt6_get_route_info(dev, prefix, prefixlen, gwaddr); } #endif @@ -2314,7 +2310,8 @@ struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_dev struct rt6_info *rt; struct fib6_table *table; - table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); + table = fib6_get_table(dev_net(dev), + addrconf_rt_table(dev, RT6_TABLE_MAIN)); if (!table) return NULL; @@ -2336,7 +2333,7 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, unsigned int pref) { struct fib6_config cfg = { - .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, + .fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_DFLT), .fc_metric = IP6_RT_PRIO_USER, .fc_ifindex = dev->ifindex, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | @@ -2353,28 +2350,17 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, return rt6_get_dflt_router(gwaddr, dev); } + +int rt6_addrconf_purge(struct rt6_info *rt, void *arg) { + if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && + (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) + return -1; + return 0; +} + void rt6_purge_dflt_routers(struct net *net) { - struct rt6_info *rt; - struct fib6_table *table; - - /* NOTE: Keep consistent with rt6_get_dflt_router */ - table = fib6_get_table(net, RT6_TABLE_DFLT); - if (!table) - return; - -restart: - read_lock_bh(&table->tb6_lock); - for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { - if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && - (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { - dst_hold(&rt->dst); - read_unlock_bh(&table->tb6_lock); - ip6_del_rt(rt); - goto restart; - } - } - read_unlock_bh(&table->tb6_lock); + fib6_clean_all(net, rt6_addrconf_purge, NULL); } static void rtmsg_to_fib6_config(struct net *net, From d005e957b7903ea472f42a722a6749f779161105 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Wed, 25 Mar 2015 04:29:14 +0530 Subject: [PATCH 098/475] net: ipv6: fix build failure if IPV6_ROUTE_INFO config is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass correct arguments to rt6_get_route_info() function, otherwise we run into following Kernel build failure: ---------- CC net/ipv6/route.o net/ipv6/route.c: In function ‘rt6_route_rcv’: net/ipv6/route.c:746:27: error: ‘net’ undeclared (first use in this function) net/ipv6/route.c:746:27: note: each undeclared identifier is reported only once for each function it appears in make[2]: *** [net/ipv6/route.o] Error 1 make[1]: *** [net/ipv6] Error 2 make: *** [net] Error 2 ---------- Signed-off-by: Amit Pundir --- net/ipv6/route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 391a3be3cabc..60359bea6a16 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -798,7 +798,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, if (rinfo->prefix_len == 0) rt = rt6_get_dflt_router(gwaddr, dev); else - rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr); + rt = rt6_get_route_info(dev, prefix, rinfo->prefix_len, gwaddr); if (rt && !lifetime) { ip6_del_rt(rt); From fd2cf795f3ab193752781be7372949ac1780d0ed Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 31 Mar 2014 16:23:51 +0900 Subject: [PATCH 099/475] net: core: Support UID-based routing. This contains the following commits: 1. cc2f522 net: core: Add a UID range to fib rules. 2. d7ed2bd net: core: Use the socket UID in routing lookups. 3. 2f9306a net: core: Add a RTA_UID attribute to routes. This is so that userspace can do per-UID route lookups. 4. 8e46efb net: ipv6: Use the UID in IPv6 PMTUD IPv4 PMTUD already does this because ipv4_sk_update_pmtu uses __build_flow_key, which includes the UID. Bug: 15413527 Change-Id: Iae3d4ca3979d252b6cec989bdc1a6875f811f03a Signed-off-by: Lorenzo Colitti --- include/net/fib_rules.h | 5 +++ include/net/flow.h | 9 +++++- include/net/ip.h | 1 + include/net/ip6_route.h | 2 +- include/net/route.h | 5 +-- include/uapi/linux/fib_rules.h | 2 ++ include/uapi/linux/rtnetlink.h | 1 + net/core/fib_rules.c | 53 ++++++++++++++++++++++++++++++-- net/ipv4/fib_frontend.c | 1 + net/ipv4/inet_connection_sock.c | 4 +-- net/ipv4/ip_output.c | 3 +- net/ipv4/ping.c | 3 +- net/ipv4/raw.c | 3 +- net/ipv4/route.c | 25 +++++++++++---- net/ipv4/syncookies.c | 5 +-- net/ipv4/udp.c | 3 +- net/ipv6/af_inet6.c | 1 + net/ipv6/ah6.c | 2 +- net/ipv6/datagram.c | 1 + net/ipv6/esp6.c | 2 +- net/ipv6/icmp.c | 2 +- net/ipv6/inet6_connection_sock.c | 2 ++ net/ipv6/ipcomp6.c | 2 +- net/ipv6/ping.c | 1 + net/ipv6/raw.c | 1 + net/ipv6/route.c | 11 +++++-- net/ipv6/syncookies.c | 1 + net/ipv6/tcp_ipv6.c | 1 + net/ipv6/udp.c | 1 + 29 files changed, 127 insertions(+), 26 deletions(-) diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 59160de702b6..55b5419cb6a7 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -29,6 +29,8 @@ struct fib_rule { int suppress_prefixlen; char iifname[IFNAMSIZ]; char oifname[IFNAMSIZ]; + kuid_t uid_start; + kuid_t uid_end; struct rcu_head rcu; }; @@ -87,6 +89,9 @@ struct fib_rules_ops { [FRA_FWMARK] = { .type = NLA_U32 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ [FRA_TABLE] = { .type = NLA_U32 }, \ + [FRA_GOTO] = { .type = NLA_U32 }, \ + [FRA_UID_START] = { .type = NLA_U32 }, \ + [FRA_UID_END] = { .type = NLA_U32 }, \ [FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \ [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \ [FRA_GOTO] = { .type = NLA_U32 } diff --git a/include/net/flow.h b/include/net/flow.h index 83969eebebf3..833080732dec 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -11,6 +11,7 @@ #include #include #include +#include /* * ifindex generation is per-net namespace, and loopback is @@ -38,6 +39,7 @@ struct flowi_common { #define FLOWI_FLAG_SKIP_NH_OIF 0x08 __u32 flowic_secid; struct flowi_tunnel flowic_tun_key; + kuid_t flowic_uid; }; union flowi_uli { @@ -75,6 +77,7 @@ struct flowi4 { #define flowi4_flags __fl_common.flowic_flags #define flowi4_secid __fl_common.flowic_secid #define flowi4_tun_key __fl_common.flowic_tun_key +#define flowi4_uid __fl_common.flowic_uid /* (saddr,daddr) must be grouped, same order as in IP header */ __be32 saddr; @@ -94,7 +97,8 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, __u32 mark, __u8 tos, __u8 scope, __u8 proto, __u8 flags, __be32 daddr, __be32 saddr, - __be16 dport, __be16 sport) + __be16 dport, __be16 sport, + kuid_t uid) { fl4->flowi4_oif = oif; fl4->flowi4_iif = LOOPBACK_IFINDEX; @@ -105,6 +109,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, fl4->flowi4_flags = flags; fl4->flowi4_secid = 0; fl4->flowi4_tun_key.tun_id = 0; + fl4->flowi4_uid = uid; fl4->daddr = daddr; fl4->saddr = saddr; fl4->fl4_dport = dport; @@ -133,6 +138,7 @@ struct flowi6 { #define flowi6_flags __fl_common.flowic_flags #define flowi6_secid __fl_common.flowic_secid #define flowi6_tun_key __fl_common.flowic_tun_key +#define flowi6_uid __fl_common.flowic_uid struct in6_addr daddr; struct in6_addr saddr; __be32 flowlabel; @@ -177,6 +183,7 @@ struct flowi { #define flowi_flags u.__fl_common.flowic_flags #define flowi_secid u.__fl_common.flowic_secid #define flowi_tun_key u.__fl_common.flowic_tun_key +#define flowi_uid u.__fl_common.flowic_uid } __attribute__((__aligned__(BITS_PER_LONG/8))); static inline struct flowi *flowi4_to_flowi(struct flowi4 *fl4) diff --git a/include/net/ip.h b/include/net/ip.h index 1a98f1ca1638..4f3ef345f4c2 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -170,6 +170,7 @@ struct ip_reply_arg { /* -1 if not needed */ int bound_dev_if; u8 tos; + kuid_t uid; }; #define IP_REPLY_ARG_NOSRCCHECK 1 diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 877f682989b8..4bbd221637cd 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -108,7 +108,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, const struct in6_addr *gwaddr); void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, int oif, - u32 mark); + u32 mark, kuid_t uid); void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu); void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark); void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, diff --git a/include/net/route.h b/include/net/route.h index a3b9ef74a389..bf6fe1a6527e 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -154,7 +154,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, - daddr, saddr, dport, sport); + daddr, saddr, dport, sport, sock_i_uid(sk)); if (sk) security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -267,7 +267,8 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 flow_flags |= FLOWI_FLAG_ANYSRC; flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, - protocol, flow_flags, dst, src, dport, sport); + protocol, flow_flags, dst, src, dport, sport, + sock_i_uid(sk)); } static inline struct rtable *ip_route_connect(struct flowi4 *fl4, diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 96161b8202b5..ce19c5bf51f7 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -49,6 +49,8 @@ enum { FRA_TABLE, /* Extended table id */ FRA_FWMASK, /* mask for netfilter mark */ FRA_OIFNAME, + FRA_UID_START, /* UID range */ + FRA_UID_END, __FRA_MAX }; diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 123a5af4e8bb..355eea225dd9 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -311,6 +311,7 @@ enum rtattr_type_t { RTA_PREF, RTA_ENCAP_TYPE, RTA_ENCAP, + RTA_UID, __RTA_MAX }; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 365de66436ac..3fbd839f6d20 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -33,6 +33,8 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->table = table; r->flags = flags; r->fr_net = ops->fro_net; + r->uid_start = INVALID_UID; + r->uid_end = INVALID_UID; r->suppress_prefixlen = -1; r->suppress_ifgroup = -1; @@ -172,6 +174,23 @@ void fib_rules_unregister(struct fib_rules_ops *ops) } EXPORT_SYMBOL_GPL(fib_rules_unregister); +static inline kuid_t fib_nl_uid(struct nlattr *nla) +{ + return make_kuid(current_user_ns(), nla_get_u32(nla)); +} + +static int nla_put_uid(struct sk_buff *skb, int idx, kuid_t uid) +{ + return nla_put_u32(skb, idx, from_kuid_munged(current_user_ns(), uid)); +} + +static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule) +{ + return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) || + (uid_gte(fl->flowi_uid, rule->uid_start) && + uid_lte(fl->flowi_uid, rule->uid_end)); +} + static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, struct flowi *fl, int flags) { @@ -189,6 +208,9 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, if (rule->tun_id && (rule->tun_id != fl->flowi_tun_key.tun_id)) goto out; + if (!fib_uid_range_match(fl, rule)) + goto out; + ret = ops->match(rule, fl, flags); out: return (rule->flags & FIB_RULE_INVERT) ? !ret : ret; @@ -371,6 +393,19 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh) } else if (rule->action == FR_ACT_GOTO) goto errout_free; + /* UID start and end must either both be valid or both unspecified. */ + rule->uid_start = rule->uid_end = INVALID_UID; + if (tb[FRA_UID_START] || tb[FRA_UID_END]) { + if (tb[FRA_UID_START] && tb[FRA_UID_END]) { + rule->uid_start = fib_nl_uid(tb[FRA_UID_START]); + rule->uid_end = fib_nl_uid(tb[FRA_UID_END]); + } + if (!uid_valid(rule->uid_start) || + !uid_valid(rule->uid_end) || + !uid_lte(rule->uid_start, rule->uid_end)) + goto errout_free; + } + err = ops->configure(rule, skb, frh, tb); if (err < 0) goto errout_free; @@ -483,6 +518,14 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) (rule->tun_id != nla_get_be64(tb[FRA_TUN_ID]))) continue; + if (tb[FRA_UID_START] && + !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START]))) + continue; + + if (tb[FRA_UID_END] && + !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END]))) + continue; + if (!ops->compare(rule, frh, tb)) continue; @@ -549,7 +592,9 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_SUPPRESS_IFGROUP */ + nla_total_size(4) /* FRA_FWMARK */ + nla_total_size(4) /* FRA_FWMASK */ - + nla_total_size(8); /* FRA_TUN_ID */ + + nla_total_size(8) /* FRA_TUN_ID */ + + nla_total_size(4) /* FRA_UID_START */ + + nla_total_size(4); /* FRA_UID_END */ if (ops->nlmsg_payload) payload += ops->nlmsg_payload(rule); @@ -607,7 +652,11 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, (rule->target && nla_put_u32(skb, FRA_GOTO, rule->target)) || (rule->tun_id && - nla_put_be64(skb, FRA_TUN_ID, rule->tun_id))) + nla_put_be64(skb, FRA_TUN_ID, rule->tun_id)) || + (uid_valid(rule->uid_start) && + nla_put_uid(skb, FRA_UID_START, rule->uid_start)) || + (uid_valid(rule->uid_end) && + nla_put_uid(skb, FRA_UID_END, rule->uid_end))) goto nla_put_failure; if (rule->suppress_ifgroup != -1) { diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 473447593060..e10edb5e78b0 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -629,6 +629,7 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = { [RTA_FLOW] = { .type = NLA_U32 }, [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, [RTA_ENCAP] = { .type = NLA_NESTED }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 46b9c887bede..a4cfa14c73ee 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -420,7 +420,7 @@ struct dst_entry *inet_csk_route_req(const struct sock *sk, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, ireq->ir_loc_addr, ireq->ir_rmt_port, - htons(ireq->ir_num)); + htons(ireq->ir_num), sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) @@ -457,7 +457,7 @@ struct dst_entry *inet_csk_route_child_sock(const struct sock *sk, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, ireq->ir_loc_addr, ireq->ir_rmt_port, - htons(ireq->ir_num)); + htons(ireq->ir_num), sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 4233cbe47052..33bef2763c72 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1573,7 +1573,8 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb, RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol, ip_reply_arg_flowi_flags(arg), daddr, saddr, - tcp_hdr(skb)->source, tcp_hdr(skb)->dest); + tcp_hdr(skb)->source, tcp_hdr(skb)->dest, + arg->uid); security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(net, &fl4); if (IS_ERR(rt)) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index e89094ab5ddb..b27e98010dea 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -789,7 +789,8 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, - inet_sk_flowi_flags(sk), faddr, saddr, 0, 0); + inet_sk_flowi_flags(sk), faddr, saddr, 0, 0, + sock_i_uid(sk)); security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); rt = ip_route_output_flow(net, &fl4, sk); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index bc35f1842512..865895d3fb27 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -599,7 +599,8 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk) | (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), - daddr, saddr, 0, 0); + daddr, saddr, 0, 0, + sock_i_uid(sk)); if (!saddr && ipc.oif) { err = l3mdev_get_saddr(net, ipc.oif, &fl4); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 85f184e429c6..4704ddcf2e7c 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -500,7 +500,7 @@ void __ip_select_ident(struct net *net, struct iphdr *iph, int segs) } EXPORT_SYMBOL(__ip_select_ident); -static void __build_flow_key(struct flowi4 *fl4, const struct sock *sk, +static void __build_flow_key(struct flowi4 *fl4, struct sock *sk, const struct iphdr *iph, int oif, u8 tos, u8 prot, u32 mark, int flow_flags) @@ -516,11 +516,12 @@ static void __build_flow_key(struct flowi4 *fl4, const struct sock *sk, flowi4_init_output(fl4, oif, mark, tos, RT_SCOPE_UNIVERSE, prot, flow_flags, - iph->daddr, iph->saddr, 0, 0); + iph->daddr, iph->saddr, 0, 0, + sock_i_uid(sk)); } static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, - const struct sock *sk) + struct sock *sk) { const struct iphdr *iph = ip_hdr(skb); int oif = skb->dev->ifindex; @@ -531,7 +532,7 @@ static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, __build_flow_key(fl4, sk, iph, oif, tos, prot, mark, 0); } -static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) +static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk) { const struct inet_sock *inet = inet_sk(sk); const struct ip_options_rcu *inet_opt; @@ -545,11 +546,12 @@ static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk), - daddr, inet->inet_saddr, 0, 0); + daddr, inet->inet_saddr, 0, 0, + sock_i_uid(sk)); rcu_read_unlock(); } -static void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk, +static void ip_rt_build_flow_key(struct flowi4 *fl4, struct sock *sk, const struct sk_buff *skb) { if (skb) @@ -2422,6 +2424,11 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id, nla_put_u32(skb, RTA_MARK, fl4->flowi4_mark)) goto nla_put_failure; + if (!uid_eq(fl4->flowi4_uid, INVALID_UID) && + nla_put_u32(skb, RTA_UID, + from_kuid_munged(current_user_ns(), fl4->flowi4_uid))) + goto nla_put_failure; + error = rt->dst.error; if (rt_is_input_route(rt)) { @@ -2473,6 +2480,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) int mark; struct sk_buff *skb; u32 table_id = RT_TABLE_MAIN; + kuid_t uid; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy); if (err < 0) @@ -2500,6 +2508,10 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) dst = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0; iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0; mark = tb[RTA_MARK] ? nla_get_u32(tb[RTA_MARK]) : 0; + if (tb[RTA_UID]) + uid = make_kuid(current_user_ns(), nla_get_u32(tb[RTA_UID])); + else + uid = (iif ? INVALID_UID : current_uid()); memset(&fl4, 0, sizeof(fl4)); fl4.daddr = dst; @@ -2507,6 +2519,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) fl4.flowi4_tos = rtm->rtm_tos; fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0; fl4.flowi4_mark = mark; + fl4.flowi4_uid = uid; if (netif_index_is_l3_master(net, fl4.flowi4_oif)) fl4.flowi4_flags = FLOWI_FLAG_L3MDEV_SRC | FLOWI_FLAG_SKIP_NH_OIF; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 4cbe9f0a4281..31b6a4c9db32 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -374,8 +374,9 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP, inet_sk_flowi_flags(sk), - opt->srr ? opt->faddr : ireq->ir_rmt_addr, - ireq->ir_loc_addr, th->source, th->dest); + (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr, + ireq->ir_loc_addr, th->source, th->dest, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index c43890848641..d8946929813e 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1023,7 +1023,8 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, flow_flags, - faddr, saddr, dport, inet->inet_sport); + faddr, saddr, dport, inet->inet_sport, + sock_i_uid(sk)); if (!saddr && ipc.oif) { err = l3mdev_get_saddr(net, ipc.oif, fl4); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 036221f89509..99fccad391e0 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -695,6 +695,7 @@ int inet6_sk_rebuild_header(struct sock *sk) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); rcu_read_lock(); diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 0630a4d5daaa..c52b8fc904c9 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -664,7 +664,7 @@ static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) ip6_redirect(skb, net, skb->dev->ifindex, 0); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); xfrm_state_put(x); return 0; diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 517c55b01ba8..0743a5f4c533 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -161,6 +161,7 @@ ipv4_connected: fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST)) fl6.flowi6_oif = np->mcast_oif; diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 060a60b2f8a6..f921368c32c9 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -476,7 +476,7 @@ static int esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) ip6_redirect(skb, net, skb->dev->ifindex, 0); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); xfrm_state_put(x); return 0; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 0a37ddc7af51..a3ec7a77a1ee 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -92,7 +92,7 @@ static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, struct net *net = dev_net(skb->dev); if (type == ICMPV6_PKT_TOOBIG) - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); else if (type == NDISC_REDIRECT) ip6_redirect(skb, net, skb->dev->ifindex, 0); diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index a7ca2cde2ecb..7b214652768e 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -86,6 +86,7 @@ struct dst_entry *inet6_csk_route_req(const struct sock *sk, fl6->flowi6_mark = ireq->ir_mark; fl6->fl6_dport = ireq->ir_rmt_port; fl6->fl6_sport = htons(ireq->ir_num); + fl6->flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(fl6)); dst = ip6_dst_lookup_flow(sk, fl6, final_p); @@ -134,6 +135,7 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, fl6->flowi6_mark = sk->sk_mark; fl6->fl6_sport = inet->inet_sport; fl6->fl6_dport = inet->inet_dport; + fl6->flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); rcu_read_lock(); diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 1b9316e1386a..b247baceb797 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -76,7 +76,7 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) ip6_redirect(skb, net, skb->dev->ifindex, 0); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); xfrm_state_put(x); return 0; diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 263a5164a6f5..2cfaedf03252 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -135,6 +135,7 @@ int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl6.saddr = np->saddr; fl6.daddr = *daddr; fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); fl6.fl6_icmp_type = user_icmph.icmp6_type; fl6.fl6_icmp_code = user_icmph.icmp6_code; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 99140986e887..d9ad71a01b4c 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -768,6 +768,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); if (sin6) { if (addr_len < SIN6_LEN_RFC2133) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 60359bea6a16..02ba70201e05 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1383,7 +1383,7 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, } void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, - int oif, u32 mark) + int oif, u32 mark, kuid_t uid) { const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; struct dst_entry *dst; @@ -1395,6 +1395,7 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); + fl6.flowi6_uid = uid; dst = ip6_route_output(net, NULL, &fl6); if (!dst->error) @@ -1406,7 +1407,7 @@ EXPORT_SYMBOL_GPL(ip6_update_pmtu); void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) { ip6_update_pmtu(skb, sock_net(sk), mtu, - sk->sk_bound_dev_if, sk->sk_mark); + sk->sk_bound_dev_if, sk->sk_mark, sock_i_uid(sk)); } EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); @@ -2685,6 +2686,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { [RTA_PREF] = { .type = NLA_U8 }, [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, [RTA_ENCAP] = { .type = NLA_NESTED }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -3246,6 +3248,11 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) if (tb[RTA_MARK]) fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]); + if (tb[RTA_UID]) + fl6.flowi6_uid = make_kuid(current_user_ns(), + nla_get_u32(tb[RTA_UID])); + else + fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); if (iif) { struct net_device *dev; int flags = 0; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index eaf7ac496d50..a22015fab95e 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -228,6 +228,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) fl6.flowi6_mark = ireq->ir_mark; fl6.fl6_dport = ireq->ir_rmt_port; fl6.fl6_sport = inet_sk(sk)->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 6b8a8a9091fa..0e3c16ff1075 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -234,6 +234,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = usin->sin6_port; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk)); final_p = fl6_update_dst(&fl6, opt, &final); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 9da3287a3923..a1b6adc20e1e 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1243,6 +1243,7 @@ do_udp_sendmsg: fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); if (msg->msg_controllen) { opt = &opt_space; From ad493510385ee040516bf83a60e6c4921fcdfdac Mon Sep 17 00:00:00 2001 From: Sreeram Ramachandran Date: Tue, 8 Jul 2014 11:57:14 -0700 Subject: [PATCH 100/475] net: core: Handle 'sk' being NULL in UID-based routing It has Amit Pundir fix: net: core: fix UID-based routing build Bug: 15413527 Change-Id: Iab1fae9da6053b284591628ef1de878761b137b1 Signed-off-by: Sreeram Ramachandran Signed-off-by: Dmitry Shmidt Signed-off-by: Amit Pundir --- include/net/route.h | 3 ++- net/ipv4/route.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/net/route.h b/include/net/route.h index bf6fe1a6527e..d016a8cb45cf 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -154,7 +154,8 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, - daddr, saddr, dport, sport, sock_i_uid(sk)); + daddr, saddr, dport, sport, + sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID); if (sk) security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4704ddcf2e7c..a0d842f4e9cf 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -517,7 +517,7 @@ static void __build_flow_key(struct flowi4 *fl4, struct sock *sk, RT_SCOPE_UNIVERSE, prot, flow_flags, iph->daddr, iph->saddr, 0, 0, - sock_i_uid(sk)); + sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID); } static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, From ead9df9eb3c009cfcf0f60fae08f6c08303a8306 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 28 Mar 2014 16:23:48 -0700 Subject: [PATCH 101/475] netfilter: Build fixups - kuid/kguid changes & xt_socket_get/put_sk Fix up build kuid/kguid build issues in netfilter code. Also re-add the xt_socket_get/put_sk interfaces needed by xt_qtaguid. Change-Id: I7027fb840e109785bddffe8ea717b8d018b26d82 Signed-off-by: John Stultz --- include/uapi/linux/netfilter/xt_socket.h | 6 ++ net/netfilter/xt_qtaguid.c | 121 +++++++++++++---------- net/netfilter/xt_socket.c | 16 ++- 3 files changed, 88 insertions(+), 55 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h index 87644f832494..8f4da12ca571 100644 --- a/include/uapi/linux/netfilter/xt_socket.h +++ b/include/uapi/linux/netfilter/xt_socket.h @@ -26,4 +26,10 @@ struct xt_socket_mtinfo3 { | XT_SOCKET_NOWILDCARD \ | XT_SOCKET_RESTORESKMARK) +void xt_socket_put_sk(struct sock *sk); +struct sock *xt_socket_get4_sk(const struct sk_buff *skb, + struct xt_action_param *par); +struct sock *xt_socket_get6_sk(const struct sk_buff *skb, + struct xt_action_param *par); + #endif /* _XT_SOCKET_H */ diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 7c9be1706533..0ad8d7a896cf 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -145,22 +145,22 @@ static bool can_manipulate_uids(void) { /* root pwnd */ 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); + || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || unlikely(!proc_ctrl_write_limited) + || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid)); } -static bool can_impersonate_uid(uid_t uid) +static bool can_impersonate_uid(kuid_t uid) { - return uid == current_fsuid() || can_manipulate_uids(); + return uid_eq(uid, current_fsuid()) || can_manipulate_uids(); } -static bool can_read_other_uid_stats(uid_t uid) +static bool can_read_other_uid_stats(kuid_t uid) { /* root pwnd */ return in_egroup_p(xt_qtaguid_stats_file->gid) - || unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || uid_eq(uid, current_fsuid()) || unlikely(!proc_stats_readall_limited) - || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); + || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid)); } static inline void dc_add_byte_packets(struct data_counters *counters, int set, @@ -542,7 +542,7 @@ static void put_utd_entry(struct uid_tag_data *utd_entry) "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()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); BUG_ON(utd_entry->num_active_tags); rb_erase(&utd_entry->node, &uid_tag_data_tree); kfree(utd_entry); @@ -744,7 +744,7 @@ static int iface_stat_fmt_proc_show(struct seq_file *m, void *v) CT_DEBUG("qtaguid:proc iface_stat_fmt pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); iface_entry = list_entry(v, struct iface_stat, list); @@ -1656,7 +1656,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) const struct file *filp; bool got_sock = false; struct sock *sk; - uid_t sock_uid; + kuid_t sock_uid; bool res; if (unlikely(module_passive)) @@ -1720,7 +1720,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) 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); + par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1); } if (sk == NULL || sk->sk_socket == NULL) { @@ -1761,7 +1761,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) * 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); + account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par); /* * The following two tests fail the match when: @@ -1769,25 +1769,32 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) * 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) ^ + if (info->match & XT_QTAGUID_UID) { + kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min); + kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max); + + if (uid_gte(filp->f_cred->fsuid, uid_min) && + uid_lte(filp->f_cred->fsuid, 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) ^ + } + if (info->match & XT_QTAGUID_GID) { + kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min); + kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max); + + if (gid_gte(filp->f_cred->fsgid, gid_min) && + gid_lte(filp->f_cred->fsgid, 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; @@ -1919,7 +1926,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) long f_count; CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); if (sock_tag_entry != SEQ_START_TOKEN) { uid = get_uid_from_tag(sock_tag_entry->tag); @@ -1977,7 +1984,8 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) static int ctrl_cmd_delete(const char *input) { char cmd; - uid_t uid; + int uid_int; + kuid_t uid; uid_t entry_uid; tag_t acct_tag; tag_t tag; @@ -1991,10 +1999,11 @@ static int ctrl_cmd_delete(const char *input) struct tag_ref *tr_entry; struct uid_tag_data *utd_entry; - argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); + argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid_int); + uid = make_kuid(&init_user_ns, uid_int); CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " "user_tag=0x%llx uid=%u\n", input, argc, cmd, - acct_tag, uid); + acct_tag, uid_int); if (argc < 2) { res = -EINVAL; goto err; @@ -2006,18 +2015,19 @@ static int ctrl_cmd_delete(const char *input) } if (argc < 3) { uid = current_fsuid(); + uid_int = from_kuid(&init_user_ns, uid); } 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()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -EPERM; goto err; } - tag = combine_atag_with_uid(acct_tag, uid); + tag = combine_atag_with_uid(acct_tag, uid_int); CT_DEBUG("qtaguid: ctrl_delete(%s): " "looking for tag=0x%llx (uid=%u)\n", - input, tag, uid); + input, tag, uid_int); /* Delete socket tags */ spin_lock_bh(&sock_tag_list_lock); @@ -2026,7 +2036,7 @@ static int ctrl_cmd_delete(const char *input) 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) + if (entry_uid != uid_int) continue; CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n", @@ -2087,7 +2097,7 @@ static int ctrl_cmd_delete(const char *input) "ts tag=0x%llx (uid=%u)\n", input, ts_entry->tn.tag, entry_uid); - if (entry_uid != uid) + if (entry_uid != uid_int) continue; if (!acct_tag || ts_entry->tn.tag == tag) { CT_DEBUG("qtaguid: ctrl_delete(%s): " @@ -2116,7 +2126,7 @@ static int ctrl_cmd_delete(const char *input) "utd uid=%u\n", input, entry_uid); - if (entry_uid != uid) + if (entry_uid != uid_int) continue; /* * Go over the tag_refs, and those that don't have @@ -2160,7 +2170,7 @@ static int ctrl_cmd_counter_set(const char *input) 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()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -EPERM; goto err; } @@ -2197,7 +2207,8 @@ static int ctrl_cmd_tag(const char *input) { char cmd; int sock_fd = 0; - uid_t uid = 0; + kuid_t uid; + unsigned int uid_int = 0; tag_t acct_tag = make_atag_from_value(0); tag_t full_tag; struct socket *el_socket; @@ -2208,10 +2219,11 @@ static int ctrl_cmd_tag(const char *input) 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); + argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid_int); + uid = make_kuid(&init_user_ns, uid_int); 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); + acct_tag, uid_int); if (argc < 2) { res = -EINVAL; goto err; @@ -2221,7 +2233,7 @@ static int ctrl_cmd_tag(const char *input) pr_info("qtaguid: ctrl_tag(%s): failed to lookup" " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", input, sock_fd, res, current->pid, current->tgid, - current_fsuid()); + from_kuid(&init_user_ns, current_fsuid())); goto err; } CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2237,21 +2249,24 @@ static int ctrl_cmd_tag(const char *input) CT_DEBUG("qtaguid: ctrl_tag(%s): " "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " "ctrl.gid=%u in_group()=%d in_egroup()=%d\n", - input, current->pid, current->tgid, current_uid(), - current_euid(), current_fsuid(), - xt_qtaguid_ctrl_file->gid, + input, current->pid, current->tgid, + from_kuid(&init_user_ns, current_uid()), + from_kuid(&init_user_ns, current_euid()), + from_kuid(&init_user_ns, current_fsuid()), + from_kgid(&init_user_ns, 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(); + uid_int = from_kuid(&init_user_ns, uid); } 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()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -EPERM; goto err_put; } - full_tag = combine_atag_with_uid(acct_tag, uid); + full_tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); @@ -2298,8 +2313,7 @@ static int ctrl_cmd_tag(const char *input) 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); + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&uid_tag_data_tree_lock); pqd_entry = proc_qtu_data_tree_search( &proc_qtu_data_tree, current->tgid); @@ -2314,7 +2328,7 @@ static int ctrl_cmd_tag(const char *input) "User space forgot to open /dev/xt_qtaguid? " "pid=%u tgid=%u uid=%u\n", __func__, current->pid, current->tgid, - current_fsuid()); + from_kuid(&init_user_ns, current_fsuid())); else list_add(&sock_tag_entry->list, &pqd_entry->sock_tag_list); @@ -2369,7 +2383,7 @@ static int ctrl_cmd_untag(const char *input) pr_info("qtaguid: ctrl_untag(%s): failed to lookup" " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", input, sock_fd, res, current->pid, current->tgid, - current_fsuid()); + from_kuid(&init_user_ns, current_fsuid())); goto err; } CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2403,7 +2417,7 @@ static int ctrl_cmd_untag(const char *input) 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()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); else list_del(&sock_tag_entry->list); spin_unlock_bh(&uid_tag_data_tree_lock); @@ -2446,7 +2460,7 @@ static ssize_t qtaguid_ctrl_parse(const char *input, size_t count) ssize_t res; CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", - input, current->pid, current->tgid, current_fsuid()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); cmd = input[0]; /* Collect params for commands */ @@ -2528,14 +2542,15 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, 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)) { + if (get_atag_from_tag(tag) && !can_read_other_uid_stats( + make_kuid(&init_user_ns,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); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()), + from_kgid(&init_user_ns,xt_qtaguid_stats_file->gid)); return 0; } ppi->item_index++; @@ -2737,12 +2752,12 @@ static int qtudev_open(struct inode *inode, struct file *file) return 0; DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, 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); + utd_entry = get_uid_data(from_kuid(&init_user_ns, current_fsuid()), &utd_entry_found); if (IS_ERR_OR_NULL(utd_entry)) { res = PTR_ERR(utd_entry); goto err_unlock; @@ -2754,7 +2769,7 @@ static int qtudev_open(struct inode *inode, struct file *file) if (pqd_entry) { pr_err("qtaguid: qtudev_open(): %u/%u %u " "%s already opened\n", - current->pid, current->tgid, current_fsuid(), + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()), QTU_DEV_NAME); res = -EBUSY; goto err_unlock_free_utd; @@ -2764,7 +2779,7 @@ static int qtudev_open(struct inode *inode, struct file *file) if (!new_pqd_entry) { pr_err("qtaguid: qtudev_open(): %u/%u %u: " "proc data alloc failed\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -ENOMEM; goto err_unlock_free_utd; } @@ -2778,7 +2793,7 @@ static int qtudev_open(struct inode *inode, struct file *file) 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); + from_kuid(&init_user_ns, current_fsuid()), new_pqd_entry); file->private_data = new_pqd_entry; return 0; diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 2ec08f04b816..d9fe8fbd5fd8 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -35,6 +35,16 @@ #include #endif +void +xt_socket_put_sk(struct sock *sk) +{ + if (sk->sk_state == TCP_TIME_WAIT) + inet_twsk_put(inet_twsk(sk)); + else + sock_put(sk); +} +EXPORT_SYMBOL(xt_socket_put_sk); + static int extract_icmp4_fields(const struct sk_buff *skb, u8 *protocol, @@ -143,7 +153,7 @@ static bool xt_socket_sk_is_transparent(struct sock *sk) } } -static struct sock *xt_socket_lookup_slow_v4(struct net *net, +struct sock *xt_socket_lookup_slow_v4(struct net *net, const struct sk_buff *skb, const struct net_device *indev) { @@ -201,6 +211,7 @@ static struct sock *xt_socket_lookup_slow_v4(struct net *net, return xt_socket_get_sock_v4(net, protocol, saddr, daddr, sport, dport, indev); } +EXPORT_SYMBOL(xt_socket_lookup_slow_v4); static bool socket_match(const struct sk_buff *skb, struct xt_action_param *par, @@ -336,7 +347,7 @@ xt_socket_get_sock_v6(struct net *net, const u8 protocol, return NULL; } -static struct sock *xt_socket_lookup_slow_v6(struct net *net, +struct sock *xt_socket_lookup_slow_v6(struct net *net, const struct sk_buff *skb, const struct net_device *indev) { @@ -376,6 +387,7 @@ static struct sock *xt_socket_lookup_slow_v6(struct net *net, return xt_socket_get_sock_v6(net, tproto, saddr, daddr, sport, dport, indev); } +EXPORT_SYMBOL(xt_socket_lookup_slow_v6); static bool socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) From 6fafffff1af8659928b8f06e1a12fee3b4423858 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 20 Jan 2015 16:13:08 +0530 Subject: [PATCH 102/475] xt_qtaguid: fix broken uid/gid range check The existing test to check if current uid/gid is within valid range is broken due to missing parenthesis. Change-Id: I889ebbd0e2ea6a9426cb1509a2975e7107666407 Signed-off-by: Amit Pundir Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 0ad8d7a896cf..9664bec1091c 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1773,8 +1773,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min); kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max); - if (uid_gte(filp->f_cred->fsuid, uid_min) && - uid_lte(filp->f_cred->fsuid, uid_max) ^ + if ((uid_gte(filp->f_cred->fsuid, uid_min) && + uid_lte(filp->f_cred->fsuid, uid_max)) ^ !(info->invert & XT_QTAGUID_UID)) { MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", par->hooknum); @@ -1786,8 +1786,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min); kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max); - if (gid_gte(filp->f_cred->fsgid, gid_min) && - gid_lte(filp->f_cred->fsgid, gid_max) ^ + if ((gid_gte(filp->f_cred->fsgid, gid_min) && + gid_lte(filp->f_cred->fsgid, gid_max)) ^ !(info->invert & XT_QTAGUID_GID)) { MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", par->hooknum); From 7c36bff505b117a9fd43bc644e3427ae1173e10a Mon Sep 17 00:00:00 2001 From: Mohamad Ayyash Date: Tue, 13 Jan 2015 19:20:44 -0800 Subject: [PATCH 103/475] xt_qtaguid: Use sk_callback_lock read locks before reading sk->sk_socket It prevents a kernel panic when accessing sk->sk_socket fields due to NULLing sk->sk_socket when sock_orphan is called through sk_common_release. Change-Id: I4aa46b4e2d8600e4d4ef8dcdd363aa4e6e5f8433 Signed-off-by: Mohamad Ayyash (cherry picked from commit cdea0ebcb8bcfe57688f6cb692b49e550ebd9796) Signed-off-by: John Stultz --- 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 9664bec1091c..e33be3aaf094 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1658,6 +1658,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) struct sock *sk; kuid_t sock_uid; bool res; + bool set_sk_callback_lock = false; if (unlikely(module_passive)) return (info->match ^ info->invert) == 0; @@ -1715,6 +1716,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) 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) { + set_sk_callback_lock = true; + read_lock_bh(&sk->sk_callback_lock); 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); @@ -1801,6 +1804,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) put_sock_ret_res: if (got_sock) xt_socket_put_sk(sk); + if (set_sk_callback_lock) + read_unlock_bh(&sk->sk_callback_lock); ret_res: MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); return res; From 62ade5c47df6e2e7871c080c125e2b4a629e8d7e Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Thu, 29 Jan 2015 01:16:23 +0530 Subject: [PATCH 104/475] xt_qtaguid: use sock_gen_put() instead of xt_socket_put_sk() Removing obsolete xt_socket_put_sk() and using sock_gen_put() instead. xt_socket_put_sk() was reintroduced for xt_qtaguid in one of the patches, but it turned out sock_gen_put() supersedes xt_socket_put_sk(). So we don't need xt_socket_put_sk() any more. This patch is based on commit 1a8bf6eeef9f (netfilter: xt_socket: use sock_gen_put()) Change-Id: I976d5f7f7eded0f3cc91b596acfeb35e4c2057e5 Signed-off-by: Amit Pundir (cherry picked from commit 551780fc28cb7480dbc4f585ef80ca02c2922ec1) Signed-off-by: John Stultz --- include/uapi/linux/netfilter/xt_socket.h | 1 - net/netfilter/xt_qtaguid.c | 4 ++-- net/netfilter/xt_socket.c | 10 ---------- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h index 8f4da12ca571..ac645e69716f 100644 --- a/include/uapi/linux/netfilter/xt_socket.h +++ b/include/uapi/linux/netfilter/xt_socket.h @@ -26,7 +26,6 @@ struct xt_socket_mtinfo3 { | XT_SOCKET_NOWILDCARD \ | XT_SOCKET_RESTORESKMARK) -void xt_socket_put_sk(struct sock *sk); struct sock *xt_socket_get4_sk(const struct sk_buff *skb, struct xt_action_param *par); struct sock *xt_socket_get6_sk(const struct sk_buff *skb, diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index e33be3aaf094..2f9784c1e692 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1605,7 +1605,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, * "struct inet_timewait_sock" which is missing fields. */ if (sk->sk_state == TCP_TIME_WAIT) { - xt_socket_put_sk(sk); + sock_gen_put(sk); sk = NULL; } } @@ -1803,7 +1803,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) put_sock_ret_res: if (got_sock) - xt_socket_put_sk(sk); + sock_gen_put(sk); if (set_sk_callback_lock) read_unlock_bh(&sk->sk_callback_lock); ret_res: diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index d9fe8fbd5fd8..c8ff76945ac8 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -35,16 +35,6 @@ #include #endif -void -xt_socket_put_sk(struct sock *sk) -{ - if (sk->sk_state == TCP_TIME_WAIT) - inet_twsk_put(inet_twsk(sk)); - else - sock_put(sk); -} -EXPORT_SYMBOL(xt_socket_put_sk); - static int extract_icmp4_fields(const struct sk_buff *skb, u8 *protocol, From 707c2f76831faa0fa95f570bb8366c0306f4476b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 3 Sep 2015 14:48:52 -0700 Subject: [PATCH 105/475] net: xt_qtaguid/xt_socket: fix refcount underflow and crash xt_socket_get[4|6]_sk() do not always increment sock refcount, which causes confusion in xt_qtaguid module which is not aware of this fact and drops the reference whether it should have or not. Fix it by changing xt_socket_get[4|6]_sk() to always increment recount of returned sock. This should fix the following crash: [ 111.319523] BUG: failure at /mnt/host/source/src/third_party/kernel/v3.18/net/ipv4/inet_timewait_sock.c:90/__inet_twsk_kill()! [ 111.331192] Kernel panic - not syncing: BUG! [ 111.335468] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G U W 3.18.0-06867-g268df91 #1 [ 111.343810] Hardware name: Google Tegra210 Smaug Rev 1+ (DT) [ 111.349463] Call trace: [ 111.351917] [] dump_backtrace+0x0/0x10c [ 111.357314] [] show_stack+0x10/0x1c [ 111.362367] [] dump_stack+0x74/0x94 [ 111.367414] [] panic+0xec/0x238 [ 111.372116] [] __inet_twsk_kill+0xd0/0xf8 [ 111.377684] [] inet_twdr_do_twkill_work+0x64/0xd0 [ 111.383946] [] inet_twdr_hangman+0x2c/0xa4 [ 111.389602] [] call_timer_fn+0xac/0x160 [ 111.394995] [] run_timer_softirq+0x23c/0x274 [ 111.400824] [] __do_softirq+0x1a4/0x330 [ 111.406218] [] irq_exit+0x70/0xd0 [ 111.411093] [] __handle_domain_irq+0x84/0xa8 [ 111.416922] [] gic_handle_irq+0x4c/0x80 b/22476945 Originally reviewed at: https://chromium-review.googlesource.com/#/c/297414/ Change-Id: I51fa94a9d92a84a0bd3b58466d711e46a6892a79 Signed-off-by: Dmitry Torokhov [jstultz: Cherry-picked and added missing local var definition] Signed-off-by: John Stultz --- net/netfilter/xt_socket.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index c8ff76945ac8..8a2a489b2cd3 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -148,6 +148,7 @@ struct sock *xt_socket_lookup_slow_v4(struct net *net, const struct net_device *indev) { const struct iphdr *iph = ip_hdr(skb); + struct sock *sk = skb->sk; __be32 uninitialized_var(daddr), uninitialized_var(saddr); __be16 uninitialized_var(dport), uninitialized_var(sport); u8 uninitialized_var(protocol); @@ -198,8 +199,14 @@ struct sock *xt_socket_lookup_slow_v4(struct net *net, } #endif - return xt_socket_get_sock_v4(net, protocol, saddr, daddr, - sport, dport, indev); + if (sk) + atomic_inc(&sk->sk_refcnt); + else + sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol, + saddr, daddr, sport, dport, + indev); + + return sk; } EXPORT_SYMBOL(xt_socket_lookup_slow_v4); @@ -233,8 +240,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, transparent) pskb->mark = sk->sk_mark; - if (sk != skb->sk) - sock_gen_put(sk); + sock_gen_put(sk); if (wildcard || !transparent) sk = NULL; @@ -341,6 +347,7 @@ struct sock *xt_socket_lookup_slow_v6(struct net *net, const struct sk_buff *skb, const struct net_device *indev) { + struct sock *sk = skb->sk; __be16 uninitialized_var(dport), uninitialized_var(sport); const struct in6_addr *daddr = NULL, *saddr = NULL; struct ipv6hdr *iph = ipv6_hdr(skb); @@ -374,8 +381,14 @@ struct sock *xt_socket_lookup_slow_v6(struct net *net, return NULL; } - return xt_socket_get_sock_v6(net, tproto, saddr, daddr, - sport, dport, indev); + if (sk) + atomic_inc(&sk->sk_refcnt); + else + sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto, + saddr, daddr, sport, dport, + indev); + + return sk; } EXPORT_SYMBOL(xt_socket_lookup_slow_v6); From 0e922fb48de57debfb0e3030f4391175833fb88d Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 7 Jul 2015 00:28:49 +0530 Subject: [PATCH 106/475] netfilter: xt_qtaguid: xt_socket: build fixes Add missing header and use xt_socket_lookup_slow_v* instead of xt_socket_get*_sk in xt_qtaguid.c. Fix xt_socket_lookup_slow_v* functions in xt_socket.c and declare them in xt_socket.h Change-Id: I55819b2d4ffa82a2be20995c87d28fb5cc77b5ba Signed-off-by: Amit Pundir Signed-off-by: John Stultz --- include/uapi/linux/netfilter/xt_socket.h | 8 ++++---- net/netfilter/xt_qtaguid.c | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h index ac645e69716f..15b4c1cf99bb 100644 --- a/include/uapi/linux/netfilter/xt_socket.h +++ b/include/uapi/linux/netfilter/xt_socket.h @@ -26,9 +26,9 @@ struct xt_socket_mtinfo3 { | XT_SOCKET_NOWILDCARD \ | XT_SOCKET_RESTORESKMARK) -struct sock *xt_socket_get4_sk(const struct sk_buff *skb, - struct xt_action_param *par); -struct sock *xt_socket_get6_sk(const struct sk_buff *skb, - struct xt_action_param *par); +struct sock *xt_socket_lookup_slow_v4(const struct sk_buff *skb, + const struct net_device *indev); +struct sock *xt_socket_lookup_slow_v6(const struct sk_buff *skb, + const struct net_device *indev); #endif /* _XT_SOCKET_H */ diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 2f9784c1e692..90b2c6aac7d8 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1588,10 +1589,10 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, switch (par->family) { case NFPROTO_IPV6: - sk = xt_socket_get6_sk(skb, par); + sk = xt_socket_lookup_slow_v6(skb, par->in); break; case NFPROTO_IPV4: - sk = xt_socket_get4_sk(skb, par); + sk = xt_socket_lookup_slow_v4(skb, par->in); break; default: return NULL; From af711d13fa59040b5f09b466c04c9299e27ba002 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 20 Nov 2015 14:45:40 +0530 Subject: [PATCH 107/475] netfilter: xt_qtaguid/socket: build fixes for 4.4 Update xt_socket_lookup_slow_v* usage in aosp patches, to align with changes from mainline commit 686c9b50809d "netfilter: x_tables: Use par->net instead of computing from the passed net devices". Signed-off-by: Amit Pundir --- include/uapi/linux/netfilter/xt_socket.h | 10 ++++++---- net/netfilter/xt_qtaguid.c | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h index 15b4c1cf99bb..7f00df6cd897 100644 --- a/include/uapi/linux/netfilter/xt_socket.h +++ b/include/uapi/linux/netfilter/xt_socket.h @@ -26,9 +26,11 @@ struct xt_socket_mtinfo3 { | XT_SOCKET_NOWILDCARD \ | XT_SOCKET_RESTORESKMARK) -struct sock *xt_socket_lookup_slow_v4(const struct sk_buff *skb, - const struct net_device *indev); -struct sock *xt_socket_lookup_slow_v6(const struct sk_buff *skb, - const struct net_device *indev); +struct sock *xt_socket_lookup_slow_v4(struct net *net, + const struct sk_buff *skb, + const struct net_device *indev); +struct sock *xt_socket_lookup_slow_v6(struct net *net, + const struct sk_buff *skb, + const struct net_device *indev); #endif /* _XT_SOCKET_H */ diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 90b2c6aac7d8..62ddd6cd1ee8 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1589,10 +1589,10 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, switch (par->family) { case NFPROTO_IPV6: - sk = xt_socket_lookup_slow_v6(skb, par->in); + sk = xt_socket_lookup_slow_v6(dev_net(skb->dev), skb, par->in); break; case NFPROTO_IPV4: - sk = xt_socket_lookup_slow_v4(skb, par->in); + sk = xt_socket_lookup_slow_v4(dev_net(skb->dev), skb, par->in); break; default: return NULL; From c500a829929382de25899ec279d243b90a90e0ca Mon Sep 17 00:00:00 2001 From: "liping.zhang" Date: Mon, 11 Jan 2016 13:31:01 +0800 Subject: [PATCH 108/475] xt_qtaguid: fix a race condition in if_tag_stat_update Miss a lock protection in if_tag_stat_update while doing get_iface_entry. So if one CPU is doing iface_stat_create while another CPU is doing if_tag_stat_update, race will happened. Change-Id: Ib8d98e542f4e385685499f5b7bb7354f08654a75 Signed-off-by: Liping Zhang --- net/netfilter/xt_qtaguid.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 62ddd6cd1ee8..04bb081adde8 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1291,11 +1291,12 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", ifname, uid, sk, direction, proto, bytes); - + spin_lock_bh(&iface_stat_list_lock); iface_entry = get_iface_entry(ifname); if (!iface_entry) { pr_err_ratelimited("qtaguid: iface_stat: stat_update() " "%s not found\n", ifname); + spin_unlock_bh(&iface_stat_list_lock); return; } /* It is ok to process data when an iface_entry is inactive */ @@ -1331,8 +1332,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, * {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; + goto unlock; } /* Loop over tag list under this interface for {0,uid_tag} */ @@ -1372,6 +1372,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, tag_stat_update(new_tag_stat, direction, proto, bytes); unlock: spin_unlock_bh(&iface_entry->tag_stat_list_lock); + spin_unlock_bh(&iface_stat_list_lock); } static int iface_netdev_event_handler(struct notifier_block *nb, From afedd7beba14385fd797166751fde39e0f52cf72 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Fri, 28 May 2010 14:32:19 -0700 Subject: [PATCH 109/475] 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 ce9585cf343a..043fe1dc0860 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -92,6 +92,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 a5d04098dfce..eeb9d5db454f 100644 --- a/net/Makefile +++ b/net/Makefile @@ -77,3 +77,4 @@ endif ifneq ($(CONFIG_NET_L3_MASTER_DEV),) obj-y += l3mdev/ endif +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 7c121720fa14889d59e933aad0a8b9ce948a39ae 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 110/475] 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 72fc8653dc090a65c330ffd7bf3e7dda1271ffe0 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 7 Feb 2014 18:40:10 -0800 Subject: [PATCH 111/475] tcp: add a sysctl to config the tcp_default_init_rwnd The default initial rwnd is hardcoded to 10. Now we allow it to be controlled via /proc/sys/net/ipv4/tcp_default_init_rwnd which limits the values from 3 to 100 This is somewhat needed because ipv6 routes are autoconfigured by the kernel. See "An Argument for Increasing TCP's Initial Congestion Window" in https://developers.google.com/speed/articles/tcp_initcwnd_paper.pdf Change-Id: I386b2a9d62de0ebe05c1ebe1b4bd91b314af5c54 Signed-off-by: JP Abgrall Conflicts: net/ipv4/sysctl_net_ipv4.c net/ipv4/tcp_input.c --- include/net/tcp.h | 1 + net/ipv4/sysctl_net_ipv4.c | 22 ++++++++++++++++++++++ net/ipv4/tcp_input.c | 1 + net/ipv4/tcp_output.c | 2 +- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 0020825157e2..b64bdeea00a9 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -284,6 +284,7 @@ extern int sysctl_tcp_autocorking; extern int sysctl_tcp_invalid_ratelimit; extern int sysctl_tcp_pacing_ss_ratio; extern int sysctl_tcp_pacing_ca_ratio; +extern int sysctl_tcp_default_init_rwnd; extern atomic_long_t tcp_memory_allocated; extern struct percpu_counter tcp_sockets_allocated; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index a0bd7a55193e..dd825be50089 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -152,6 +152,21 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write, return ret; } +/* Validate changes from /proc interface. */ +static int proc_tcp_default_init_rwnd(ctl_table *ctl, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + int old_value = *(int *)ctl->data; + int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + int new_value = *(int *)ctl->data; + + if (write && ret == 0 && (new_value < 3 || new_value > 100)) + *(int *)ctl->data = old_value; + + return ret; +} + static int proc_tcp_congestion_control(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -760,6 +775,13 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec_ms_jiffies, }, + { + .procname = "tcp_default_init_rwnd", + .data = &sysctl_tcp_default_init_rwnd, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_tcp_default_init_rwnd + }, { .procname = "icmp_msgs_per_sec", .data = &sysctl_icmp_msgs_per_sec, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d4c51158470f..da34f830f4bc 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -102,6 +102,7 @@ int sysctl_tcp_thin_dupack __read_mostly; int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; int sysctl_tcp_early_retrans __read_mostly = 3; int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2; +int sysctl_tcp_default_init_rwnd __read_mostly = TCP_INIT_CWND * 2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ #define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 9bfc39ff2285..9f069bd9de46 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -191,7 +191,7 @@ u32 tcp_default_init_rwnd(u32 mss) * (RFC 3517, Section 4, NextSeg() rule (2)). Further place a * limit when mss is larger than 1460. */ - u32 init_rwnd = TCP_INIT_CWND * 2; + u32 init_rwnd = sysctl_tcp_default_init_rwnd; if (mss > 1460) init_rwnd = max((1460 * init_rwnd) / mss, 2U); From d3bdc5a2122fffb635a1c7323a25fe3578d0cc23 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 26 Mar 2014 13:03:12 +0900 Subject: [PATCH 112/475] net: support marking accepting TCP sockets When using mark-based routing, sockets returned from accept() may need to be marked differently depending on the incoming connection request. This is the case, for example, if different socket marks identify different networks: a listening socket may want to accept connections from all networks, but each connection should be marked with the network that the request came in on, so that subsequent packets are sent on the correct network. This patch adds a sysctl to mark TCP sockets based on the fwmark of the incoming SYN packet. If enabled, and an unmarked socket receives a SYN, then the SYN packet's fwmark is written to the connection's inet_request_sock, and later written back to the accepted socket when the connection is established. If the socket already has a nonzero mark, then the behaviour is the same as it is today, i.e., the listening socket's fwmark is used. Black-box tested using user-mode linux: - IPv4/IPv6 SYN+ACK, FIN, etc. packets are routed based on the mark of the incoming SYN packet. - The socket returned by accept() is marked with the mark of the incoming SYN packet. - Tested with syncookies=1 and syncookies=2. Change-Id: I26bc1eceefd2c588d73b921865ab70e4645ade57 Signed-off-by: Lorenzo Colitti --- Documentation/networking/ip-sysctl.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 2ea4c45cf1c8..2042261408b9 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -584,6 +584,16 @@ tcp_fastopen - INTEGER See include/net/tcp.h and the code for more details. +tcp_fwmark_accept - BOOLEAN + If set, incoming connections to listening sockets that do not have a + socket mark will set the mark of the accepting socket to the fwmark of + the incoming SYN packet. This will cause all packets on that connection + (starting from the first SYNACK) to be sent with that fwmark. The + listening socket's mark is unchanged. Listening sockets that already + have a fwmark set via setsockopt(SOL_SOCKET, SO_MARK, ...) are + unaffected. + Default: 0 + tcp_syn_retries - INTEGER Number of times initial SYNs for an active TCP connection attempt will be retransmitted. Should not be higher than 255. Default value From dcc8ef44353408913246228dc5f9b933d19c353a Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 27 Oct 2015 18:09:40 -0700 Subject: [PATCH 113/475] tcp: fix tcp_default_init_rwnd() for 4.1 Change-Id: If3ecf5f59acf379ffcc468f28434830a92b0383d Signed-off-by: Dmitry Shmidt --- net/ipv4/sysctl_net_ipv4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index dd825be50089..b1784c897e6c 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -153,7 +153,7 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write, } /* Validate changes from /proc interface. */ -static int proc_tcp_default_init_rwnd(ctl_table *ctl, int write, +static int proc_tcp_default_init_rwnd(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { From 8bf4413b4f54e24120b90ecbfee426beeddc3ff0 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 28 Oct 2015 15:56:59 +0900 Subject: [PATCH 114/475] Don't kill IPv4 sockets when killing IPv6 sockets was requested. c7c3ec4903d32c60423ee013d96e94602f66042c cherry-picked the tcp_nuke_addr ioctl, but omitted a check that ensures that a socket is an IPv6 socket. This makes it so that if we issue a SIOCKILLADDR on ::, it kills IPv4 sockets as well. This is because every IPv4 socket has an IPv6 source address (sk_v6_rcv_saddr) of ::. Thus, when we iterate over an IPv4 socket, and compare the source address of the socket to the source address in the ioctl, it matches the :: that was passed in, and we kill the socket. Change-Id: I736431a898e6ec91536536d352936a210aa10100 --- net/ipv4/tcp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ced1683b2f3b..65dc38a429ae 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3285,6 +3285,8 @@ restart: #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) if (family == AF_INET6) { struct in6_addr *s6; + if (!inet->pinet6) + continue; s6 = &sk->sk_v6_rcv_saddr; if (ipv6_addr_type(s6) == IPV6_ADDR_MAPPED) From 04f86f74f217b48cf3c24bbfdf66dec38d4d1e23 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Wed, 15 Apr 2015 20:29:09 +0530 Subject: [PATCH 115/475] net: ipv6: fix virtual tunneling build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IPv6 PMTUD has been updated to support UID-based routing. Pass INVALID_UID as the socket UID to ip6_update_pmtu(), otherwise we run into following build error: ---------------- CC net/ipv6/ip6_vti.o net/ipv6/ip6_vti.c: In function ‘vti6_err’: net/ipv6/ip6_vti.c:559:3: error: too few arguments to function ‘ip6_update_pmtu’ In file included from include/net/ip_tunnels.h:19:0, from net/ipv6/ip6_vti.c:44: include/net/ip6_route.h:110:6: note: declared here make[2]: *** [net/ipv6/ip6_vti.o] Error 1 ---------------- Signed-off-by: Amit Pundir --- net/ipv6/ip6_vti.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 0a8610b33d79..c76ebc7fc52d 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -599,7 +599,7 @@ static int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) ip6_redirect(skb, net, skb->dev->ifindex, 0); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); xfrm_state_put(x); return 0; From 3823c8136f2170b3ac5e6a5f8b857746a786e845 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Wed, 26 Mar 2014 15:27:05 +0530 Subject: [PATCH 116/475] tcp: Fix IPV6 module build errors If CONFIG_IPV6=m is selected, we are getting following build errors. net/built-in.o: In function `tcp_is_local6': net/ipv4/tcp.c:3261: undefined reference to `rt6_lookup' Making the code conditional upon only CONFIG_IPV6=y fixes this issue. Also export tcp_nuke_addr to build IPv6 modules. Otherwise we run into following build error: CC [M] lib/zlib_deflate/deftree.o CC [M] lib/zlib_deflate/deflate_syms.o LD [M] lib/zlib_deflate/zlib_deflate.o Building modules, stage 2. MODPOST 46 modules ERROR: "tcp_nuke_addr" [net/ipv6/ipv6.ko] undefined! make[2]: *** [__modpost] Error 1 Signed-off-by: Tushar Behera CC: John Stultz Signed-off-by: John Stultz Signed-off-by: Amit Pundir --- net/ipv4/tcp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 65dc38a429ae..e8c126a52551 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3214,7 +3214,7 @@ static int tcp_is_local(struct net *net, __be32 addr) { return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK); } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if defined(CONFIG_IPV6) static int tcp_is_local6(struct net *net, struct in6_addr *addr) { struct rt6_info *rt6 = rt6_lookup(net, addr, addr, 0, 0); return rt6 && rt6->dst.dev && (rt6->dst.dev->flags & IFF_LOOPBACK); @@ -3282,7 +3282,7 @@ restart: continue; } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#if defined(CONFIG_IPV6) if (family == AF_INET6) { struct in6_addr *s6; if (!inet->pinet6) @@ -3319,3 +3319,4 @@ restart: return 0; } +EXPORT_SYMBOL_GPL(tcp_nuke_addr); From c805cbb1cc0c8d579c0881b6024fe303820bab2b Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 4 Dec 2008 17:37:05 -0800 Subject: [PATCH 117/475] 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, 8 insertions(+), 1 deletion(-) diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 598d374f6a35..44c3be9d1021 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 f53bf3b6558b..9f843bbe8c10 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -802,7 +802,7 @@ void rfkill_resume_polling(struct rfkill *rfkill) } EXPORT_SYMBOL(rfkill_resume_polling); -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_RFKILL_PM static int rfkill_suspend(struct device *dev) { struct rfkill *rfkill = to_rfkill(dev); @@ -838,7 +838,9 @@ static struct class rfkill_class = { .dev_release = rfkill_release, .dev_groups = rfkill_dev_groups, .dev_uevent = rfkill_dev_uevent, +#ifdef CONFIG_RFKILL_PM .pm = RFKILL_PM_OPS, +#endif }; bool rfkill_blocked(struct rfkill *rfkill) From bd3cc5f111c21bdc57f4a8d0ff1b9f1499294902 Mon Sep 17 00:00:00 2001 From: Ashish Sharma Date: Fri, 7 Oct 2011 17:54:16 -0700 Subject: [PATCH 118/475] 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 5e88d3e17546..689b8412c58e 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -46,16 +46,17 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } - u64_stats_update_begin(&brstats->syncp); - brstats->tx_packets++; - brstats->tx_bytes += skb->len; - u64_stats_update_end(&brstats->syncp); - BR_INPUT_SKB_CB(skb)->brdev = 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 (!br_allowed_ingress(br, br_vlan_group_rcu(br), skb, &vid)) goto out; From f0902928c24be3e2e255ebac9a9e3e88580d8564 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 24 Jun 2014 09:36:50 -0700 Subject: [PATCH 119/475] net: wireless: Decrease scan entry expiration to avoid stall results Change-Id: I0e23ce45d78d7c17633670973f49943a5ed6032d 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 14d5369eb778..30f967665e84 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -56,7 +56,7 @@ * also linked into the probe response struct. */ -#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) +#define IEEE80211_SCAN_RESULT_EXPIRE (7 * HZ) static void bss_free(struct cfg80211_internal_bss *bss) { From 29dbdf021b673eae60a6e44163f17944e1de34e7 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 18 Mar 2010 16:04:18 -0700 Subject: [PATCH 120/475] wlan: Create generic wlan platform data header Change-Id: I233559218cc40acb28b57315ea25c08a9c866725 Signed-off-by: Dmitry Shmidt --- include/linux/wlan_plat.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 include/linux/wlan_plat.h 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 ded18b9ba5014098310237524801fbcf5044fd71 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 3 Jun 2010 10:55:33 -0700 Subject: [PATCH 121/475] 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 83e28b7f214760605506687014c3e309b8601cbf Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 14 Feb 2011 16:58:48 -0800 Subject: [PATCH 122/475] 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 6a26c26e75853a610d07cbe9e75deebb5a472791 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 22 Aug 2014 14:40:18 -0700 Subject: [PATCH 123/475] Add flags parameter to get_country_code template Change-Id: Ic3f173db144a301ea104f544fc8ec723241c1d59 Signed-off-by: Dmitry Shmidt --- include/linux/wlan_plat.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/wlan_plat.h b/include/linux/wlan_plat.h index 40ec3482d1ef..8ad2dbd0c296 100644 --- a/include/linux/wlan_plat.h +++ b/include/linux/wlan_plat.h @@ -15,13 +15,15 @@ #ifndef _LINUX_WLAN_PLAT_H_ #define _LINUX_WLAN_PLAT_H_ +#define WLAN_PLAT_NODFS_FLAG 0x01 + 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); int (*get_mac_addr)(unsigned char *buf); - void *(*get_country_code)(char *ccode); + void *(*get_country_code)(char *ccode, u32 flags); }; #endif From d0bc6b1af5a5a9c4397064967e4e9c2bc6552c6d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 12 Jan 2015 13:42:05 -0800 Subject: [PATCH 124/475] wlan: Add get_wake_irq functionality Change-Id: Ic41f06c509b2e625dc9ec4131903db6920c5fd4e 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 8ad2dbd0c296..8e8b06f1ba4a 100644 --- a/include/linux/wlan_plat.h +++ b/include/linux/wlan_plat.h @@ -23,6 +23,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); + int (*get_wake_irq)(void); void *(*get_country_code)(char *ccode, u32 flags); }; From 4f7e9b2f2ebba52373c74b7f4a102dc2c0f79983 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 1 Jul 2011 17:19:56 -0700 Subject: [PATCH 125/475] 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 | 2 +- drivers/usb/phy/otg-wakelock.c | 190 +++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/phy/otg-wakelock.c diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 22e8ecb6bfbd..d874bbaa4ac4 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -6,6 +6,14 @@ menu "USB Physical Layer drivers" config USB_PHY def_bool n +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 19c0dccbb116..d5f7f0843352 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -3,7 +3,7 @@ # obj-$(CONFIG_USB_PHY) += phy.o obj-$(CONFIG_OF) += of.o - +obj-$(CONFIG_USB_OTG_WAKELOCK) += otg-wakelock.o # transceiver drivers, keep the list sorted obj-$(CONFIG_AB8500_USB) += phy-ab8500-usb.o 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 ce9cc0abe054a9a21778485d9d950361696a26af Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 26 Sep 2011 20:35:30 -0700 Subject: [PATCH 126/475] 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 6d90f8a4b9fc3baa827f51f8557614b6a81e4231 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 1 Feb 2012 14:23:15 -0800 Subject: [PATCH 127/475] 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 d79720cd89247a52347dad203d718ad28b8e7a6c Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 19 Dec 2011 14:37:50 -0800 Subject: [PATCH 128/475] 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. Change-Id: I2d8f2c37029fb37d8deb791d04eb7346f94f5adb Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_mtp.c | 1283 ++++++++++++++++++++++++++++++++++++ include/linux/usb/f_mtp.h | 75 +++ 2 files changed, 1358 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/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 75636fb2197792d1d03dfcc52e04a4d5d6d0b8aa Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 19 Dec 2011 14:38:41 -0800 Subject: [PATCH 129/475] 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). Change-Id: Ib11672fa3439dcb3a6588774b132b5a85e03e8ba Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_adb.c | 611 +++++++++++++++++++++++++++++++++++++ 1 file changed, 611 insertions(+) create mode 100644 drivers/usb/gadget/f_adb.c 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 272014a78e113afcdda4aa0b2dd6d386c7523df7 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 19 Dec 2011 14:39:37 -0800 Subject: [PATCH 130/475] 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. Change-Id: I67964b50d278f3c0471d47efbb7b0973a3502681 Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 796 +++++++++++++++++++++++++++++++ include/linux/usb/f_accessory.h | 83 ++++ 2 files changed, 879 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/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 fe7aa575bb73bef711cfa6779c6c631c8ee795b5 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 5 Mar 2012 13:29:45 -0800 Subject: [PATCH 131/475] 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 3daf0db436a9d96f5d3ac43f5e6d315178e3184a Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 8 Mar 2012 17:57:51 -0800 Subject: [PATCH 132/475] 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 b3479c8467733c27fed142bcd43f50da4357188f Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 19 Mar 2012 18:56:52 -0700 Subject: [PATCH 133/475] 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/f_adb.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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 d428b2ae73ecdddf8566b3dbd9a602c8fe9dc0ec Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Thu, 10 May 2012 16:41:40 -0700 Subject: [PATCH 134/475] 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 a1e00af15eba5b6770c37e1ef4c63a232e60632d Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Fri, 22 Jun 2012 18:17:10 -0700 Subject: [PATCH 135/475] 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 d7fd5f6ca93f92a0acf22c8c4224dcfd793b93f1 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 11 May 2012 09:00:40 -0700 Subject: [PATCH 136/475] 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 b723fc53ecf06a805fd48e1b82d9fceec49e2975 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 26 Mar 2012 11:03:55 -0700 Subject: [PATCH 137/475] USB: gadget: f_accessory: Add support for HID input devices Change-Id: I4f1452db32508382df52acdc47c0eb395ae328c7 Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 391 ++++++++++++++++++++++++++++++- include/linux/usb/f_accessory.h | 50 +++- 2 files changed, 432 insertions(+), 9 deletions(-) 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 db3262ee89a58d0460ce84a0f231721207cef5a4 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 11 May 2012 09:01:08 -0700 Subject: [PATCH 138/475] 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/f_audio_source.c | 828 ++++++++++++++++++++++++++++ 1 file changed, 828 insertions(+) create mode 100644 drivers/usb/gadget/f_audio_source.c 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 bc894a651ce87549e35ad3b9aec5243aacf2c1e4 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 28 Nov 2012 13:03:40 -0800 Subject: [PATCH 139/475] 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 f0fd81de048f2aa20982fca0784c232c42e6b2a1 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 140/475] 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 0592b02412b0a4e433c72816fb6d12939b9567bf 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 141/475] 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 Change-Id: I332a6802dbd49b4018b9318b8621d26ed94c955d Signed-off-by: Arve Hjønnevåg --- 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 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) 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 8b5fda4105d9b90f9080a711015613f366b01fee Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 5 Nov 2012 18:47:08 -0800 Subject: [PATCH 142/475] usb: gadget: Fix android gadget driver build Removed obsolete f_adb function Change-Id: Idfb4110429bc0ea63f493c68ad667f49ca471987 Signed-off-by: Benoit Goby --- drivers/usb/gadget/f_adb.c | 619 ------------------------------------- 1 file changed, 619 deletions(-) delete mode 100644 drivers/usb/gadget/f_adb.c 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; -} From 04efb4284190248110c09d38a141a98629b3c4a8 Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Thu, 12 Sep 2013 01:42:18 +0000 Subject: [PATCH 143/475] 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 473f306e19ceab6cfb6ea83bd81aa7a4c800b78a Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 7 Nov 2013 13:08:15 -0800 Subject: [PATCH 144/475] usb: gadget: f_mtp: move userspace interface to uapi Move the most of linux/usb/f_mtp.h header to uapi. Move the only remaining structure definition into f_mtp.c, the only place that uses it. Change-Id: I952c1a9dc15c36bf295a0eb4d74b6b1ad912ed03 Signed-off-by: Colin Cross --- drivers/usb/gadget/f_mtp.c | 11 ++++++ include/linux/usb/f_mtp.h | 54 +----------------------------- include/uapi/linux/usb/f_mtp.h | 61 ++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 53 deletions(-) create mode 100644 include/uapi/linux/usb/f_mtp.h diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 12fb818ab147..960d64fbd40b 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -269,6 +269,17 @@ struct mtp_device_status { __le16 wCode; }; +struct mtp_data_header { + /* length of packet, including this header */ + __le32 length; + /* container type (2 for data packet) */ + __le16 type; + /* MTP command code */ + __le16 command; + /* MTP transaction ID */ + __le32 transaction_id; +}; + /* temporary variable used between mtp_open() and mtp_gadget_bind() */ static struct mtp_dev *_mtp_dev; diff --git a/include/linux/usb/f_mtp.h b/include/linux/usb/f_mtp.h index 72a432e2fcdd..4e8417791bea 100644 --- a/include/linux/usb/f_mtp.h +++ b/include/linux/usb/f_mtp.h @@ -18,58 +18,6 @@ #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) +#include #endif /* __LINUX_USB_F_MTP_H */ diff --git a/include/uapi/linux/usb/f_mtp.h b/include/uapi/linux/usb/f_mtp.h new file mode 100644 index 000000000000..503291855abd --- /dev/null +++ b/include/uapi/linux/usb/f_mtp.h @@ -0,0 +1,61 @@ +/* + * 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 _UAPI_LINUX_USB_F_MTP_H +#define _UAPI_LINUX_USB_F_MTP_H + +#include +#include + +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 /* _UAPI_LINUX_USB_F_MTP_H */ From 13781993745d705231257f257291685ccf83cc75 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 7 Nov 2013 13:08:39 -0800 Subject: [PATCH 145/475] usb: gadget: f_accessory: move userspace interface to uapi Move the entire contents of linux/usb/f_accessory.h header to uapi, it only contains a userspace interface. Change-Id: Ieb5547da449588ae554988a201c0e6b4e3afc531 Signed-off-by: Colin Cross --- include/linux/usb/f_accessory.h | 125 +---------------------- include/uapi/linux/usb/f_accessory.h | 146 +++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 124 deletions(-) create mode 100644 include/uapi/linux/usb/f_accessory.h diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index 61ebe0aabc5b..ebe3c4d59309 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -18,129 +18,6 @@ #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 - * - * 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 HID and device to host audio support - */ -#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 - -/* 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 - * 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]) -#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) -/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ -#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) +#include #endif /* __LINUX_USB_F_ACCESSORY_H */ diff --git a/include/uapi/linux/usb/f_accessory.h b/include/uapi/linux/usb/f_accessory.h new file mode 100644 index 000000000000..0baeb7d0d74c --- /dev/null +++ b/include/uapi/linux/usb/f_accessory.h @@ -0,0 +1,146 @@ +/* + * 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 _UAPI_LINUX_USB_F_ACCESSORY_H +#define _UAPI_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 + * + * 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 HID and device to host audio support + */ +#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 + +/* 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 + * 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]) +#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) +/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ +#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) + +#endif /* _UAPI_LINUX_USB_F_ACCESSORY_H */ From 43d61e6ff255f4e8aa7aed876852838374575c51 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 24 Feb 2014 10:19:13 -0800 Subject: [PATCH 146/475] drivers: usb: gadget: 64-bit related type fixes Change-Id: I2f9b12e1e0cdfe64ffe20db78d319a6221821184 Signed-off-by: Greg Hackmann --- drivers/usb/gadget/f_accessory.c | 16 +++++++++------- drivers/usb/gadget/f_mtp.c | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 092964c2b506..73c6b2072d73 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -552,10 +552,11 @@ static ssize_t acc_read(struct file *fp, char __user *buf, { struct acc_dev *dev = fp->private_data; struct usb_request *req; - int r = count, xfer; + ssize_t r = count; + unsigned xfer; int ret = 0; - pr_debug("acc_read(%d)\n", count); + pr_debug("acc_read(%zu)\n", count); if (dev->disconnected) return -ENODEV; @@ -596,7 +597,7 @@ requeue_req: if (req->actual == 0) goto requeue_req; - pr_debug("rx %p %d\n", req, req->actual); + pr_debug("rx %p %u\n", req, req->actual); xfer = (req->actual < count) ? req->actual : count; r = xfer; if (copy_to_user(buf, req->buf, xfer)) @@ -605,7 +606,7 @@ requeue_req: r = -EIO; done: - pr_debug("acc_read returning %d\n", r); + pr_debug("acc_read returning %zd\n", r); return r; } @@ -614,10 +615,11 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, { struct acc_dev *dev = fp->private_data; struct usb_request *req = 0; - int r = count, xfer; + ssize_t r = count; + unsigned xfer; int ret; - pr_debug("acc_write(%d)\n", count); + pr_debug("acc_write(%zu)\n", count); if (!dev->online || dev->disconnected) return -ENODEV; @@ -665,7 +667,7 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, if (req) req_put(dev, &dev->tx_idle, req); - pr_debug("acc_write returning %d\n", r); + pr_debug("acc_write returning %zd\n", r); return r; } diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 960d64fbd40b..620aeaaf2d72 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -466,10 +466,11 @@ static ssize_t mtp_read(struct file *fp, char __user *buf, struct mtp_dev *dev = fp->private_data; struct usb_composite_dev *cdev = dev->cdev; struct usb_request *req; - int r = count, xfer; + ssize_t r = count; + unsigned xfer; int ret = 0; - DBG(cdev, "mtp_read(%d)\n", count); + DBG(cdev, "mtp_read(%zu)\n", count); if (count > MTP_BULK_BUFFER_SIZE) return -EINVAL; @@ -533,7 +534,7 @@ done: dev->state = STATE_READY; spin_unlock_irq(&dev->lock); - DBG(cdev, "mtp_read returning %d\n", r); + DBG(cdev, "mtp_read returning %zd\n", r); return r; } @@ -543,11 +544,12 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf, struct mtp_dev *dev = fp->private_data; struct usb_composite_dev *cdev = dev->cdev; struct usb_request *req = 0; - int r = count, xfer; + ssize_t r = count; + unsigned xfer; int sendZLP = 0; int ret; - DBG(cdev, "mtp_write(%d)\n", count); + DBG(cdev, "mtp_write(%zu)\n", count); spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) { @@ -624,7 +626,7 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf, dev->state = STATE_READY; spin_unlock_irq(&dev->lock); - DBG(cdev, "mtp_write returning %d\n", r); + DBG(cdev, "mtp_write returning %zd\n", r); return r; } @@ -823,7 +825,7 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) int ret; int length = event->length; - DBG(dev->cdev, "mtp_send_event(%d)\n", event->length); + DBG(dev->cdev, "mtp_send_event(%zu)\n", event->length); if (length < 0 || length > INTR_BUFFER_SIZE) return -EINVAL; From b6d704d67426e8a1f90ea214097dbeb7bcb8ebed Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Mon, 23 Jun 2014 19:07:44 +0800 Subject: [PATCH 147/475] usb: gadget: f_accessory: Enabled Zero Length Packet (ZLP) for acc_write Accessory connected to Android Device requires Zero Length Packet (ZLP) to be written when data transferred out from the Android device are multiples of wMaxPacketSize (64bytes (Full-Speed) / 512bytes (High-Speed)) to end the transfer. Change-Id: Ib2c2c0ab98ef9afa10e74a720142deca5c0ed476 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_accessory.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 73c6b2072d73..f8490c034107 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -640,10 +640,17 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, break; } - if (count > BULK_BUFFER_SIZE) + if (count > BULK_BUFFER_SIZE) { xfer = BULK_BUFFER_SIZE; - else + /* ZLP, They will be more TX requests so not yet. */ + req->zero = 0; + } else { xfer = count; + /* If the data length is a multple of the + * maxpacket size then send a zero length packet(ZLP). + */ + req->zero = ((xfer % dev->ep_in->maxpacket) == 0); + } if (copy_from_user(req->buf, buf, xfer)) { r = -EFAULT; break; From fbc1d7837f2524f5d0347c4a120594d1485fde48 Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Tue, 1 Jul 2014 18:17:20 +0800 Subject: [PATCH 148/475] usb: gadget: f_audio_source: change max ISO packet size Re-applying from https://gitorious.org/shr/linux/commit/eb4c9d2db894c3492c0a848581bd4f6790f93d5f Most USB-AUDIO devices are limited to 256 byte for max iso buffer size. If a IN_EP_MAX_PACKET_SIZE is bigger than a USB-AUDIO device's max iso buffer size, it will cause noise. This patch will prevent this case as possibe by reducing packet size. When using 44.1khz, 2ch, 16bit audio data, if max packet size is bigger than 176 bytes, it's no problem. Credits to: Iliyan Malchev Change-Id: Ic2a1c19ea65d5fb42bf12926b51b255b465d7215 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_audio_source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 56dcf217cfe5..65760c42d422 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -24,7 +24,7 @@ #define SAMPLE_RATE 44100 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) -#define IN_EP_MAX_PACKET_SIZE 384 +#define IN_EP_MAX_PACKET_SIZE 256 /* Number of requests to allocate */ #define IN_EP_REQ_COUNT 4 From 62ed32043ae487f289d51d1bd45a50f59036c506 Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Mon, 23 Jun 2014 19:14:01 +0800 Subject: [PATCH 149/475] usb: gadget: f_audio_source: Fixed USB Audio Class Interface Descriptor Fixed Android Issue #56549. When both Vendor Class and Audio Class are activated for AOA 2.0, the baInterfaceNr of the AudioControl Interface Descriptor points to wrong interface numbers. They should be pointing to Audio Control Device and Audio Streaming interfaces. Replaced baInterfaceNr with the correct value. Change-Id: Iaa083f3d97c1f0fc9481bf87852b2b51278a6351 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_audio_source.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 65760c42d422..21ced13c83d8 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -580,12 +580,18 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) goto fail; ac_interface_desc.bInterfaceNumber = status; + /* AUDIO_AC_INTERFACE */ + ac_header_desc.baInterfaceNr[0] = 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; + /* AUDIO_AS_INTERFACE */ + ac_header_desc.baInterfaceNr[1] = status; + status = -ENODEV; /* allocate our endpoint */ From c996cdd384f0afeb5cc0c110913391582d1d8fc0 Mon Sep 17 00:00:00 2001 From: xerox_lin Date: Mon, 18 Aug 2014 21:54:23 +0800 Subject: [PATCH 150/475] USB: rndis: Free the rndis response queue during REMOTE_NDIS_RESET_MSG When rndis data transfer is in progress, some Windows7 Host PC is not sending the GET_ENCAPSULATED_RESPONSE command for receiving the response for the previous SEND_ENCAPSULATED_COMMAND processed. The rndis function driver appends each response for the SEND_ENCAPSULATED_COMMAND in a queue. As the above process got corrupted, the Host sends a REMOTE_NDIS_RESET_MSG command to do a soft-reset. As the rndis response queue is not freed, the previous response is sent as a part of this REMOTE_NDIS_RESET_MSG's reset response and the Host blocks any more Rndis transfers. Hence free the rndis response queue as a part of this soft-reset so that the current response for REMOTE_NDIS_RESET_MSG is sent properly during the response command. Change-Id: I8eff3849db452fe01b7d1fe4140ef1f1ad3f4fd4 Signed-off-by: Rajkumar Raghupathy Signed-off-by: Xerox Lin --- drivers/usb/gadget/function/rndis.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 70d3917cc003..27163e816be4 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -681,6 +681,13 @@ static int rndis_reset_response(struct rndis_params *params, rndis_reset_cmplt_type *resp; rndis_resp_t *r; + u32 length; + u8 *xbuf; + + /* drain the response queue */ + while ((xbuf = rndis_get_next_response(configNr, &length))) + rndis_free_response(configNr, xbuf); + r = rndis_add_response(params, sizeof(rndis_reset_cmplt_type)); if (!r) return -ENOMEM; From 70d6557c5913961b590f3807302664ce36b36c6f Mon Sep 17 00:00:00 2001 From: xerox_lin Date: Thu, 14 Aug 2014 14:48:44 +0800 Subject: [PATCH 151/475] usb: Add support for rndis uplink aggregation RNDIS protocol supports data aggregation on uplink and can help reduce mips by reducing number of interrupts on device. Throughput also improved by 20-30%. Aggregation is disabled by setting aggregation packet size to 1. To help better UL throughput, set as ul aggregation support to 3 rndis packets by default. It can be configured via module parameter: rndis_ul_max_pkt_per_xfer. Change-Id: I0b62a21a5c3ceb6b04933d0d6da33301dbafe493 Signed-off-by: Vamsi Krishna Signed-off-by: Xerox Lin --- drivers/usb/gadget/function/f_rndis.c | 12 +++ drivers/usb/gadget/function/rndis.c | 105 +++++++++++++++++++++----- drivers/usb/gadget/function/rndis.h | 2 + drivers/usb/gadget/function/u_ether.c | 6 ++ drivers/usb/gadget/function/u_ether.h | 4 + 5 files changed, 111 insertions(+), 18 deletions(-) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index e587767e374c..479fa2938412 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -70,6 +70,16 @@ * - MS-Windows drivers sometimes emit undocumented requests. */ +static bool rndis_multipacket_dl_disable; +module_param(rndis_multipacket_dl_disable, bool, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(rndis_multipacket_dl_disable, + "Disable RNDIS Multi-packet support in DownLink"); + +static unsigned int rndis_ul_max_pkt_per_xfer = 3; +module_param(rndis_ul_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer, + "Maximum packets per transfer for UL aggregation"); + struct f_rndis { struct gether port; u8 ctrl_id, data_id; @@ -792,6 +802,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); rndis_set_host_mac(rndis->params, rndis->ethaddr); + rndis_set_max_pkt_xfer(rndis->config, rndis_ul_max_pkt_per_xfer); if (rndis->manufacturer && rndis->vendorID && rndis_set_param_vendor(rndis->params, rndis->vendorID, @@ -978,6 +989,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) rndis->port.header_len = sizeof(struct rndis_packet_msg_type); rndis->port.wrap = rndis_add_header; rndis->port.unwrap = rndis_rm_hdr; + rndis->port.ul_max_pkts_per_xfer = rndis_ul_max_pkt_per_xfer; rndis->port.func.name = "rndis"; /* descriptors are per-instance copies */ diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 27163e816be4..c3ecff8aff8a 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -42,6 +42,16 @@ #include "rndis.h" +int rndis_ul_max_pkt_per_xfer_rcvd; +module_param(rndis_ul_max_pkt_per_xfer_rcvd, int, S_IRUGO); +MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer_rcvd, + "Max num of REMOTE_NDIS_PACKET_MSGs received in a single transfer"); + +int rndis_ul_max_xfer_size_rcvd; +module_param(rndis_ul_max_xfer_size_rcvd, int, S_IRUGO); +MODULE_PARM_DESC(rndis_ul_max_xfer_size_rcvd, + "Max size of bus transfer received"); + /* The driver for your USB chip needs to support ep0 OUT to work with * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional). @@ -579,12 +589,12 @@ static int rndis_init_response(struct rndis_params *params, resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION); resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS); resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3); - resp->MaxPacketsPerTransfer = cpu_to_le32(1); - resp->MaxTransferSize = cpu_to_le32( - params->dev->mtu + resp->MaxPacketsPerTransfer = cpu_to_le32(params->max_pkt_per_xfer); + resp->MaxTransferSize = cpu_to_le32(params->max_pkt_per_xfer * + (params->dev->mtu + sizeof(struct ethhdr) + sizeof(struct rndis_packet_msg_type) - + 22); + + 22)); resp->PacketAlignmentFactor = cpu_to_le32(0); resp->AFListOffset = cpu_to_le32(0); resp->AFListSize = cpu_to_le32(0); @@ -964,6 +974,8 @@ int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev, params->dev = dev; params->filter = cdc_filter; + rndis_ul_max_xfer_size_rcvd = 0; + rndis_ul_max_pkt_per_xfer_rcvd = 0; return 0; } EXPORT_SYMBOL_GPL(rndis_set_param_dev); @@ -996,6 +1008,13 @@ int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed) } EXPORT_SYMBOL_GPL(rndis_set_param_medium); +void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer) +{ + pr_debug("%s:\n", __func__); + + rndis_per_dev_params[configNr].max_pkt_per_xfer = max_pkt_per_xfer; +} + void rndis_add_hdr(struct sk_buff *skb) { struct rndis_packet_msg_type *header; @@ -1068,23 +1087,73 @@ int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, struct sk_buff_head *list) { - /* tmp points to a struct rndis_packet_msg_type */ - __le32 *tmp = (void *)skb->data; + int num_pkts = 1; - /* MessageType, MessageLength */ - if (cpu_to_le32(RNDIS_MSG_PACKET) - != get_unaligned(tmp++)) { - dev_kfree_skb_any(skb); - return -EINVAL; - } - tmp++; + if (skb->len > rndis_ul_max_xfer_size_rcvd) + rndis_ul_max_xfer_size_rcvd = skb->len; - /* DataOffset, DataLength */ - if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) { - dev_kfree_skb_any(skb); - return -EOVERFLOW; + while (skb->len) { + struct rndis_packet_msg_type *hdr; + struct sk_buff *skb2; + u32 msg_len, data_offset, data_len; + + /* some rndis hosts send extra byte to avoid zlp, ignore it */ + if (skb->len == 1) { + dev_kfree_skb_any(skb); + return 0; + } + + if (skb->len < sizeof *hdr) { + pr_err("invalid rndis pkt: skblen:%u hdr_len:%u", + skb->len, sizeof *hdr); + dev_kfree_skb_any(skb); + return -EINVAL; + } + + hdr = (void *)skb->data; + msg_len = le32_to_cpu(hdr->MessageLength); + data_offset = le32_to_cpu(hdr->DataOffset); + data_len = le32_to_cpu(hdr->DataLength); + + if (skb->len < msg_len || + ((data_offset + data_len + 8) > msg_len)) { + pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n", + le32_to_cpu(hdr->MessageType), + msg_len, data_offset, data_len, skb->len); + dev_kfree_skb_any(skb); + return -EOVERFLOW; + } + if (le32_to_cpu(hdr->MessageType) != RNDIS_MSG_PACKET) { + pr_err("invalid rndis message: %d/%d/%d/%d, len:%d\n", + le32_to_cpu(hdr->MessageType), + msg_len, data_offset, data_len, skb->len); + dev_kfree_skb_any(skb); + return -EINVAL; + } + + skb_pull(skb, data_offset + 8); + + if (msg_len == skb->len) { + skb_trim(skb, data_len); + break; + } + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (!skb2) { + pr_err("%s:skb clone failed\n", __func__); + dev_kfree_skb_any(skb); + return -ENOMEM; + } + + skb_pull(skb, msg_len - sizeof *hdr); + skb_trim(skb2, data_len); + skb_queue_tail(list, skb2); + + num_pkts++; } - skb_trim(skb, get_unaligned_le32(tmp++)); + + if (num_pkts > rndis_ul_max_pkt_per_xfer_rcvd) + rndis_ul_max_pkt_per_xfer_rcvd = num_pkts; skb_queue_tail(list, skb); return 0; diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index ef92eb66d8ad..2b92b745d292 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -190,6 +190,7 @@ typedef struct rndis_params struct net_device *dev; u32 vendorID; + u8 max_pkt_per_xfer; const char *vendorDescr; void (*resp_avail)(void *v); void *v; @@ -206,6 +207,7 @@ int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, const char *vendorDescr); int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed); +void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer); void rndis_add_hdr(struct sk_buff *skb); int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, struct sk_buff_head *list); diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 6554322af2c1..0d2860a09f40 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -71,6 +71,7 @@ struct eth_dev { unsigned qmult; unsigned header_len; + unsigned ul_max_pkts_per_xfer; struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb); int (*unwrap)(struct gether *, struct sk_buff *skb, @@ -230,9 +231,13 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) size += out->maxpacket - 1; size -= size % out->maxpacket; + if (dev->ul_max_pkts_per_xfer) + size *= dev->ul_max_pkts_per_xfer; + if (dev->port_usb->is_fixed) size = max_t(size_t, size, dev->port_usb->fixed_out_len); + DBG(dev, "%s: size: %d\n", __func__, size); skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags); if (skb == NULL) { DBG(dev, "no rx skb\n"); @@ -1077,6 +1082,7 @@ struct net_device *gether_connect(struct gether *link) dev->header_len = link->header_len; dev->unwrap = link->unwrap; dev->wrap = link->wrap; + dev->ul_max_pkts_per_xfer = link->ul_max_pkts_per_xfer; spin_lock(&dev->lock); dev->port_usb = link; diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h index c77145bd6b5b..7914b4b1ce6c 100644 --- a/drivers/usb/gadget/function/u_ether.h +++ b/drivers/usb/gadget/function/u_ether.h @@ -73,6 +73,10 @@ struct gether { bool is_fixed; u32 fixed_out_len; u32 fixed_in_len; + unsigned ul_max_pkts_per_xfer; +/* Max number of SKB packets to be used to create Multi Packet RNDIS */ +#define TX_SKB_HOLD_THRESHOLD 3 + bool multi_pkt_xfer; bool supports_multi_frame; struct sk_buff *(*wrap)(struct gether *port, struct sk_buff *skb); From 398a708ed5f3ef771d96dfb9b95b5d5170d17eb7 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 24 Sep 2014 18:58:23 -0700 Subject: [PATCH 152/475] usb: u_ether: Add workqueue as bottom half handler for rx data path u_ether driver passes rx data to network layer and resubmits the request back to usb hardware in interrupt context. Network layer processes rx data by scheduling tasklet. For high throughput scenarios on rx data path driver is spending lot of time in interrupt context due to rx data processing by tasklet and continuous completion and re-submission of the usb requests which results in watchdog bark. Hence move the rx data processing and usb request submission to a workqueue bottom half handler. Change-Id: I316de8e267997137ac189a8b7b2846fa325f4a5a Signed-off-by: Badhri Jagan Sridharan --- drivers/usb/gadget/function/u_ether.c | 119 +++++++++++++++++--------- 1 file changed, 80 insertions(+), 39 deletions(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 0d2860a09f40..4119dbb73602 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -53,6 +53,8 @@ * blocks and still have efficient handling. */ #define GETHER_MAX_ETH_FRAME_LEN 15412 +static struct workqueue_struct *uether_wq; + struct eth_dev { /* lock is held while accessing port_usb */ @@ -78,6 +80,7 @@ struct eth_dev { struct sk_buff_head *list); struct work_struct work; + struct work_struct rx_work; unsigned long todo; #define WORK_RX_MEMORY 0 @@ -263,18 +266,16 @@ enomem: DBG(dev, "rx submit --> %d\n", retval); if (skb) dev_kfree_skb_any(skb); - spin_lock_irqsave(&dev->req_lock, flags); - list_add(&req->list, &dev->rx_reqs); - spin_unlock_irqrestore(&dev->req_lock, flags); } return retval; } static void rx_complete(struct usb_ep *ep, struct usb_request *req) { - struct sk_buff *skb = req->context, *skb2; + struct sk_buff *skb = req->context; struct eth_dev *dev = ep->driver_data; int status = req->status; + bool queue = 0; switch (status) { @@ -298,30 +299,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) } else { skb_queue_tail(&dev->rx_frames, skb); } - skb = NULL; - - skb2 = skb_dequeue(&dev->rx_frames); - while (skb2) { - if (status < 0 - || ETH_HLEN > skb2->len - || skb2->len > GETHER_MAX_ETH_FRAME_LEN) { - dev->net->stats.rx_errors++; - dev->net->stats.rx_length_errors++; - DBG(dev, "rx length %d\n", skb2->len); - dev_kfree_skb_any(skb2); - goto next_frame; - } - skb2->protocol = eth_type_trans(skb2, dev->net); - dev->net->stats.rx_packets++; - dev->net->stats.rx_bytes += skb2->len; - - /* no buffer copies needed, unless hardware can't - * use skb buffers. - */ - status = netif_rx(skb2); -next_frame: - skb2 = skb_dequeue(&dev->rx_frames); - } + if (!status) + queue = 1; break; /* software-driven interface shutdown */ @@ -344,22 +323,20 @@ quiesce: /* FALLTHROUGH */ default: + queue = 1; + dev_kfree_skb_any(skb); dev->net->stats.rx_errors++; DBG(dev, "rx status %d\n", status); break; } - if (skb) - dev_kfree_skb_any(skb); - if (!netif_running(dev->net)) { clean: - spin_lock(&dev->req_lock); - list_add(&req->list, &dev->rx_reqs); - spin_unlock(&dev->req_lock); - req = NULL; - } - if (req) - rx_submit(dev, req, GFP_ATOMIC); + spin_lock(&dev->req_lock); + list_add(&req->list, &dev->rx_reqs); + spin_unlock(&dev->req_lock); + + if (queue) + queue_work(uether_wq, &dev->rx_work); } static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n) @@ -424,16 +401,24 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) { struct usb_request *req; unsigned long flags; + int req_cnt = 0; /* fill unused rxq slots with some skb */ spin_lock_irqsave(&dev->req_lock, flags); while (!list_empty(&dev->rx_reqs)) { + /* break the nexus of continuous completion and re-submission*/ + if (++req_cnt > qlen(dev->gadget)) + break; + req = container_of(dev->rx_reqs.next, struct usb_request, list); list_del_init(&req->list); spin_unlock_irqrestore(&dev->req_lock, flags); if (rx_submit(dev, req, gfp_flags) < 0) { + spin_lock_irqsave(&dev->req_lock, flags); + list_add(&req->list, &dev->rx_reqs); + spin_unlock_irqrestore(&dev->req_lock, flags); defer_kevent(dev, WORK_RX_MEMORY); return; } @@ -443,6 +428,36 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) spin_unlock_irqrestore(&dev->req_lock, flags); } +static void process_rx_w(struct work_struct *work) +{ + struct eth_dev *dev = container_of(work, struct eth_dev, rx_work); + struct sk_buff *skb; + int status = 0; + + if (!dev->port_usb) + return; + + while ((skb = skb_dequeue(&dev->rx_frames))) { + if (status < 0 + || ETH_HLEN > skb->len + || skb->len > ETH_FRAME_LEN) { + dev->net->stats.rx_errors++; + dev->net->stats.rx_length_errors++; + DBG(dev, "rx length %d\n", skb->len); + dev_kfree_skb_any(skb); + continue; + } + skb->protocol = eth_type_trans(skb, dev->net); + dev->net->stats.rx_packets++; + dev->net->stats.rx_bytes += skb->len; + + status = netif_rx_ni(skb); + } + + if (netif_running(dev->net)) + rx_fill(dev, GFP_KERNEL); +} + static void eth_work(struct work_struct *work) { struct eth_dev *dev = container_of(work, struct eth_dev, work); @@ -786,6 +801,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, spin_lock_init(&dev->lock); spin_lock_init(&dev->req_lock); INIT_WORK(&dev->work, eth_work); + INIT_WORK(&dev->rx_work, process_rx_w); INIT_LIST_HEAD(&dev->tx_reqs); INIT_LIST_HEAD(&dev->rx_reqs); @@ -1129,6 +1145,7 @@ void gether_disconnect(struct gether *link) { struct eth_dev *dev = link->ioport; struct usb_request *req; + struct sk_buff *skb; WARN_ON(!dev); if (!dev) @@ -1169,6 +1186,12 @@ void gether_disconnect(struct gether *link) spin_lock(&dev->req_lock); } spin_unlock(&dev->req_lock); + + spin_lock(&dev->rx_frames.lock); + while ((skb = __skb_dequeue(&dev->rx_frames))) + dev_kfree_skb_any(skb); + spin_unlock(&dev->rx_frames.lock); + link->out_ep->desc = NULL; /* finish forgetting about this USB link episode */ @@ -1182,5 +1205,23 @@ void gether_disconnect(struct gether *link) } EXPORT_SYMBOL_GPL(gether_disconnect); -MODULE_LICENSE("GPL"); +static int __init gether_init(void) +{ + uether_wq = create_singlethread_workqueue("uether"); + if (!uether_wq) { + pr_err("%s: Unable to create workqueue: uether\n", __func__); + return -ENOMEM; + } + return 0; +} +module_init(gether_init); + +static void __exit gether_exit(void) +{ + destroy_workqueue(uether_wq); + +} +module_exit(gether_exit); MODULE_AUTHOR("David Brownell"); +MODULE_DESCRIPTION("ethernet over USB driver"); +MODULE_LICENSE("GPL v2"); From 265801537d110eb68d44a2f66015479908f635c0 Mon Sep 17 00:00:00 2001 From: "taeju.park" Date: Fri, 14 Sep 2012 14:09:03 +0900 Subject: [PATCH 153/475] usb: gadget: prevent change of Host MAC address of 'usb0' interface On windows 7 platform, previously allocated ip address is maintained. However, Host MAC address of 'usb0' interface is changed when the tethering driver re-enumerated. Thus, the tethering network driver can't be allocated ip address from dhcp. It causes connection delay between host and phone for usb tethering. This patch prevents from changing Host MAC address of 'usb0' interface. In other words, this patch maintains the Host MAC address allocated when first tethering driver although the driver is re-enumerated. However, after reboot, the Host MAC address can be changed. Change-Id: I43add9925e9d6d90c56cffbd3ed999104448f818 Signed-off-by: Badhri Jagan Sridharan --- drivers/usb/gadget/function/u_ether.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 4119dbb73602..a06bd0e21392 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -728,6 +728,8 @@ static int eth_stop(struct net_device *net) /*-------------------------------------------------------------------------*/ +static u8 host_ethaddr[ETH_ALEN]; + static int get_ether_addr(const char *str, u8 *dev_addr) { if (str) { @@ -758,6 +760,17 @@ static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len) return 18; } +static int get_host_ether_addr(u8 *str, u8 *dev_addr) +{ + memcpy(dev_addr, str, ETH_ALEN); + if (is_valid_ether_addr(dev_addr)) + return 0; + + random_ether_addr(dev_addr); + memcpy(str, dev_addr, ETH_ALEN); + return 1; +} + static const struct net_device_ops eth_netdev_ops = { .ndo_open = eth_open, .ndo_stop = eth_stop, @@ -815,9 +828,11 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, if (get_ether_addr(dev_addr, net->dev_addr)) dev_warn(&g->dev, "using random %s ethernet address\n", "self"); - if (get_ether_addr(host_addr, dev->host_mac)) - dev_warn(&g->dev, - "using random %s ethernet address\n", "host"); + + if (get_host_ether_addr(host_ethaddr, dev->host_mac)) + dev_warn(&g->dev, "using random %s ethernet address\n", "host"); + else + dev_warn(&g->dev, "using previous %s ethernet address\n", "host"); if (ethaddr) memcpy(ethaddr, dev->host_mac, ETH_ALEN); From 78281f6ed79413e5a16eac6edc4a72c6836ae5a9 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Thu, 18 Sep 2014 10:42:41 -0700 Subject: [PATCH 154/475] USB: gadget: u_ether: Fix data stall issue in RNDIS tethering mode For dual speed gadget, with current no. of request(10), there is possibility of corner case occurence where all 10 reuqests are queued to HW without setting IOC bit, which could lead to data stall in RNDIS tethering and RNDIS local networking. With this patch, counter will be incremented before queueing request to HW and sets IOC bit for every nth request due to which the corner case of all requests queued to HW without IOC bit set will be avoided. Change-Id: I26515bfd9bbc8f7af38be7835692143f7093118a Signed-off-by: Vijayavardhan Vennapusa --- drivers/usb/gadget/function/u_ether.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index a06bd0e21392..998e67b03864 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -66,7 +66,7 @@ struct eth_dev { spinlock_t req_lock; /* guard {rx,tx}_reqs */ struct list_head tx_reqs, rx_reqs; - atomic_t tx_qlen; + unsigned tx_qlen; struct sk_buff_head rx_frames; @@ -494,7 +494,6 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req) spin_unlock(&dev->req_lock); dev_kfree_skb_any(skb); - atomic_dec(&dev->tx_qlen); if (netif_carrier_ok(dev->net)) netif_wake_queue(dev->net); } @@ -614,12 +613,19 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, req->length = length; - /* throttle high/super speed IRQ rate back slightly */ - if (gadget_is_dualspeed(dev->gadget)) - req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH || - dev->gadget->speed == USB_SPEED_SUPER) - ? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0) - : 0; + /* throttle highspeed IRQ rate back slightly */ + if (gadget_is_dualspeed(dev->gadget) && + (dev->gadget->speed == USB_SPEED_HIGH)) { + dev->tx_qlen++; + if (dev->tx_qlen == qmult) { + req->no_interrupt = 0; + dev->tx_qlen = 0; + } else { + req->no_interrupt = 1; + } + } else { + req->no_interrupt = 0; + } retval = usb_ep_queue(in, req, GFP_ATOMIC); switch (retval) { @@ -628,7 +634,6 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, break; case 0: net->trans_start = jiffies; - atomic_inc(&dev->tx_qlen); } if (retval) { @@ -655,7 +660,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) rx_fill(dev, gfp_flags); /* and open the tx floodgates */ - atomic_set(&dev->tx_qlen, 0); + dev->tx_qlen = 0; netif_wake_queue(dev->net); } From debf5d6eacef1795d537c64992e8ffa6e7aec952 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Thu, 18 Sep 2014 10:46:08 -0700 Subject: [PATCH 155/475] RNDIS: Add Data aggregation (multi packet) support Add data aggregation support using RNDIS Multi Packet feature to achieve better UDP Downlink throughput. Max 3 RNDIS Packets aggregated into one RNDIS Packet with this implementation. With this change, seeing UDP Downlink throughput increase from 90 Mbps to above 100 Mbps when using Iperf and sending data more than 100 Mbps. Change-Id: I21c39482718944bb1b1068bdd02f626531e58f08 Signed-off-by: Mayank Rana Signed-off-by: Rajkumar Raghupathy --- drivers/usb/gadget/function/f_rndis.c | 14 +++ drivers/usb/gadget/function/u_ether.c | 150 ++++++++++++++++++++++++-- 2 files changed, 154 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 479fa2938412..07fad13f93d2 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -460,6 +460,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) { struct f_rndis *rndis = req->context; int status; + rndis_init_msg_type *buf; /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ // spin_lock(&dev->lock); @@ -467,6 +468,19 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) if (status < 0) pr_err("RNDIS command error %d, %d/%d\n", status, req->actual, req->length); + + buf = (rndis_init_msg_type *)req->buf; + + if (buf->MessageType == RNDIS_MSG_INIT) { + if (buf->MaxTransferSize > 2048) + rndis->port.multi_pkt_xfer = 1; + else + rndis->port.multi_pkt_xfer = 0; + DBG(cdev, "%s: MaxTransferSize: %d : Multi_pkt_txr: %s\n", + __func__, buf->MaxTransferSize, + rndis->port.multi_pkt_xfer ? "enabled" : + "disabled"); + } // spin_unlock(&dev->lock); } diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 998e67b03864..25a77ce82caa 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -67,6 +67,11 @@ struct eth_dev { spinlock_t req_lock; /* guard {rx,tx}_reqs */ struct list_head tx_reqs, rx_reqs; unsigned tx_qlen; +/* Minimum number of TX USB request queued to UDC */ +#define TX_REQ_THRESHOLD 5 + int no_tx_req_used; + int tx_skb_hold_count; + u32 tx_req_bufsize; struct sk_buff_head rx_frames; @@ -475,6 +480,11 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req) { struct sk_buff *skb = req->context; struct eth_dev *dev = ep->driver_data; + struct net_device *net = dev->net; + struct usb_request *new_req; + struct usb_ep *in; + int length; + int retval; switch (req->status) { default: @@ -485,14 +495,73 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req) case -ESHUTDOWN: /* disconnect etc */ break; case 0: - dev->net->stats.tx_bytes += skb->len; + if (!req->zero) + dev->net->stats.tx_bytes += req->length-1; + else + dev->net->stats.tx_bytes += req->length; } dev->net->stats.tx_packets++; spin_lock(&dev->req_lock); - list_add(&req->list, &dev->tx_reqs); - spin_unlock(&dev->req_lock); - dev_kfree_skb_any(skb); + list_add_tail(&req->list, &dev->tx_reqs); + + if (dev->port_usb->multi_pkt_xfer) { + dev->no_tx_req_used--; + req->length = 0; + in = dev->port_usb->in_ep; + + if (!list_empty(&dev->tx_reqs)) { + new_req = container_of(dev->tx_reqs.next, + struct usb_request, list); + list_del(&new_req->list); + spin_unlock(&dev->req_lock); + if (new_req->length > 0) { + length = new_req->length; + + /* NCM requires no zlp if transfer is + * dwNtbInMaxSize */ + if (dev->port_usb->is_fixed && + length == dev->port_usb->fixed_in_len && + (length % in->maxpacket) == 0) + new_req->zero = 0; + else + new_req->zero = 1; + + /* use zlp framing on tx for strict CDC-Ether + * conformance, though any robust network rx + * path ignores extra padding. and some hardware + * doesn't like to write zlps. + */ + if (new_req->zero && !dev->zlp && + (length % in->maxpacket) == 0) { + new_req->zero = 0; + length++; + } + + new_req->length = length; + retval = usb_ep_queue(in, new_req, GFP_ATOMIC); + switch (retval) { + default: + DBG(dev, "tx queue err %d\n", retval); + break; + case 0: + spin_lock(&dev->req_lock); + dev->no_tx_req_used++; + spin_unlock(&dev->req_lock); + net->trans_start = jiffies; + } + } else { + spin_lock(&dev->req_lock); + list_add(&new_req->list, &dev->tx_reqs); + spin_unlock(&dev->req_lock); + } + } else { + spin_unlock(&dev->req_lock); + } + } else { + spin_unlock(&dev->req_lock); + dev_kfree_skb_any(skb); + } if (netif_carrier_ok(dev->net)) netif_wake_queue(dev->net); @@ -503,6 +572,26 @@ static inline int is_promisc(u16 cdc_filter) return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; } +static void alloc_tx_buffer(struct eth_dev *dev) +{ + struct list_head *act; + struct usb_request *req; + + dev->tx_req_bufsize = (TX_SKB_HOLD_THRESHOLD * + (dev->net->mtu + + sizeof(struct ethhdr) + /* size of rndis_packet_msg_type */ + + 44 + + 22)); + + list_for_each(act, &dev->tx_reqs) { + req = container_of(act, struct usb_request, list); + if (!req->buf) + req->buf = kmalloc(dev->tx_req_bufsize, + GFP_ATOMIC); + } +} + static netdev_tx_t eth_start_xmit(struct sk_buff *skb, struct net_device *net) { @@ -529,6 +618,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } + /* Allocate memory for tx_reqs to support multi packet transfer */ + if (dev->port_usb->multi_pkt_xfer && !dev->tx_req_bufsize) + alloc_tx_buffer(dev); + /* apply outgoing CDC or RNDIS filters */ if (skb && !is_promisc(cdc_filter)) { u8 *dest = skb->data; @@ -591,9 +684,37 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, } } - length = skb->len; - req->buf = skb->data; - req->context = skb; + spin_lock_irqsave(&dev->req_lock, flags); + dev->tx_skb_hold_count++; + spin_unlock_irqrestore(&dev->req_lock, flags); + + if (dev->port_usb->multi_pkt_xfer) { + memcpy(req->buf + req->length, skb->data, skb->len); + req->length = req->length + skb->len; + length = req->length; + dev_kfree_skb_any(skb); + + spin_lock_irqsave(&dev->req_lock, flags); + if (dev->tx_skb_hold_count < TX_SKB_HOLD_THRESHOLD) { + if (dev->no_tx_req_used > TX_REQ_THRESHOLD) { + list_add(&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->req_lock, flags); + goto success; + } + } + + dev->no_tx_req_used++; + spin_unlock_irqrestore(&dev->req_lock, flags); + + spin_lock_irqsave(&dev->lock, flags); + dev->tx_skb_hold_count = 0; + spin_unlock_irqrestore(&dev->lock, flags); + } else { + length = skb->len; + req->buf = skb->data; + req->context = skb; + } + req->complete = tx_complete; /* NCM requires no zlp if transfer is dwNtbInMaxSize */ @@ -608,8 +729,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, * though any robust network rx path ignores extra padding. * and some hardware doesn't like to write zlps. */ - if (req->zero && !dev->zlp && (length % in->maxpacket) == 0) + if (req->zero && !dev->zlp && (length % in->maxpacket) == 0) { + req->zero = 0; length++; + } req->length = length; @@ -617,7 +740,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, if (gadget_is_dualspeed(dev->gadget) && (dev->gadget->speed == USB_SPEED_HIGH)) { dev->tx_qlen++; - if (dev->tx_qlen == qmult) { + if (dev->tx_qlen == (qmult/2)) { req->no_interrupt = 0; dev->tx_qlen = 0; } else { @@ -637,7 +760,8 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, } if (retval) { - dev_kfree_skb_any(skb); + if (!dev->port_usb->multi_pkt_xfer) + dev_kfree_skb_any(skb); drop: dev->net->stats.tx_dropped++; multiframe: @@ -647,6 +771,7 @@ multiframe: list_add(&req->list, &dev->tx_reqs); spin_unlock_irqrestore(&dev->req_lock, flags); } +success: return NETDEV_TX_OK; } @@ -1121,6 +1246,9 @@ struct net_device *gether_connect(struct gether *link) dev->ul_max_pkts_per_xfer = link->ul_max_pkts_per_xfer; spin_lock(&dev->lock); + dev->tx_skb_hold_count = 0; + dev->no_tx_req_used = 0; + dev->tx_req_bufsize = 0; dev->port_usb = link; if (netif_running(dev->net)) { if (link->open) @@ -1188,6 +1316,8 @@ void gether_disconnect(struct gether *link) list_del(&req->list); spin_unlock(&dev->req_lock); + if (link->multi_pkt_xfer) + kfree(req->buf); usb_ep_free_request(link->in_ep, req); spin_lock(&dev->req_lock); } From c3d7f0cdd24e3db82758cb5989e6e27a6ca270d1 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Thu, 18 Sep 2014 10:48:48 -0700 Subject: [PATCH 156/475] ndis: Add debug support to disable RNDIS Multipacket Feature This change adds module param which allows to disable RNDIS Multi-packet Feature (Aggregation support in Downlink path) as this feature is enabled by default. To disable use this param before moving to RNDIS Composition: echo 1 > /sys/module/g_android/parameters/rndis_multipacket_dl_disable Also counts errors as Rx errors if received RNDIS packets are not following RNDIS message format as those packets are being discarded. Change-Id: I764430da78f2204af92e14bb279c11b24c7e4c67 Signed-off-by: Mayank Rana --- drivers/usb/gadget/function/f_rndis.c | 2 ++ drivers/usb/gadget/function/u_ether.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 07fad13f93d2..635bbbbd0639 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -480,6 +480,8 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) __func__, buf->MaxTransferSize, rndis->port.multi_pkt_xfer ? "enabled" : "disabled"); + if (rndis_multipacket_dl_disable) + rndis->port.multi_pkt_xfer = 0; } // spin_unlock(&dev->lock); } diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 25a77ce82caa..69bf03e04603 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -296,6 +296,10 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) status = dev->unwrap(dev->port_usb, skb, &dev->rx_frames); + if (status == -EINVAL) + dev->net->stats.rx_errors++; + else if (status == -EOVERFLOW) + dev->net->stats.rx_over_errors++; } else { dev_kfree_skb_any(skb); status = -ENOTCONN; From 24134b26ba1051df06c4f722d2f6b0301f83aff2 Mon Sep 17 00:00:00 2001 From: xerox_lin Date: Thu, 4 Sep 2014 16:01:59 +0800 Subject: [PATCH 157/475] USB: gadget: rndis: Add module parameter for DL max packets per xfer Currently DL aggregation is supported in RNDIS driver and is set to 3 by default. And there is no support to change downlink maximum packets per transfer at runtime through module parameter. Hence add module parameter for DL maximum packets per transfer to change it at runtime. echo 6 > /sys/module/g_android/parameters/rndis_dl_max_pkt_per_xfer To disable DL aggregation during runtime, echo 1 > /sys/module/g_android/parameters/rndis_dl_max_pkt_per_xfer Change-Id: I3a1d0bc97358e2b6f233df7ae8725fb507de50db Signed-off-by: Xerox Lin Signed-off-by: Vijayavardhan Vennapusa --- drivers/usb/gadget/function/f_rndis.c | 11 ++++++----- drivers/usb/gadget/function/u_ether.c | 6 ++++-- drivers/usb/gadget/function/u_ether.h | 3 +-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 635bbbbd0639..2758522180f3 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -70,10 +70,10 @@ * - MS-Windows drivers sometimes emit undocumented requests. */ -static bool rndis_multipacket_dl_disable; -module_param(rndis_multipacket_dl_disable, bool, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(rndis_multipacket_dl_disable, - "Disable RNDIS Multi-packet support in DownLink"); +static unsigned int rndis_dl_max_pkt_per_xfer = 3; +module_param(rndis_dl_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(rndis_dl_max_pkt_per_xfer, + "Maximum packets per transfer for DL aggregation"); static unsigned int rndis_ul_max_pkt_per_xfer = 3; module_param(rndis_ul_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR); @@ -480,7 +480,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) __func__, buf->MaxTransferSize, rndis->port.multi_pkt_xfer ? "enabled" : "disabled"); - if (rndis_multipacket_dl_disable) + if (rndis_dl_max_pkt_per_xfer <= 1) rndis->port.multi_pkt_xfer = 0; } // spin_unlock(&dev->lock); @@ -1006,6 +1006,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) rndis->port.wrap = rndis_add_header; rndis->port.unwrap = rndis_rm_hdr; rndis->port.ul_max_pkts_per_xfer = rndis_ul_max_pkt_per_xfer; + rndis->port.dl_max_pkts_per_xfer = rndis_dl_max_pkt_per_xfer; rndis->port.func.name = "rndis"; /* descriptors are per-instance copies */ diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 69bf03e04603..e39880a654cc 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -79,6 +79,7 @@ struct eth_dev { unsigned header_len; unsigned ul_max_pkts_per_xfer; + unsigned dl_max_pkts_per_xfer; struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb); int (*unwrap)(struct gether *, struct sk_buff *skb, @@ -581,7 +582,7 @@ static void alloc_tx_buffer(struct eth_dev *dev) struct list_head *act; struct usb_request *req; - dev->tx_req_bufsize = (TX_SKB_HOLD_THRESHOLD * + dev->tx_req_bufsize = (dev->dl_max_pkts_per_xfer * (dev->net->mtu + sizeof(struct ethhdr) /* size of rndis_packet_msg_type */ @@ -699,7 +700,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, dev_kfree_skb_any(skb); spin_lock_irqsave(&dev->req_lock, flags); - if (dev->tx_skb_hold_count < TX_SKB_HOLD_THRESHOLD) { + if (dev->tx_skb_hold_count < dev->dl_max_pkts_per_xfer) { if (dev->no_tx_req_used > TX_REQ_THRESHOLD) { list_add(&req->list, &dev->tx_reqs); spin_unlock_irqrestore(&dev->req_lock, flags); @@ -1248,6 +1249,7 @@ struct net_device *gether_connect(struct gether *link) dev->unwrap = link->unwrap; dev->wrap = link->wrap; dev->ul_max_pkts_per_xfer = link->ul_max_pkts_per_xfer; + dev->dl_max_pkts_per_xfer = link->dl_max_pkts_per_xfer; spin_lock(&dev->lock); dev->tx_skb_hold_count = 0; diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h index 7914b4b1ce6c..e9fd14b46102 100644 --- a/drivers/usb/gadget/function/u_ether.h +++ b/drivers/usb/gadget/function/u_ether.h @@ -74,8 +74,7 @@ struct gether { u32 fixed_out_len; u32 fixed_in_len; unsigned ul_max_pkts_per_xfer; -/* Max number of SKB packets to be used to create Multi Packet RNDIS */ -#define TX_SKB_HOLD_THRESHOLD 3 + unsigned dl_max_pkts_per_xfer; bool multi_pkt_xfer; bool supports_multi_frame; struct sk_buff *(*wrap)(struct gether *port, From 7e3ab4a4692faea484ae0c8b87a46ec668504f72 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 17 Nov 2014 21:11:23 -0800 Subject: [PATCH 158/475] usb: gadget: mtp/ptp: Migrate functions to the USB_FUNCTION interface This patch adds support to use mtp/ptp gadget functions through the DECLARE_USB_FUNCTION_INIT interface. enabling USB_CONFIGFS_F_MTP config compiles f_mtp.c thereby providing support for MTP gadget enabling USB_CONFIGFS_F_PTP config compiles f_ptp.c thereby providing support for PTP gadget Signed-off-by: Badhri Jagan Sridharan Change-Id: I38d7b570e8886d155ef10cd2c839b2232dcb3158 --- drivers/usb/gadget/Kconfig | 20 ++++ drivers/usb/gadget/Makefile | 5 + drivers/usb/gadget/f_mtp.c | 178 +++++++++++++++++++++++++++++++++++- drivers/usb/gadget/f_mtp.h | 18 ++++ drivers/usb/gadget/f_ptp.c | 38 ++++++++ 5 files changed, 255 insertions(+), 4 deletions(-) create mode 100644 drivers/usb/gadget/f_mtp.h create mode 100644 drivers/usb/gadget/f_ptp.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 33834aa09ed4..fc7b02bfcf09 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -199,6 +199,12 @@ config USB_F_HID config USB_F_PRINTER tristate +config USB_F_MTP + tristate + +config USB_F_PTP + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -371,6 +377,20 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. +config USB_CONFIGFS_F_MTP + boolean "MTP gadget" + depends on USB_CONFIGFS + select USB_F_MTP + help + USB gadget MTP support + +config USB_CONFIGFS_F_PTP + boolean "PTP gadget" + depends on USB_CONFIGFS && USB_CONFIGFS_F_MTP + select USB_F_PTP + help + USB gadget PTP support + config USB_CONFIGFS_F_UAC1 bool "Audio Class 1.0" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 598a67d6ba05..502c379cce80 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -10,3 +10,8 @@ libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ + +usb_f_mtp-y := f_mtp.o +obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o +usb_f_ptp-y := f_ptp.o +obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 620aeaaf2d72..82f6b2ebaebb 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -35,9 +35,14 @@ #include #include #include +#include +#include + +#include "configfs.h" #define MTP_BULK_BUFFER_SIZE 16384 #define INTR_BUFFER_SIZE 28 +#define MAX_INST_NAME_LEN 40 /* String IDs */ #define INTERFACE_STRING_INDEX 0 @@ -66,8 +71,9 @@ /* constants for device status */ #define MTP_RESPONSE_OK 0x2001 #define MTP_RESPONSE_DEVICE_BUSY 0x2019 +#define DRIVER_NAME "mtp" -static const char mtp_shortname[] = "mtp_usb"; +static const char mtp_shortname[] = DRIVER_NAME "_usb"; struct mtp_dev { struct usb_function function; @@ -280,6 +286,12 @@ struct mtp_data_header { __le32 transaction_id; }; +struct mtp_instance { + struct usb_function_instance func_inst; + const char *name; + struct mtp_dev *dev; +}; + /* temporary variable used between mtp_open() and mtp_gadget_bind() */ static struct mtp_dev *_mtp_dev; @@ -456,7 +468,7 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev, return 0; fail: - printk(KERN_ERR "mtp_bind() could not allocate requests\n"); + pr_err("mtp_bind() could not allocate requests\n"); return -1; } @@ -1099,6 +1111,13 @@ mtp_function_bind(struct usb_configuration *c, struct usb_function *f) return id; mtp_interface_desc.bInterfaceNumber = id; + 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; + } /* allocate endpoints */ ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc, &mtp_fullspeed_out_desc, &mtp_intr_desc); @@ -1126,6 +1145,7 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) struct usb_request *req; int i; + mtp_string_defs[INTERFACE_STRING_INDEX].id = 0; while ((req = mtp_req_get(dev, &dev->tx_idle))) mtp_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -1213,7 +1233,7 @@ static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) } dev->cdev = c->cdev; - dev->function.name = "mtp"; + dev->function.name = DRIVER_NAME; dev->function.strings = mtp_strings; if (ptp_config) { dev->function.fs_descriptors = fs_ptp_descs; @@ -1230,12 +1250,16 @@ static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) return usb_add_function(c, &dev->function); } -static int mtp_setup(void) +static int __mtp_setup(struct mtp_instance *fi_mtp) { struct mtp_dev *dev; int ret; dev = kzalloc(sizeof(*dev), GFP_KERNEL); + + if (fi_mtp != NULL) + fi_mtp->dev = dev; + if (!dev) return -ENOMEM; @@ -1273,6 +1297,17 @@ err1: return ret; } +static int mtp_setup(void) +{ + return __mtp_setup(NULL); +} + +static int mtp_setup_configfs(struct mtp_instance *fi_mtp) +{ + return __mtp_setup(fi_mtp); +} + + static void mtp_cleanup(void) { struct mtp_dev *dev = _mtp_dev; @@ -1285,3 +1320,138 @@ static void mtp_cleanup(void) _mtp_dev = NULL; kfree(dev); } + +static struct mtp_instance *to_mtp_instance(struct config_item *item) +{ + return container_of(to_config_group(item), struct mtp_instance, + func_inst.group); +} + +static void mtp_attr_release(struct config_item *item) +{ + struct mtp_instance *fi_mtp = to_mtp_instance(item); + usb_put_function_instance(&fi_mtp->func_inst); +} + +static struct configfs_item_operations mtp_item_ops = { + .release = mtp_attr_release, +}; + +static struct config_item_type mtp_func_type = { + .ct_item_ops = &mtp_item_ops, + .ct_owner = THIS_MODULE, +}; + + +static struct mtp_instance *to_fi_mtp(struct usb_function_instance *fi) +{ + return container_of(fi, struct mtp_instance, func_inst); +} + +static int mtp_set_inst_name(struct usb_function_instance *fi, const char *name) +{ + struct mtp_instance *fi_mtp; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_mtp = to_fi_mtp(fi); + fi_mtp->name = ptr; + + return 0; +} + +static void mtp_free_inst(struct usb_function_instance *fi) +{ + struct mtp_instance *fi_mtp; + + fi_mtp = to_fi_mtp(fi); + kfree(fi_mtp->name); + mtp_cleanup(); + kfree(fi_mtp); +} + +struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config) +{ + struct mtp_instance *fi_mtp; + int ret = 0; + + fi_mtp = kzalloc(sizeof(*fi_mtp), GFP_KERNEL); + if (!fi_mtp) + return ERR_PTR(-ENOMEM); + fi_mtp->func_inst.set_inst_name = mtp_set_inst_name; + fi_mtp->func_inst.free_func_inst = mtp_free_inst; + + if (mtp_config) { + ret = mtp_setup_configfs(fi_mtp); + if (ret) { + kfree(fi_mtp); + pr_err("Error setting MTP\n"); + return ERR_PTR(ret); + } + } else + fi_mtp->dev = _mtp_dev; + + config_group_init_type_name(&fi_mtp->func_inst.group, + "", &mtp_func_type); + + return &fi_mtp->func_inst; +} +EXPORT_SYMBOL_GPL(alloc_inst_mtp_ptp); + +static struct usb_function_instance *mtp_alloc_inst(void) +{ + return alloc_inst_mtp_ptp(true); +} + +static int mtp_ctrlreq_configfs(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + return mtp_ctrlrequest(f->config->cdev, ctrl); +} + +static void mtp_free(struct usb_function *f) +{ + /*NO-OP: no function specific resource allocation in mtp_alloc*/ +} + +struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, + bool mtp_config) +{ + struct mtp_instance *fi_mtp = to_fi_mtp(fi); + struct mtp_dev *dev = fi_mtp->dev; + + dev->function.name = DRIVER_NAME; + dev->function.strings = mtp_strings; + if (mtp_config) { + dev->function.fs_descriptors = fs_mtp_descs; + dev->function.hs_descriptors = hs_mtp_descs; + } else { + dev->function.fs_descriptors = fs_ptp_descs; + dev->function.hs_descriptors = hs_ptp_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; + dev->function.setup = mtp_ctrlreq_configfs; + dev->function.free_func = mtp_free; + + return &dev->function; +} +EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp); + +static struct usb_function *mtp_alloc(struct usb_function_instance *fi) +{ + return function_alloc_mtp_ptp(fi, true); +} + +DECLARE_USB_FUNCTION_INIT(mtp, mtp_alloc_inst, mtp_alloc); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/f_mtp.h b/drivers/usb/gadget/f_mtp.h new file mode 100644 index 000000000000..7adb1ff08eff --- /dev/null +++ b/drivers/usb/gadget/f_mtp.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2014 Google, Inc. + * Author: Badhri Jagan Sridharan + * + * 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. + * + */ + +extern struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config); +extern struct usb_function *function_alloc_mtp_ptp( + struct usb_function_instance *fi, bool mtp_config); diff --git a/drivers/usb/gadget/f_ptp.c b/drivers/usb/gadget/f_ptp.c new file mode 100644 index 000000000000..da3e4d53e085 --- /dev/null +++ b/drivers/usb/gadget/f_ptp.c @@ -0,0 +1,38 @@ +/* + * Gadget Function Driver for PTP + * + * Copyright (C) 2014 Google, Inc. + * Author: Badhri Jagan Sridharan + * + * 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 "f_mtp.h" + +static struct usb_function_instance *ptp_alloc_inst(void) +{ + return alloc_inst_mtp_ptp(false); +} + +static struct usb_function *ptp_alloc(struct usb_function_instance *fi) +{ + return function_alloc_mtp_ptp(fi, false); +} + +DECLARE_USB_FUNCTION_INIT(ptp, ptp_alloc_inst, ptp_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Badhri Jagan Sridharan"); From 6ccbd233aecf8aaa054d41df74d7e6b3fe21c73b Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 15 Dec 2014 16:42:27 -0800 Subject: [PATCH 159/475] usb: gadget: configfs: Add usb_function ptr to fi struct Add a pointer to the usb_function inside the usb_function_instance structure to service functions specific setup requests even before the function gets added to the usb_gadget Signed-off-by: Badhri Jagan Sridharan Change-Id: I6f457006f6c5516cc6986ec2acdf5b1ecf259d0c --- include/linux/usb/composite.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 1074b8921a5d..15d7c311e86e 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -574,6 +574,7 @@ struct usb_function_instance { struct config_group group; struct list_head cfs_list; struct usb_function_driver *fd; + struct usb_function *f; int (*set_inst_name)(struct usb_function_instance *inst, const char *name); void (*free_func_inst)(struct usb_function_instance *inst); From 4b2db1ab6c58dbafa567d718d09ae8ee1cbee4d5 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 15 Dec 2014 10:44:47 -0800 Subject: [PATCH 160/475] usb: gadget: Add Uevent to notify userspace Android userspace UsbDeviceManager relies on the uevents generated by the composition driver to generate user notifications. This CL adds uevents to be generated whenever USB changes its state i.e. connected, disconnected, configured. This CL also intercepts the setup requests from the usb_core anb routes it to the specific usb function if required. Signed-off-by: Badhri Jagan Sridharan Change-Id: Ib3d3a78255a532f7449dac286f776c2966caf8c1 --- drivers/usb/gadget/Kconfig | 8 ++ drivers/usb/gadget/configfs.c | 154 +++++++++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index fc7b02bfcf09..6977b224218c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -391,6 +391,14 @@ config USB_CONFIGFS_F_PTP help USB gadget PTP support +config USB_CONFIGFS_UEVENT + boolean "Uevent notification of Gadget state" + depends on USB_CONFIGFS + help + Enable uevent notifications to userspace when the gadget + state changes. The gadget can be in any of the following + three states: "CONNECTED/DISCONNECTED/CONFIGURED" + config USB_CONFIGFS_F_UAC1 bool "Audio Class 1.0" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 163d305e1200..8c527eb2289c 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -9,6 +9,20 @@ #include "u_f.h" #include "u_os_desc.h" +#ifdef CONFIG_USB_CONFIGFS_UEVENT +#include +#include +#include +#include "u_fs.h" + +#ifdef CONFIG_USB_CONFIGFS_F_ACC +extern int acc_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl); +void acc_disconnect(void); +#endif +static struct class *android_class; +#endif + int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) { @@ -62,6 +76,12 @@ struct gadget_info { bool use_os_desc; char b_vendor_code; char qw_sign[OS_STRING_QW_SIGN_LEN]; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + bool connected; + bool sw_connected; + struct work_struct work; + struct device *dev; +#endif }; static inline struct gadget_info *to_gadget_info(struct config_item *item) @@ -266,7 +286,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, mutex_lock(&gi->lock); - if (!strlen(name)) { + if (!strlen(name) || strcmp(name, "none") == 0) { ret = unregister_gadget(gi); if (ret) goto err; @@ -1373,6 +1393,57 @@ err_comp_cleanup: return ret; } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static void android_work(struct work_struct *data) +{ + struct gadget_info *gi = container_of(data, struct gadget_info, work); + struct usb_composite_dev *cdev = &gi->cdev; + char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; + char *connected[2] = { "USB_STATE=CONNECTED", NULL }; + char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; + /* 0-connected 1-configured 2-disconnected*/ + bool status[3] = { false, false, false }; + unsigned long flags; + bool uevent_sent = false; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + status[1] = true; + + if (gi->connected != gi->sw_connected) { + if (gi->connected) + status[0] = true; + else + status[2] = true; + gi->sw_connected = gi->connected; + } + spin_unlock_irqrestore(&cdev->lock, flags); + + if (status[0]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected); + pr_info("%s: sent uevent %s\n", __func__, connected[0]); + uevent_sent = true; + } + + if (status[1]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured); + pr_info("%s: sent uevent %s\n", __func__, configured[0]); + uevent_sent = true; + } + + if (status[2]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected); + pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); + uevent_sent = true; + } + + if (!uevent_sent) { + pr_info("%s: did not send uevent (%d %d %p)\n", __func__, + gi->connected, gi->sw_connected, cdev->config); + } +} +#endif + static void configfs_composite_unbind(struct usb_gadget *gadget) { struct usb_composite_dev *cdev; @@ -1392,14 +1463,78 @@ static void configfs_composite_unbind(struct usb_gadget *gadget) set_gadget_data(gadget, NULL); } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static int android_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *c) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + int value = -EOPNOTSUPP; + struct usb_function_instance *fi; + + spin_lock_irqsave(&cdev->lock, flags); + if (!gi->connected) { + gi->connected = 1; + schedule_work(&gi->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + list_for_each_entry(fi, &gi->available_func, cfs_list) { + if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) { + value = fi->f->setup(fi->f, c); + if (value >= 0) + break; + } + } + +#ifdef CONFIG_USB_CONFIGFS_F_ACC + if (value < 0) + value = acc_ctrlrequest(cdev, c); +#endif + + if (value < 0) + value = composite_setup(gadget, c); + + spin_lock_irqsave(&cdev->lock, flags); + if (c->bRequest == USB_REQ_SET_CONFIGURATION && + cdev->config) { + schedule_work(&gi->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + + return value; +} + +static void android_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + + /* accessory HID support can be active while the + accessory function is not actually enabled, + so we need to inform it when we are disconnected. + */ + +#ifdef CONFIG_USB_CONFIGFS_F_ACC + acc_disconnect(); +#endif + gi->connected = 0; + schedule_work(&gi->work); + composite_disconnect(gadget); +} +#endif + static const struct usb_gadget_driver configfs_driver_template = { .bind = configfs_composite_bind, .unbind = configfs_composite_unbind, - +#ifdef CONFIG_USB_CONFIGFS_UEVENT + .setup = android_setup, + .disconnect = android_disconnect, +#else .setup = composite_setup, .reset = composite_disconnect, .disconnect = composite_disconnect, - +#endif .suspend = composite_suspend, .resume = composite_resume, @@ -1455,6 +1590,12 @@ static struct config_group *gadgets_make( gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); gi->composite.name = gi->composite.gadget_driver.function; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + INIT_WORK(&gi->work, android_work); + gi->dev = device_create(android_class, NULL, + MKDEV(0, 0), NULL, "android0"); +#endif + if (!gi->composite.gadget_driver.function) goto err; @@ -1506,6 +1647,13 @@ static int __init gadget_cfs_init(void) config_group_init(&gadget_subsys.su_group); ret = configfs_register_subsystem(&gadget_subsys); + +#ifdef CONFIG_USB_CONFIGFS_UEVENT + android_class = class_create(THIS_MODULE, "android_usb"); + if (IS_ERR(android_class)) + return PTR_ERR(android_class); +#endif + return ret; } module_init(gadget_cfs_init); From 260bcfb18baa50809c2ac4eb2facc487fe1aecc1 Mon Sep 17 00:00:00 2001 From: Praneeth Bajjuri Date: Thu, 22 Jan 2015 16:38:56 -0600 Subject: [PATCH 161/475] usb: u_ether: Fix compile errors commit f1a1823ff24fa4e3412b5078f20021cf40834946 usb: gadget: u_ether: convert into module changes qlen function definition. and this has to be fixed accordingly in current u_ether driver. This patch fixes following compile error in u_ether caused by commit. drivers/usb/gadget/u_ether.c: In function 'rx_fill': drivers/usb/gadget/u_ether.c:416:3: error: too few arguments to function 'qlen' if (++req_cnt > qlen(dev->gadget)) ^ drivers/usb/gadget/u_ether.c: In function 'eth_start_xmit': drivers/usb/gadget/u_ether.c:738:24: error: 'qmult' undeclared (first use in this function) if (dev->tx_qlen == (qmult/2)) { which was caused by commits commit 79467317949e1621240f632acfb7453783bec2e7 USB: gadget: u_ether: Fix data stall issue in RNDIS tethering mode commit 68b91e8c54f5c091986c5719631893b10eab760a usb: u_ether: Add workqueue as bottom half handler for rx data path Change-Id: Ic4e5a1e08cb688e5a606c7c1895f869d8f887b9f Signed-off-by: Praneeth Bajjuri --- drivers/usb/gadget/function/u_ether.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index e39880a654cc..565cde9fbcf1 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -417,7 +417,7 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) spin_lock_irqsave(&dev->req_lock, flags); while (!list_empty(&dev->rx_reqs)) { /* break the nexus of continuous completion and re-submission*/ - if (++req_cnt > qlen(dev->gadget)) + if (++req_cnt > qlen(dev->gadget, dev->qmult)) break; req = container_of(dev->rx_reqs.next, @@ -745,7 +745,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, if (gadget_is_dualspeed(dev->gadget) && (dev->gadget->speed == USB_SPEED_HIGH)) { dev->tx_qlen++; - if (dev->tx_qlen == (qmult/2)) { + if (dev->tx_qlen == (dev->qmult/2)) { req->no_interrupt = 0; dev->tx_qlen = 0; } else { From e087ef4d0d80f0e54fe57a5969a6617ffcbd6005 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 16 Jan 2015 05:41:10 +0530 Subject: [PATCH 162/475] usb: gadget: check for accessory device before disconnecting HIDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While disabling ConfigFS Android gadget, android_disconnect() calls kill_all_hid_devices(), if CONFIG_USB_CONFIGFS_F_ACC is enabled, to free the registered HIDs without checking whether the USB accessory device really exist or not. If USB accessory device doesn't exist then we run into following kernel panic: ----8<---- [  136.724761] Unable to handle kernel NULL pointer dereference at virtual address 00000064 [  136.724809] pgd = c0204000 [  136.731924] [00000064] *pgd=00000000 [  136.737830] Internal error: Oops: 5 [#1] SMP ARM [  136.738108] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.18.0-rc4-00400-gf75300e-dirty #76 [  136.742788] task: c0fb19d8 ti: c0fa4000 task.ti: c0fa4000 [  136.750890] PC is at _raw_spin_lock_irqsave+0x24/0x60 [  136.756246] LR is at kill_all_hid_devices+0x24/0x114 ---->8---- This patch adds a test to check if USB Accessory device exists before freeing HIDs. Change-Id: Ie229feaf0de3f4f7a151fcaa9a994e34e15ff73b Signed-off-by: Amit Pundir --- drivers/usb/gadget/f_accessory.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index f8490c034107..9ffe017bf1cf 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -929,6 +929,10 @@ kill_all_hid_devices(struct acc_dev *dev) struct list_head *entry, *temp; unsigned long flags; + /* do nothing if usb accessory device doesn't exist */ + if (!dev) + return; + spin_lock_irqsave(&dev->lock, flags); list_for_each_safe(entry, temp, &dev->hid_list) { hid = list_entry(entry, struct acc_hid_dev, list); From 795b676102b66700581daed20c3fe6961d09563a Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 25 Mar 2015 14:37:23 -0700 Subject: [PATCH 163/475] usb: gadget: f_audio_source:replace deprecated API Replace snd_card_create with snd_card_new. snd_card_create depcrecated starting form v3.15 Signed-off-by: Badhri Jagan Sridharan Change-Id: I76f7d753812963d595055bce7d3e6518163482f5 --- drivers/usb/gadget/f_audio_source.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 21ced13c83d8..4b9786b48950 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -788,16 +788,16 @@ int audio_source_bind_config(struct usb_configuration *c, audio = &_audio_dev; - err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + err = snd_card_new(&c->cdev->gadget->dev, + 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; From dbaad39b68c88745a59d53e45403332bd1761462 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 27 Mar 2015 14:15:19 -0700 Subject: [PATCH 164/475] usb: gadget: Add function devices to the parent Added create_function_device to create child function devices for USB gadget functions. Android UsbDeviceManager relies on communicating to the devices created by the gadget functions to implement functions such as audio_source. Signed-off-by: Badhri Jagan Sridharan Change-Id: I0df9ad86ac32d8cdacdea164e9fed49891b45fc2 --- drivers/usb/gadget/configfs.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 8c527eb2289c..38077becbf2c 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -13,7 +13,6 @@ #include #include #include -#include "u_fs.h" #ifdef CONFIG_USB_CONFIGFS_F_ACC extern int acc_ctrlrequest(struct usb_composite_dev *cdev, @@ -21,6 +20,18 @@ extern int acc_ctrlrequest(struct usb_composite_dev *cdev, void acc_disconnect(void); #endif static struct class *android_class; +static struct device *android_device; +static int index; + +struct device *create_function_device(char *name) +{ + if (android_device && !IS_ERR(android_device)) + return device_create(android_class, android_device, + MKDEV(0, index++), NULL, name); + else + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(create_function_device); #endif int check_user_usb_string(const char *name, @@ -1420,19 +1431,22 @@ static void android_work(struct work_struct *data) spin_unlock_irqrestore(&cdev->lock, flags); if (status[0]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, connected); pr_info("%s: sent uevent %s\n", __func__, connected[0]); uevent_sent = true; } if (status[1]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, configured); pr_info("%s: sent uevent %s\n", __func__, configured[0]); uevent_sent = true; } if (status[2]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, disconnected); pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); uevent_sent = true; } @@ -1554,7 +1568,6 @@ static struct config_group *gadgets_make( gi = kzalloc(sizeof(*gi), GFP_KERNEL); if (!gi) return ERR_PTR(-ENOMEM); - gi->group.default_groups = gi->default_groups; gi->group.default_groups[0] = &gi->functions_group; gi->group.default_groups[1] = &gi->configs_group; @@ -1592,8 +1605,10 @@ static struct config_group *gadgets_make( #ifdef CONFIG_USB_CONFIGFS_UEVENT INIT_WORK(&gi->work, android_work); - gi->dev = device_create(android_class, NULL, + android_device = device_create(android_class, NULL, MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(android_device)) + goto err; #endif if (!gi->composite.gadget_driver.function) @@ -1610,6 +1625,9 @@ err: static void gadgets_drop(struct config_group *group, struct config_item *item) { config_item_put(item); +#ifdef CONFIG_USB_CONFIGFS_UEVENT + device_destroy(android_device->class, android_device->devt); +#endif } static struct configfs_group_operations gadgets_ops = { @@ -1661,5 +1679,10 @@ module_init(gadget_cfs_init); static void __exit gadget_cfs_exit(void) { configfs_unregister_subsystem(&gadget_subsys); +#ifdef CONFIG_USB_CONFIGFS_UEVENT + if (!IS_ERR(android_class)) + class_destroy(android_class); +#endif + } module_exit(gadget_cfs_exit); From 3541608288141d2c7763315fab6e8be44db32349 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 23 Nov 2014 13:51:28 -0800 Subject: [PATCH 165/475] usb:gadget:audio_source: Move to USB_FUNCTION API This patch adds support to use audio_source gadget function through DECLARE_USB_FUNCTION_INIT interface. Signed-off-by: Badhri Jagan Sridharan Change-Id: I1fc6c9ea07105ae4eb785eebd3bb925bfdd8bc6b --- drivers/usb/gadget/Kconfig | 10 ++ drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_audio_source.c | 234 +++++++++++++++++++++++++++- 3 files changed, 239 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 6977b224218c..f550989173bf 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -203,6 +203,9 @@ config USB_F_MTP tristate config USB_F_PTP + tristate + +config USB_F_AUDIO_SRC tristate choice @@ -391,6 +394,13 @@ config USB_CONFIGFS_F_PTP help USB gadget PTP support +config USB_CONFIGFS_F_AUDIO_SRC + boolean "Audio Source gadget" + depends on USB_CONFIGFS + select USB_F_AUDIO_SRC + help + USB gadget Audio Source support + config USB_CONFIGFS_UEVENT boolean "Uevent notification of Gadget state" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 502c379cce80..386ed6343e33 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -15,3 +15,5 @@ usb_f_mtp-y := f_mtp.o obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o usb_f_ptp-y := f_ptp.o obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o +usb_f_audio_source-y := f_audio_source.o +obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 4b9786b48950..39645be93502 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -21,6 +21,13 @@ #include #include +#include +#include +#include +#include +#include +#include +#include #define SAMPLE_RATE 44100 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) @@ -32,6 +39,7 @@ #define AUDIO_AC_INTERFACE 0 #define AUDIO_AS_INTERFACE 1 #define AUDIO_NUM_INTERFACES 2 +#define MAX_INST_NAME_LEN 40 /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc = { @@ -259,6 +267,7 @@ struct audio_dev { ktime_t start_time; /* number of frames sent since start_time */ s64 frames_sent; + struct audio_source_config *config; }; static inline struct audio_dev *func_to_audio(struct usb_function *f) @@ -268,6 +277,36 @@ static inline struct audio_dev *func_to_audio(struct usb_function *f) /*-------------------------------------------------------------------------*/ +struct audio_source_instance { + struct usb_function_instance func_inst; + const char *name; + struct audio_source_config *config; + struct device *audio_device; +}; + +static void audio_source_attr_release(struct config_item *item); + +static struct configfs_item_operations audio_source_item_ops = { + .release = audio_source_attr_release, +}; + +static struct config_item_type audio_source_func_type = { + .ct_item_ops = &audio_source_item_ops, + .ct_owner = THIS_MODULE, +}; + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(pcm, S_IRUGO, audio_source_pcm_show, NULL); + +static struct device_attribute *audio_source_function_attributes[] = { + &dev_attr_pcm, + NULL +}; + +/*--------------------------------------------------------------------------*/ + 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); @@ -561,6 +600,13 @@ static void audio_build_desc(struct audio_dev *audio) memcpy(sam_freq, &rate, 3); } + +static int snd_card_setup(struct usb_configuration *c, + struct audio_source_config *config); +static struct audio_source_instance *to_fi_audio_source( + const struct usb_function_instance *fi); + + /* audio function driver setup/binding */ static int audio_bind(struct usb_configuration *c, struct usb_function *f) @@ -571,6 +617,18 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct usb_request *req; int i; + int err; + + if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { + struct audio_source_instance *fi_audio = + to_fi_audio_source(f->fi); + struct audio_source_config *config = + fi_audio->config; + + err = snd_card_setup(c, config); + if (err) + return err; + } audio_build_desc(audio); @@ -636,6 +694,16 @@ audio_unbind(struct usb_configuration *c, struct usb_function *f) audio->pcm = NULL; audio->substream = NULL; audio->in_ep = NULL; + + if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { + struct audio_source_instance *fi_audio = + to_fi_audio_source(f->fi); + struct audio_source_config *config = + fi_audio->config; + + config->card = -1; + config->device = -1; + } } static void audio_pcm_playback_start(struct audio_dev *audio) @@ -779,8 +847,6 @@ 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; @@ -788,6 +854,31 @@ int audio_source_bind_config(struct usb_configuration *c, audio = &_audio_dev; + err = snd_card_setup(c, config); + if (err) + return err; + + err = usb_add_function(c, &audio->func); + if (err) + goto add_fail; + + return 0; + +add_fail: + snd_card_free(audio->card); + return err; +} + +static int snd_card_setup(struct usb_configuration *c, + struct audio_source_config *config) +{ + struct audio_dev *audio; + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + audio = &_audio_dev; + err = snd_card_new(&c->cdev->gadget->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); @@ -817,18 +908,147 @@ int audio_source_bind_config(struct usb_configuration *c, 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; } + +static struct audio_source_instance *to_audio_source_instance( + struct config_item *item) +{ + return container_of(to_config_group(item), struct audio_source_instance, + func_inst.group); +} + +static struct audio_source_instance *to_fi_audio_source( + const struct usb_function_instance *fi) +{ + return container_of(fi, struct audio_source_instance, func_inst); +} + +static void audio_source_attr_release(struct config_item *item) +{ + struct audio_source_instance *fi_audio = to_audio_source_instance(item); + + usb_put_function_instance(&fi_audio->func_inst); +} + +static int audio_source_set_inst_name(struct usb_function_instance *fi, + const char *name) +{ + struct audio_source_instance *fi_audio; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_audio = to_fi_audio_source(fi); + fi_audio->name = ptr; + + return 0; +} + +static void audio_source_free_inst(struct usb_function_instance *fi) +{ + struct audio_source_instance *fi_audio; + + fi_audio = to_fi_audio_source(fi); + device_destroy(fi_audio->audio_device->class, + fi_audio->audio_device->devt); + kfree(fi_audio->name); + kfree(fi_audio->config); +} + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct audio_source_instance *fi_audio = dev_get_drvdata(dev); + struct audio_source_config *config = fi_audio->config; + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", config->card, config->device); +} + +struct device *create_function_device(char *name); + +static struct usb_function_instance *audio_source_alloc_inst(void) +{ + struct audio_source_instance *fi_audio; + struct device_attribute **attrs; + struct device_attribute *attr; + struct device *dev; + void *err_ptr; + int err = 0; + + fi_audio = kzalloc(sizeof(*fi_audio), GFP_KERNEL); + if (!fi_audio) + return ERR_PTR(-ENOMEM); + + fi_audio->func_inst.set_inst_name = audio_source_set_inst_name; + fi_audio->func_inst.free_func_inst = audio_source_free_inst; + + fi_audio->config = kzalloc(sizeof(struct audio_source_config), + GFP_KERNEL); + if (!fi_audio->config) { + err_ptr = ERR_PTR(-ENOMEM); + goto fail_audio; + } + + config_group_init_type_name(&fi_audio->func_inst.group, "", + &audio_source_func_type); + dev = create_function_device("f_audio_source"); + + if (IS_ERR(dev)) { + err_ptr = dev; + goto fail_audio_config; + } + + fi_audio->config->card = -1; + fi_audio->config->device = -1; + fi_audio->audio_device = dev; + + attrs = audio_source_function_attributes; + if (attrs) { + while ((attr = *attrs++) && !err) + err = device_create_file(dev, attr); + if (err) { + err_ptr = ERR_PTR(-EINVAL); + goto fail_device; + } + } + + dev_set_drvdata(dev, fi_audio); + _audio_dev.config = fi_audio->config; + + return &fi_audio->func_inst; + +fail_device: + device_destroy(dev->class, dev->devt); +fail_audio_config: + kfree(fi_audio->config); +fail_audio: + kfree(fi_audio); + return err_ptr; + +} + +static struct usb_function *audio_source_alloc(struct usb_function_instance *fi) +{ + return &_audio_dev.func; +} + +DECLARE_USB_FUNCTION_INIT(audio_source, audio_source_alloc_inst, + audio_source_alloc); +MODULE_LICENSE("GPL"); From 9c5cdf4ddd10cbba16022f94093d366e7657f2b1 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 27 Mar 2015 14:49:55 -0700 Subject: [PATCH 166/475] usb: gadget: Move gadget functions code 3.18 kernel has reorganized drivers/usb/gadget directory. Moving gadget functions drivers from drivers/usb/gadget to drivers/usb/gadget/function Signed-off-by: Badhri Jagan Sridharan Change-Id: I1eab0190f8d42e3be1b4e91ad3bc3a2dc853b0ef --- drivers/usb/gadget/Makefile | 7 ------- drivers/usb/gadget/function/Makefile | 6 ++++++ drivers/usb/gadget/{ => function}/f_audio_source.c | 0 drivers/usb/gadget/{ => function}/f_mtp.c | 0 drivers/usb/gadget/{ => function}/f_mtp.h | 0 drivers/usb/gadget/{ => function}/f_ptp.c | 0 6 files changed, 6 insertions(+), 7 deletions(-) rename drivers/usb/gadget/{ => function}/f_audio_source.c (100%) rename drivers/usb/gadget/{ => function}/f_mtp.c (100%) rename drivers/usb/gadget/{ => function}/f_mtp.h (100%) rename drivers/usb/gadget/{ => function}/f_ptp.c (100%) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 386ed6343e33..598a67d6ba05 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -10,10 +10,3 @@ libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ - -usb_f_mtp-y := f_mtp.o -obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o -usb_f_ptp-y := f_ptp.o -obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o -usb_f_audio_source-y := f_audio_source.o -obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index bd7def576955..cbfd09c183ed 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -44,3 +44,9 @@ usb_f_hid-y := f_hid.o obj-$(CONFIG_USB_F_HID) += usb_f_hid.o usb_f_printer-y := f_printer.o obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o +usb_f_mtp-y := f_mtp.o +obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o +usb_f_ptp-y := f_ptp.o +obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o +usb_f_audio_source-y := f_audio_source.o +obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c similarity index 100% rename from drivers/usb/gadget/f_audio_source.c rename to drivers/usb/gadget/function/f_audio_source.c diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c similarity index 100% rename from drivers/usb/gadget/f_mtp.c rename to drivers/usb/gadget/function/f_mtp.c diff --git a/drivers/usb/gadget/f_mtp.h b/drivers/usb/gadget/function/f_mtp.h similarity index 100% rename from drivers/usb/gadget/f_mtp.h rename to drivers/usb/gadget/function/f_mtp.h diff --git a/drivers/usb/gadget/f_ptp.c b/drivers/usb/gadget/function/f_ptp.c similarity index 100% rename from drivers/usb/gadget/f_ptp.c rename to drivers/usb/gadget/function/f_ptp.c From 8cc9024964a7be5899860692991040ee9105324a Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 23 Nov 2014 17:21:22 -0800 Subject: [PATCH 167/475] usb: gadget: Accessory:Migrate to USB_FUNCTION API This patch adds support to use Android accessory gadget function through the DECLARE_USB_FUNCTION_INIT interface. Signed-off-by: Badhri Jagan Sridharan Change-Id: Ib352752d5bc905fa1df9049b53eabf1294930db7 --- drivers/usb/gadget/Kconfig | 12 ++- drivers/usb/gadget/Makefile | 3 + drivers/usb/gadget/f_accessory.c | 153 ++++++++++++++++++++++++++++++- 3 files changed, 164 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f550989173bf..221f2c50d7e2 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -208,6 +208,9 @@ config USB_F_PTP config USB_F_AUDIO_SRC tristate +config USB_F_ACC + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -394,9 +397,16 @@ config USB_CONFIGFS_F_PTP help USB gadget PTP support +config USB_CONFIGFS_F_ACC + boolean "Accessory gadget" + depends on USB_CONFIGFS + select USB_F_ACC + help + USB gadget Accessory support + config USB_CONFIGFS_F_AUDIO_SRC boolean "Audio Source gadget" - depends on USB_CONFIGFS + depends on USB_CONFIGFS && USB_CONFIGFS_F_ACC select USB_F_AUDIO_SRC help USB gadget Audio Source support diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 598a67d6ba05..1b55d76ee049 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -9,4 +9,7 @@ obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o +usb_f_accessory-y := f_accessory.o +obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o + obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 9ffe017bf1cf..d9db5c595b99 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -39,6 +39,10 @@ #include #include +#include +#include + +#define MAX_INST_NAME_LEN 40 #define BULK_BUFFER_SIZE 16384 #define ACC_STRING_SIZE 256 @@ -194,6 +198,11 @@ static struct usb_gadget_strings *acc_strings[] = { /* temporary variable used between acc_open() and acc_gadget_bind() */ static struct acc_dev *_acc_dev; +struct acc_instance { + struct usb_function_instance func_inst; + const char *name; +}; + static inline struct acc_dev *func_to_dev(struct usb_function *f) { return container_of(f, struct acc_dev, function); @@ -775,7 +784,7 @@ static struct hid_driver acc_hid_driver = { .probe = acc_hid_probe, }; -static int acc_ctrlrequest(struct usb_composite_dev *cdev, +int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { struct acc_dev *dev = _acc_dev; @@ -879,9 +888,11 @@ err: w_value, w_index, w_length); return value; } +EXPORT_SYMBOL_GPL(acc_ctrlrequest); static int -acc_function_bind(struct usb_configuration *c, struct usb_function *f) +__acc_function_bind(struct usb_configuration *c, + struct usb_function *f, bool configfs) { struct usb_composite_dev *cdev = c->cdev; struct acc_dev *dev = func_to_dev(f); @@ -890,6 +901,16 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) DBG(cdev, "acc_function_bind dev: %p\n", dev); + if (configfs) { + 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; + } ret = hid_register_driver(&acc_hid_driver); if (ret) return ret; @@ -922,6 +943,17 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; } +static int +acc_function_bind(struct usb_configuration *c, struct usb_function *f) { + return __acc_function_bind(c, f, false); +} + +static int +acc_function_bind_configfs(struct usb_configuration *c, + struct usb_function *f) { + return __acc_function_bind(c, f, true); +} + static void kill_all_hid_devices(struct acc_dev *dev) { @@ -1179,11 +1211,12 @@ err: return ret; } -static void acc_disconnect(void) +void acc_disconnect(void) { /* unregister all HID devices if USB is disconnected */ kill_all_hid_devices(_acc_dev); } +EXPORT_SYMBOL_GPL(acc_disconnect); static void acc_cleanup(void) { @@ -1191,3 +1224,117 @@ static void acc_cleanup(void) kfree(_acc_dev); _acc_dev = NULL; } +static struct acc_instance *to_acc_instance(struct config_item *item) +{ + return container_of(to_config_group(item), struct acc_instance, + func_inst.group); +} + +static void acc_attr_release(struct config_item *item) +{ + struct acc_instance *fi_acc = to_acc_instance(item); + + usb_put_function_instance(&fi_acc->func_inst); +} + +static struct configfs_item_operations acc_item_ops = { + .release = acc_attr_release, +}; + +static struct config_item_type acc_func_type = { + .ct_item_ops = &acc_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct acc_instance *to_fi_acc(struct usb_function_instance *fi) +{ + return container_of(fi, struct acc_instance, func_inst); +} + +static int acc_set_inst_name(struct usb_function_instance *fi, const char *name) +{ + struct acc_instance *fi_acc; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_acc = to_fi_acc(fi); + fi_acc->name = ptr; + return 0; +} + +static void acc_free_inst(struct usb_function_instance *fi) +{ + struct acc_instance *fi_acc; + + fi_acc = to_fi_acc(fi); + kfree(fi_acc->name); + acc_cleanup(); +} + +static struct usb_function_instance *acc_alloc_inst(void) +{ + struct acc_instance *fi_acc; + struct acc_dev *dev; + int err; + + fi_acc = kzalloc(sizeof(*fi_acc), GFP_KERNEL); + if (!fi_acc) + return ERR_PTR(-ENOMEM); + fi_acc->func_inst.set_inst_name = acc_set_inst_name; + fi_acc->func_inst.free_func_inst = acc_free_inst; + + err = acc_setup(); + if (err) { + kfree(fi_acc); + pr_err("Error setting ACCESSORY\n"); + return ERR_PTR(err); + } + + config_group_init_type_name(&fi_acc->func_inst.group, + "", &acc_func_type); + dev = _acc_dev; + return &fi_acc->func_inst; +} + +static void acc_free(struct usb_function *f) +{ +/*NO-OP: no function specific resource allocation in mtp_alloc*/ +} + +int acc_ctrlrequest_configfs(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) { + if (f->config != NULL && f->config->cdev != NULL) + return acc_ctrlrequest(f->config->cdev, ctrl); + else + return -1; +} + +static struct usb_function *acc_alloc(struct usb_function_instance *fi) +{ + struct acc_dev *dev = _acc_dev; + + pr_info("acc_alloc\n"); + + dev->function.name = "accessory"; + dev->function.strings = acc_strings, + dev->function.fs_descriptors = fs_acc_descs; + dev->function.hs_descriptors = hs_acc_descs; + dev->function.bind = acc_function_bind_configfs; + dev->function.unbind = acc_function_unbind; + dev->function.set_alt = acc_function_set_alt; + dev->function.disable = acc_function_disable; + dev->function.free_func = acc_free; + dev->function.setup = acc_ctrlrequest_configfs; + + return &dev->function; +} +DECLARE_USB_FUNCTION_INIT(accessory, acc_alloc_inst, acc_alloc); +MODULE_LICENSE("GPL"); From e1cc1e2a3771d90ac2ec42e3219fae2f2ca1ffc7 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 30 Mar 2015 15:32:22 -0700 Subject: [PATCH 168/475] usb: gadget: Relocate f_accessory 3.18 kernel has reorganized drivers/usb/gadget directory. Moving accessory gadget driver from drivers/usb/gadget to drivers/usb/gadget/function Signed-off-by: Badhri Jagan Sridharan Change-Id: If73c6df0537c4b1f51338ed3b0db817e51f06b4a --- drivers/usb/gadget/Makefile | 3 --- drivers/usb/gadget/function/Makefile | 2 ++ drivers/usb/gadget/{ => function}/f_accessory.c | 0 3 files changed, 2 insertions(+), 3 deletions(-) rename drivers/usb/gadget/{ => function}/f_accessory.c (100%) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1b55d76ee049..598a67d6ba05 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -9,7 +9,4 @@ obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o -usb_f_accessory-y := f_accessory.o -obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o - obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index cbfd09c183ed..1cd544beef63 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -50,3 +50,5 @@ usb_f_ptp-y := f_ptp.o obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o usb_f_audio_source-y := f_audio_source.o obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o +usb_f_accessory-y := f_accessory.o +obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c similarity index 100% rename from drivers/usb/gadget/f_accessory.c rename to drivers/usb/gadget/function/f_accessory.c From 1c9137c8c53cf4af32f33ac1d8d2c9675009991b Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 6 May 2015 13:40:15 -0700 Subject: [PATCH 169/475] usb: gadget: Do not disconnect unregistered dev configfs_composite_unbind sets the gadget data to null. Therefore, add check in disconnect function to make sure that cdev is not NULL. Prints a WARN message if the driver tries to redundantly disconnect a gadget. Signed-off-by: Badhri Jagan Sridharan Change-Id: I248cb7175d0dd9a51c18053dd39475d8b3284f6d --- drivers/usb/gadget/composite.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 8b14c2a13ac5..ffa2a682c04b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1870,6 +1870,12 @@ void composite_disconnect(struct usb_gadget *gadget) struct usb_composite_dev *cdev = get_gadget_data(gadget); unsigned long flags; + if (cdev == NULL) { + WARN(1, "%s: Calling disconnect on a Gadget that is \ + not connected\n", __func__); + return; + } + /* REVISIT: should we have config and device level * disconnect callbacks? */ From e931d151a9e953381f992f6da68386f1858da043 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 14 Jul 2015 15:46:11 -0700 Subject: [PATCH 170/475] usb:gadget:Add "state" attribute to android_device Added a device attribute to android_device to determine USB_GADGET's state Signed-off-by: Badhri Jagan Sridharan Change-Id: I17f8903120df96bf2f4bf441940b53a87b818230 --- drivers/usb/gadget/configfs.c | 66 ++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 38077becbf2c..d0196637b693 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1559,11 +1559,49 @@ static const struct usb_gadget_driver configfs_driver_template = { }, }; +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static ssize_t state_show(struct device *pdev, struct device_attribute *attr, + char *buf) +{ + struct gadget_info *dev = dev_get_drvdata(pdev); + struct usb_composite_dev *cdev; + char *state = "DISCONNECTED"; + unsigned long flags; + + if (!dev) + goto out; + + cdev = &dev->cdev; + + 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); +} + +static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); + +static struct device_attribute *android_usb_attributes[] = { + &dev_attr_state, + NULL +}; +#endif + static struct config_group *gadgets_make( struct config_group *group, const char *name) { struct gadget_info *gi; + struct device_attribute **attrs; + struct device_attribute *attr; + int err; gi = kzalloc(sizeof(*gi), GFP_KERNEL); if (!gi) @@ -1609,14 +1647,33 @@ static struct config_group *gadgets_make( MKDEV(0, 0), NULL, "android0"); if (IS_ERR(android_device)) goto err; + + dev_set_drvdata(android_device, gi); + + attrs = android_usb_attributes; + while ((attr = *attrs++)) { + err = device_create_file(android_device, attr); + if (err) + goto err1; + } #endif if (!gi->composite.gadget_driver.function) - goto err; + goto err1; config_group_init_type_name(&gi->group, name, &gadget_root_type); return &gi->group; + +err1: +#ifdef CONFIG_USB_CONFIGFS_UEVENT + attrs = android_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(android_device, attr); + + device_destroy(android_device->class, + android_device->devt); +#endif err: kfree(gi); return ERR_PTR(-ENOMEM); @@ -1624,8 +1681,15 @@ err: static void gadgets_drop(struct config_group *group, struct config_item *item) { + struct device_attribute **attrs; + struct device_attribute *attr; + config_item_put(item); + #ifdef CONFIG_USB_CONFIGFS_UEVENT + attrs = android_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(android_device, attr); device_destroy(android_device->class, android_device->devt); #endif } From a89affddffb840da8d9d34c553d65dd1739e9ae1 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Sat, 1 Aug 2015 03:26:51 +0530 Subject: [PATCH 171/475] usb: gadget: fix NULL ptr derefer while symlinking PTP func Fix NULL pointer dereference while trying to link PTP function to a gadget configuration without creating MTP function. PTP piggyback on MTP function so make sure we have MTP function created beforehand. Otherwise we run into following kernel panic: ----------------------- [ 70.329957] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 70.330738] pgd = dd8ec000 [ 70.330916] [00000000] *pgd=00000000 [ 70.331663] Internal error: Oops: 805 [#1] SMP THUMB2 [ 70.332155] CPU: 0 PID: 2067 Comm: ln Not tainted 3.18.0-00587-gdfa582e #1 [ 70.332511] task: dd9c92c0 ti: dd822000 task.ti: dd822000 [ 70.333094] PC is at function_alloc_mtp_ptp+0xe/0x68 [ 70.333311] LR is at usb_get_function+0x11/0x1c [ 70.333489] pc : [] lr : [] psr: 60070033 <..snip..> [ 70.384111] 3fc0: bec14ae4 00000004 bec14c0a 00000053 00000004 b6f0422d 00000000 bec14adc [ 70.384369] 3fe0: bec14af8 bec14a98 b6f071f3 b6e8977c 20070010 bec14c0d 00000000 00000000 [ 70.384832] [] (function_alloc_mtp_ptp) from [] (usb_get_function+0x11/0x1c) [ 70.385146] [] (usb_get_function) from [] (config_usb_cfg_link+0x87/0xa8) [ 70.385421] [] (config_usb_cfg_link) from [] (configfs_symlink+0xb7/0x1c8) [ 70.385696] [] (configfs_symlink) from [] (vfs_symlink+0x85/0xc0) [ 70.386010] [] (vfs_symlink) from [] (SyS_symlinkat+0x43/0x70) [ 70.386261] [] (SyS_symlinkat) from [] (ret_fast_syscall+0x1/0x5c) [ 70.386610] Code: eb04 4a0f 6e03 480f (e883) 0005 [ 70.387346] ---[ end trace 8dba7c552e02f8fa ]--- [ 70.387647] Kernel panic - not syncing: Fatal exception [ 70.387980] ---[ end Kernel panic - not syncing: Fatal exception ----------------------- Steps to reproduce the kernel panic: mount -t configfs none /config mkdir /config/usb_gadget/g1 cd /config/usb_gadget/g1 echo 0x18d1 > idVendor echo 0x4e26 > idProduct mkdir strings/0x409 echo 0123459876 > strings/0x409/serialnumber echo Asus > strings/0x409/manufacturer echo Nexus7 > strings/0x409/product mkdir configs/c.1 mkdir configs/c.1/strings/0x409 echo "Conf 1" > configs/c.1/strings/0x409/configuration echo 120 > configs/c.1/MaxPower mkdir functions/ptp.ptp ln -s functions/ptp.ptp configs/c.1/ptp.ptp Also MTP and PTP are mutually exclusive functions so make sure we have only one of it linked to a configuration at a time. Otherwise it opens up another set of bug(s?). Signed-off-by: Amit Pundir --- drivers/usb/gadget/configfs.c | 5 +++++ drivers/usb/gadget/function/f_mtp.c | 18 +++++++++++++++++- drivers/usb/gadget/functions.c | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index d0196637b693..8c48ab4e78ba 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -425,6 +425,11 @@ static int config_usb_cfg_link( } f = usb_get_function(fi); + if (f == NULL) { + /* Are we trying to symlink PTP without MTP function? */ + ret = -EINVAL; /* Invalid Configuration */ + goto out; + } if (IS_ERR(f)) { ret = PTR_ERR(f); goto out; diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index 82f6b2ebaebb..03a61f8b9d48 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -1426,8 +1426,24 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, bool mtp_config) { struct mtp_instance *fi_mtp = to_fi_mtp(fi); - struct mtp_dev *dev = fi_mtp->dev; + struct mtp_dev *dev; + /* + * PTP piggybacks on MTP function so make sure we have + * created MTP function before we associate this PTP + * function with a gadget configuration. + */ + if (fi_mtp->dev == NULL) { + pr_err("Error: Create MTP function before linking" + " PTP function with a gadget configuration\n"); + pr_err("\t1: Delete existing PTP function if any\n"); + pr_err("\t2: Create MTP function\n"); + pr_err("\t3: Create and symlink PTP function" + " with a gadget configuration\n"); + return NULL; + } + + dev = fi_mtp->dev; dev->function.name = DRIVER_NAME; dev->function.strings = mtp_strings; if (mtp_config) { diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c index b13f839e7368..389c1f3d0fee 100644 --- a/drivers/usb/gadget/functions.c +++ b/drivers/usb/gadget/functions.c @@ -58,7 +58,7 @@ struct usb_function *usb_get_function(struct usb_function_instance *fi) struct usb_function *f; f = fi->fd->alloc_func(fi); - if (IS_ERR(f)) + if ((f == NULL) || IS_ERR(f)) return f; f->fi = fi; return f; From ce31762e99a3a75edb10d053dd71ec02bbe7aaa5 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 31 Aug 2015 21:36:07 -0700 Subject: [PATCH 172/475] usb: phy: Dual role sysfs class definition This CL adds a new class to monitor and change dual role usb ports from userspace. The usb phy drivers can register to the dual_role_usb class and expose the capabilities of the ports. The phy drivers can decide on whether a specific attribute can be changed from userspace by choosing to implement the appropriate callback. Cherry-picked from https://android-review.googlesource.com/#/c/167310/ Signed-off-by: Badhri Jagan Sridharan Bug: 21615151 Change-Id: Id1c4aaa97e898264d7006381a7badd029b5d9789 --- .../ABI/testing/sysfs-class-dual-role-usb | 71 +++ drivers/usb/phy/Kconfig | 9 + drivers/usb/phy/Makefile | 2 + drivers/usb/phy/class-dual-role.c | 529 ++++++++++++++++++ include/linux/usb/class-dual-role.h | 128 +++++ 5 files changed, 739 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-dual-role-usb create mode 100644 drivers/usb/phy/class-dual-role.c create mode 100644 include/linux/usb/class-dual-role.h diff --git a/Documentation/ABI/testing/sysfs-class-dual-role-usb b/Documentation/ABI/testing/sysfs-class-dual-role-usb new file mode 100644 index 000000000000..a900fd75430c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-dual-role-usb @@ -0,0 +1,71 @@ +What: /sys/class/dual_role_usb/.../ +Date: June 2015 +Contact: Badhri Jagan Sridharan +Description: + Provide a generic interface to monitor and change + the state of dual role usb ports. The name here + refers to the name mentioned in the + dual_role_phy_desc that is passed while registering + the dual_role_phy_intstance through + devm_dual_role_instance_register. + +What: /sys/class/dual_role_usb/.../supported_modes +Date: June 2015 +Contact: Badhri Jagan Sridharan +Description: + This is a static node, once initialized this + is not expected to change during runtime. "dfp" + refers to "downstream facing port" i.e. port can + only act as host. "ufp" refers to "upstream + facing port" i.e. port can only act as device. + "dfp ufp" refers to "dual role port" i.e. the port + can either be a host port or a device port. + +What: /sys/class/dual_role_usb/.../mode +Date: June 2015 +Contact: Badhri Jagan Sridharan +Description: + The mode node refers to the current mode in which the + port is operating. "dfp" for host ports. "ufp" for device + ports and "none" when cable is not connected. + + On devices where the USB mode is software-controllable, + userspace can change the mode by writing "dfp" or "ufp". + On devices where the USB mode is fixed in hardware, + this attribute is read-only. + +What: /sys/class/dual_role_usb/.../power_role +Date: June 2015 +Contact: Badhri Jagan Sridharan +Description: + The power_role node mentions whether the port + is "sink"ing or "source"ing power. "none" if + they are not connected. + + On devices implementing USB Power Delivery, + userspace can control the power role by writing "sink" or + "source". On devices without USB-PD, this attribute is + read-only. + +What: /sys/class/dual_role_usb/.../data_role +Date: June 2015 +Contact: Badhri Jagan Sridharan +Description: + The data_role node mentions whether the port + is acting as "host" or "device" for USB data connection. + "none" if there is no active data link. + + On devices implementing USB Power Delivery, userspace + can control the data role by writing "host" or "device". + On devices without USB-PD, this attribute is read-only + +What: /sys/class/dual_role_usb/.../powers_vconn +Date: June 2015 +Contact: Badhri Jagan Sridharan +Description: + The powers_vconn node mentions whether the port + is supplying power for VCONN pin. + + On devices with software control of VCONN, + userspace can disable the power supply to VCONN by writing "n", + or enable the power supply by writing "y". diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index d874bbaa4ac4..795485eac7b0 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -221,4 +221,13 @@ config USB_ULPI_VIEWPORT Provides read/write operations to the ULPI phy register set for controllers with a viewport register (e.g. Chipidea/ARC controllers). +config DUAL_ROLE_USB_INTF + bool "Generic DUAL ROLE sysfs interface" + depends on SYSFS && USB_PHY + help + A generic sysfs interface to track and change the state of + dual role usb phys. The usb phy drivers can register to + this interface to expose it capabilities to the userspace + and thereby allowing userspace to change the port mode. + endmenu diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index d5f7f0843352..f7543f3b9943 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -4,6 +4,8 @@ obj-$(CONFIG_USB_PHY) += phy.o obj-$(CONFIG_OF) += of.o obj-$(CONFIG_USB_OTG_WAKELOCK) += otg-wakelock.o +obj-$(CONFIG_DUAL_ROLE_USB_INTF) += class-dual-role.o + # transceiver drivers, keep the list sorted obj-$(CONFIG_AB8500_USB) += phy-ab8500-usb.o diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c new file mode 100644 index 000000000000..ce889dd529cb --- /dev/null +++ b/drivers/usb/phy/class-dual-role.c @@ -0,0 +1,529 @@ +/* + * class-dual-role.c + * + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DUAL_ROLE_NOTIFICATION_TIMEOUT 2000 + +static ssize_t dual_role_store_property(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t dual_role_show_property(struct device *dev, + struct device_attribute *attr, + char *buf); + +#define DUAL_ROLE_ATTR(_name) \ +{ \ + .attr = { .name = #_name }, \ + .show = dual_role_show_property, \ + .store = dual_role_store_property, \ +} + +static struct device_attribute dual_role_attrs[] = { + DUAL_ROLE_ATTR(supported_modes), + DUAL_ROLE_ATTR(mode), + DUAL_ROLE_ATTR(power_role), + DUAL_ROLE_ATTR(data_role), + DUAL_ROLE_ATTR(powers_vconn), +}; + +struct class *dual_role_class; +EXPORT_SYMBOL_GPL(dual_role_class); + +static struct device_type dual_role_dev_type; + +static char *kstrdupcase(const char *str, gfp_t gfp, bool to_upper) +{ + char *ret, *ustr; + + ustr = ret = kmalloc(strlen(str) + 1, gfp); + + if (!ret) + return NULL; + + while (*str) + *ustr++ = to_upper ? toupper(*str++) : tolower(*str++); + + *ustr = 0; + + return ret; +} + +static void dual_role_changed_work(struct work_struct *work) +{ + struct dual_role_phy_instance *dual_role = + container_of(work, struct dual_role_phy_instance, + changed_work); + + dev_dbg(&dual_role->dev, "%s\n", __func__); + kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE); +} + +void dual_role_instance_changed(struct dual_role_phy_instance *dual_role) +{ + dev_dbg(&dual_role->dev, "%s\n", __func__); + pm_wakeup_event(&dual_role->dev, DUAL_ROLE_NOTIFICATION_TIMEOUT); + schedule_work(&dual_role->changed_work); +} +EXPORT_SYMBOL_GPL(dual_role_instance_changed) + +int dual_role_get_property(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + unsigned int *val) +{ + return dual_role->desc->get_property(dual_role, prop, val); +} +EXPORT_SYMBOL_GPL(dual_role_get_property); + +int dual_role_set_property(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val) +{ + if (!dual_role->desc->set_property) + return -ENODEV; + + return dual_role->desc->set_property(dual_role, prop, val); +} +EXPORT_SYMBOL_GPL(dual_role_set_property); + +int dual_role_property_is_writeable(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop) +{ + if (!dual_role->desc->property_is_writeable) + return -ENODEV; + + return dual_role->desc->property_is_writeable(dual_role, prop); +} +EXPORT_SYMBOL_GPL(dual_role_property_is_writeable); + +static void dual_role_dev_release(struct device *dev) +{ + struct dual_role_phy_instance *dual_role = + container_of(dev, struct dual_role_phy_instance, dev); + pr_debug("device: '%s': %s\n", dev_name(dev), __func__); + kfree(dual_role); +} + +static struct dual_role_phy_instance *__must_check +__dual_role_register(struct device *parent, + const struct dual_role_phy_desc *desc) +{ + struct device *dev; + struct dual_role_phy_instance *dual_role; + int rc; + + dual_role = kzalloc(sizeof(*dual_role), GFP_KERNEL); + if (!dual_role) + return ERR_PTR(-ENOMEM); + + dev = &dual_role->dev; + + device_initialize(dev); + + dev->class = dual_role_class; + dev->type = &dual_role_dev_type; + dev->parent = parent; + dev->release = dual_role_dev_release; + dev_set_drvdata(dev, dual_role); + dual_role->desc = desc; + + rc = dev_set_name(dev, "%s", desc->name); + if (rc) + goto dev_set_name_failed; + + INIT_WORK(&dual_role->changed_work, dual_role_changed_work); + + rc = device_init_wakeup(dev, true); + if (rc) + goto wakeup_init_failed; + + rc = device_add(dev); + if (rc) + goto device_add_failed; + + dual_role_instance_changed(dual_role); + + return dual_role; + +device_add_failed: + device_init_wakeup(dev, false); +wakeup_init_failed: +dev_set_name_failed: + put_device(dev); + kfree(dual_role); + + return ERR_PTR(rc); +} + +static void dual_role_instance_unregister(struct dual_role_phy_instance + *dual_role) +{ + cancel_work_sync(&dual_role->changed_work); + device_init_wakeup(&dual_role->dev, false); + device_unregister(&dual_role->dev); +} + +static void devm_dual_role_release(struct device *dev, void *res) +{ + struct dual_role_phy_instance **dual_role = res; + + dual_role_instance_unregister(*dual_role); +} + +struct dual_role_phy_instance *__must_check +devm_dual_role_instance_register(struct device *parent, + const struct dual_role_phy_desc *desc) +{ + struct dual_role_phy_instance **ptr, *dual_role; + + ptr = devres_alloc(devm_dual_role_release, sizeof(*ptr), GFP_KERNEL); + + if (!ptr) + return ERR_PTR(-ENOMEM); + dual_role = __dual_role_register(parent, desc); + if (IS_ERR(dual_role)) { + devres_free(ptr); + } else { + *ptr = dual_role; + devres_add(parent, ptr); + } + return dual_role; +} +EXPORT_SYMBOL_GPL(devm_dual_role_instance_register); + +static int devm_dual_role_match(struct device *dev, void *res, void *data) +{ + struct dual_role_phy_instance **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +void devm_dual_role_instance_unregister(struct device *dev, + struct dual_role_phy_instance + *dual_role) +{ + int rc; + + rc = devres_release(dev, devm_dual_role_release, + devm_dual_role_match, dual_role); + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_dual_role_instance_unregister); + +void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role) +{ + return dual_role->drv_data; +} +EXPORT_SYMBOL_GPL(dual_role_get_drvdata); + +/***************** Device attribute functions **************************/ + +/* port type */ +static char *supported_modes_text[] = { + "ufp dfp", "dfp", "ufp" +}; + +/* current mode */ +static char *mode_text[] = { + "ufp", "dfp", "none" +}; + +/* Power role */ +static char *pr_text[] = { + "source", "sink", "none" +}; + +/* Data role */ +static char *dr_text[] = { + "host", "device", "none" +}; + +/* Vconn supply */ +static char *vconn_supply_text[] = { + "n", "y" +}; + +static ssize_t dual_role_show_property(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); + const ptrdiff_t off = attr - dual_role_attrs; + unsigned int value; + + if (off == DUAL_ROLE_PROP_SUPPORTED_MODES) { + value = dual_role->desc->supported_modes; + } else { + ret = dual_role_get_property(dual_role, off, &value); + + if (ret < 0) { + if (ret == -ENODATA) + dev_dbg(dev, + "driver has no data for `%s' property\n", + attr->attr.name); + else if (ret != -ENODEV) + dev_err(dev, + "driver failed to report `%s' property: %zd\n", + attr->attr.name, ret); + return ret; + } + } + + if (off == DUAL_ROLE_PROP_SUPPORTED_MODES) { + BUILD_BUG_ON(DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL != + ARRAY_SIZE(supported_modes_text)); + if (value < DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL) + return snprintf(buf, PAGE_SIZE, "%s\n", + supported_modes_text[value]); + else + return -EIO; + } else if (off == DUAL_ROLE_PROP_MODE) { + BUILD_BUG_ON(DUAL_ROLE_PROP_MODE_TOTAL != + ARRAY_SIZE(mode_text)); + if (value < DUAL_ROLE_PROP_MODE_TOTAL) + return snprintf(buf, PAGE_SIZE, "%s\n", + mode_text[value]); + else + return -EIO; + } else if (off == DUAL_ROLE_PROP_PR) { + BUILD_BUG_ON(DUAL_ROLE_PROP_PR_TOTAL != ARRAY_SIZE(pr_text)); + if (value < DUAL_ROLE_PROP_PR_TOTAL) + return snprintf(buf, PAGE_SIZE, "%s\n", + pr_text[value]); + else + return -EIO; + } else if (off == DUAL_ROLE_PROP_DR) { + BUILD_BUG_ON(DUAL_ROLE_PROP_DR_TOTAL != ARRAY_SIZE(dr_text)); + if (value < DUAL_ROLE_PROP_DR_TOTAL) + return snprintf(buf, PAGE_SIZE, "%s\n", + dr_text[value]); + else + return -EIO; + } else if (off == DUAL_ROLE_PROP_VCONN_SUPPLY) { + BUILD_BUG_ON(DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL != + ARRAY_SIZE(vconn_supply_text)); + if (value < DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL) + return snprintf(buf, PAGE_SIZE, "%s\n", + vconn_supply_text[value]); + else + return -EIO; + } else + return -EIO; +} + +static ssize_t dual_role_store_property(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t ret; + struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); + const ptrdiff_t off = attr - dual_role_attrs; + unsigned int value; + int total, i; + char *dup_buf, **text_array; + bool result = false; + + dup_buf = kstrdupcase(buf, GFP_KERNEL, false); + switch (off) { + case DUAL_ROLE_PROP_MODE: + total = DUAL_ROLE_PROP_MODE_TOTAL; + text_array = mode_text; + break; + case DUAL_ROLE_PROP_PR: + total = DUAL_ROLE_PROP_PR_TOTAL; + text_array = pr_text; + break; + case DUAL_ROLE_PROP_DR: + total = DUAL_ROLE_PROP_DR_TOTAL; + text_array = dr_text; + break; + case DUAL_ROLE_PROP_VCONN_SUPPLY: + ret = strtobool(dup_buf, &result); + value = result; + if (!ret) + goto setprop; + default: + ret = -EINVAL; + goto error; + } + + for (i = 0; i <= total; i++) { + if (i == total) { + ret = -ENOTSUPP; + goto error; + } + if (!strncmp(*(text_array + i), dup_buf, + strlen(*(text_array + i)))) { + value = i; + break; + } + } + +setprop: + ret = dual_role->desc->set_property(dual_role, off, &value); + +error: + kfree(dup_buf); + + if (ret < 0) + return ret; + + return count; +} + +static umode_t dual_role_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int attrno) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); + umode_t mode = S_IRUSR | S_IRGRP | S_IROTH; + int i; + + if (attrno == DUAL_ROLE_PROP_SUPPORTED_MODES) + return mode; + + for (i = 0; i < dual_role->desc->num_properties; i++) { + int property = dual_role->desc->properties[i]; + + if (property == attrno) { + if (dual_role->desc->property_is_writeable && + dual_role_property_is_writeable(dual_role, property) + > 0) + mode |= S_IWUSR; + + return mode; + } + } + + return 0; +} + +static struct attribute *__dual_role_attrs[ARRAY_SIZE(dual_role_attrs) + 1]; + +static struct attribute_group dual_role_attr_group = { + .attrs = __dual_role_attrs, + .is_visible = dual_role_attr_is_visible, +}; + +static const struct attribute_group *dual_role_attr_groups[] = { + &dual_role_attr_group, + NULL, +}; + +void dual_role_init_attrs(struct device_type *dev_type) +{ + int i; + + dev_type->groups = dual_role_attr_groups; + + for (i = 0; i < ARRAY_SIZE(dual_role_attrs); i++) + __dual_role_attrs[i] = &dual_role_attrs[i].attr; +} + +int dual_role_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct dual_role_phy_instance *dual_role = dev_get_drvdata(dev); + int ret = 0, j; + char *prop_buf; + char *attrname; + + dev_dbg(dev, "uevent\n"); + + if (!dual_role || !dual_role->desc) { + dev_dbg(dev, "No dual_role phy yet\n"); + return ret; + } + + dev_dbg(dev, "DUAL_ROLE_NAME=%s\n", dual_role->desc->name); + + ret = add_uevent_var(env, "DUAL_ROLE_NAME=%s", dual_role->desc->name); + if (ret) + return ret; + + prop_buf = (char *)get_zeroed_page(GFP_KERNEL); + if (!prop_buf) + return -ENOMEM; + + for (j = 0; j < dual_role->desc->num_properties; j++) { + struct device_attribute *attr; + char *line; + + attr = &dual_role_attrs[dual_role->desc->properties[j]]; + + ret = dual_role_show_property(dev, attr, prop_buf); + if (ret == -ENODEV || ret == -ENODATA) { + ret = 0; + continue; + } + + if (ret < 0) + goto out; + line = strnchr(prop_buf, PAGE_SIZE, '\n'); + if (line) + *line = 0; + + attrname = kstrdupcase(attr->attr.name, GFP_KERNEL, true); + if (!attrname) + ret = -ENOMEM; + + dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf); + + ret = add_uevent_var(env, "DUAL_ROLE_%s=%s", attrname, + prop_buf); + kfree(attrname); + if (ret) + goto out; + } + +out: + free_page((unsigned long)prop_buf); + + return ret; +} + +/******************* Module Init ***********************************/ + +static int __init dual_role_class_init(void) +{ + dual_role_class = class_create(THIS_MODULE, "dual_role_usb"); + + if (IS_ERR(dual_role_class)) + return PTR_ERR(dual_role_class); + + dual_role_class->dev_uevent = dual_role_uevent; + dual_role_init_attrs(&dual_role_dev_type); + + return 0; +} + +static void __exit dual_role_class_exit(void) +{ + class_destroy(dual_role_class); +} + +subsys_initcall(dual_role_class_init); +module_exit(dual_role_class_exit); diff --git a/include/linux/usb/class-dual-role.h b/include/linux/usb/class-dual-role.h new file mode 100644 index 000000000000..af42ed34944a --- /dev/null +++ b/include/linux/usb/class-dual-role.h @@ -0,0 +1,128 @@ +#ifndef __LINUX_CLASS_DUAL_ROLE_H__ +#define __LINUX_CLASS_DUAL_ROLE_H__ + +#include +#include +#include + +struct device; + +enum dual_role_supported_modes { + DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP = 0, + DUAL_ROLE_SUPPORTED_MODES_DFP, + DUAL_ROLE_SUPPORTED_MODES_UFP, +/*The following should be the last element*/ + DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL, +}; + +enum { + DUAL_ROLE_PROP_MODE_UFP = 0, + DUAL_ROLE_PROP_MODE_DFP, + DUAL_ROLE_PROP_MODE_NONE, +/*The following should be the last element*/ + DUAL_ROLE_PROP_MODE_TOTAL, +}; + +enum { + DUAL_ROLE_PROP_PR_SRC = 0, + DUAL_ROLE_PROP_PR_SNK, + DUAL_ROLE_PROP_PR_NONE, +/*The following should be the last element*/ + DUAL_ROLE_PROP_PR_TOTAL, + +}; + +enum { + DUAL_ROLE_PROP_DR_HOST = 0, + DUAL_ROLE_PROP_DR_DEVICE, + DUAL_ROLE_PROP_DR_NONE, +/*The following should be the last element*/ + DUAL_ROLE_PROP_DR_TOTAL, +}; + +enum { + DUAL_ROLE_PROP_VCONN_SUPPLY_NO = 0, + DUAL_ROLE_PROP_VCONN_SUPPLY_YES, +/*The following should be the last element*/ + DUAL_ROLE_PROP_VCONN_SUPPLY_TOTAL, +}; + +enum dual_role_property { + DUAL_ROLE_PROP_SUPPORTED_MODES = 0, + DUAL_ROLE_PROP_MODE, + DUAL_ROLE_PROP_PR, + DUAL_ROLE_PROP_DR, + DUAL_ROLE_PROP_VCONN_SUPPLY, +}; + +struct dual_role_phy_instance; + +/* Description of typec port */ +struct dual_role_phy_desc { + /* /sys/class/dual_role_usb// */ + const char *name; + enum dual_role_supported_modes supported_modes; + enum dual_role_property *properties; + size_t num_properties; + + /* Callback for "cat /sys/class/dual_role_usb//" */ + int (*get_property)(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + unsigned int *val); + /* Callback for "echo > + * /sys/class/dual_role_usb//" */ + int (*set_property)(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val); + /* Decides whether userspace can change a specific property */ + int (*property_is_writeable)(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop); +}; + +struct dual_role_phy_instance { + const struct dual_role_phy_desc *desc; + + /* Driver private data */ + void *drv_data; + + struct device dev; + struct work_struct changed_work; +}; + +#if IS_ENABLED(CONFIG_DUAL_ROLE_USB_INTF) +extern void dual_role_instance_changed(struct dual_role_phy_instance + *dual_role); +extern struct dual_role_phy_instance *__must_check +devm_dual_role_instance_register(struct device *parent, + const struct dual_role_phy_desc *desc); +extern void devm_dual_role_instance_unregister(struct device *dev, + struct dual_role_phy_instance + *dual_role); +extern int dual_role_get_property(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + unsigned int *val); +extern int dual_role_set_property(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, + const unsigned int *val); +extern int dual_role_property_is_writeable(struct dual_role_phy_instance + *dual_role, + enum dual_role_property prop); +extern void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role); +#else /* CONFIG_DUAL_ROLE_USB_INTF */ +static void dual_role_instance_changed(struct dual_role_phy_instance + *dual_role){} +static struct dual_role_phy_instance *__must_check +devm_dual_role_instance_register(struct device *parent, + const struct dual_role_phy_desc *desc) +{ + return ERR_PTR(-ENOSYS); +} +static void devm_dual_role_instance_unregister(struct device *dev, + struct dual_role_phy_instance + *dual_role){} +static void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role) +{ + return ERR_PTR(-ENOSYS); +} +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ +#endif /* __LINUX_CLASS_DUAL_ROLE_H__ */ From b9efd37bc8c01458a81d5b142e4b2259465a3c51 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Wed, 2 Sep 2015 16:38:31 +0530 Subject: [PATCH 173/475] usb: phy: fix dual role sysfs build if kernel modules are supported Add a missing ";" after EXPORT_SYMBOL() otherwise we run into following build error if Kernel Modules are supported: ---------- CC drivers/usb/phy/class-dual-role.o drivers/usb/phy/class-dual-role.c:91:1: error: expected ',' or ';' before 'int' int dual_role_get_property(struct dual_role_phy_instance *dual_role, ^ make[3]: *** [drivers/usb/phy/class-dual-role.o] Error 1 ---------- Signed-off-by: Amit Pundir --- drivers/usb/phy/class-dual-role.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c index ce889dd529cb..51fcb545a9d5 100644 --- a/drivers/usb/phy/class-dual-role.c +++ b/drivers/usb/phy/class-dual-role.c @@ -86,7 +86,7 @@ void dual_role_instance_changed(struct dual_role_phy_instance *dual_role) pm_wakeup_event(&dual_role->dev, DUAL_ROLE_NOTIFICATION_TIMEOUT); schedule_work(&dual_role->changed_work); } -EXPORT_SYMBOL_GPL(dual_role_instance_changed) +EXPORT_SYMBOL_GPL(dual_role_instance_changed); int dual_role_get_property(struct dual_role_phy_instance *dual_role, enum dual_role_property prop, From de9e90c21eb1f9b93dbfb2cd67be38c20fb6c102 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 9 Aug 2015 15:12:50 -0700 Subject: [PATCH 174/475] usb: gadget: Add device attribute to determine gadget state Android frameworks (UsbDeviceManager) relies on gadget state exported through device attributes. This CL adds the device attribute to export USB gadget state. Change-Id: Id0391810d75b58c579610fbec6e37ab22f28886d Signed-off-by: Badhri Jagan Sridharan --- drivers/usb/gadget/configfs.c | 85 +++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 8c48ab4e78ba..542d60792e2c 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1597,6 +1597,54 @@ static struct device_attribute *android_usb_attributes[] = { &dev_attr_state, NULL }; + +static int android_device_create(struct gadget_info *gi) +{ + struct device_attribute **attrs; + struct device_attribute *attr; + + INIT_WORK(&gi->work, android_work); + android_device = device_create(android_class, NULL, + MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(android_device)) + return PTR_ERR(android_device); + + dev_set_drvdata(android_device, gi); + + attrs = android_usb_attributes; + while ((attr = *attrs++)) { + int err; + + err = device_create_file(android_device, attr); + if (err) { + device_destroy(android_device->class, + android_device->devt); + return err; + } + } + + return 0; +} + +static void android_device_destroy(void) +{ + struct device_attribute **attrs; + struct device_attribute *attr; + + attrs = android_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(android_device, attr); + device_destroy(android_device->class, android_device->devt); +} +#else +static inline int android_device_create(struct gadget_info *gi) +{ + return 0; +} + +static inline void android_device_destroy(void) +{ +} #endif static struct config_group *gadgets_make( @@ -1646,39 +1694,16 @@ static struct config_group *gadgets_make( gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); gi->composite.name = gi->composite.gadget_driver.function; -#ifdef CONFIG_USB_CONFIGFS_UEVENT - INIT_WORK(&gi->work, android_work); - android_device = device_create(android_class, NULL, - MKDEV(0, 0), NULL, "android0"); - if (IS_ERR(android_device)) + if (!gi->composite.gadget_driver.function) goto err; - dev_set_drvdata(android_device, gi); - - attrs = android_usb_attributes; - while ((attr = *attrs++)) { - err = device_create_file(android_device, attr); - if (err) - goto err1; - } -#endif - - if (!gi->composite.gadget_driver.function) - goto err1; + if (android_device_create(gi) < 0) + goto err; config_group_init_type_name(&gi->group, name, &gadget_root_type); return &gi->group; -err1: -#ifdef CONFIG_USB_CONFIGFS_UEVENT - attrs = android_usb_attributes; - while ((attr = *attrs++)) - device_remove_file(android_device, attr); - - device_destroy(android_device->class, - android_device->devt); -#endif err: kfree(gi); return ERR_PTR(-ENOMEM); @@ -1690,13 +1715,7 @@ static void gadgets_drop(struct config_group *group, struct config_item *item) struct device_attribute *attr; config_item_put(item); - -#ifdef CONFIG_USB_CONFIGFS_UEVENT - attrs = android_usb_attributes; - while ((attr = *attrs++)) - device_remove_file(android_device, attr); - device_destroy(android_device->class, android_device->devt); -#endif + android_device_destroy(); } static struct configfs_group_operations gadgets_ops = { From 06047350fc1c0dafb9e39af6b2428dad9a445005 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 2 Sep 2015 22:49:10 -0700 Subject: [PATCH 175/475] ANDROID: usb: gadget: create F_midi device Android frameworks relies on the alsa config reported by the f_midi device. Signed-off-by: Badhri Jagan Sridharan Change-Id: I0695e00b166fd953f50acea93802245b0d5a5240 --- drivers/usb/gadget/function/f_midi.c | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 898a570319f1..8a0e7f988d25 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1040,6 +1040,65 @@ static void f_midi_free_inst(struct usb_function_instance *f) kfree(opts); } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +extern struct device *create_function_device(char *name); +static ssize_t alsa_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_function_instance *fi_midi = dev_get_drvdata(dev); + struct f_midi *midi; + + if (!fi_midi->f) + dev_warn(dev, "f_midi: function not set\n"); + + if (fi_midi && fi_midi->f) { + midi = func_to_midi(fi_midi->f); + if (midi->rmidi && midi->rmidi->card) + return sprintf(buf, "%d %d\n", + midi->rmidi->card->number, midi->rmidi->device); + } + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", -1, -1); +} + +static DEVICE_ATTR(alsa, S_IRUGO, alsa_show, NULL); + +static struct device_attribute *alsa_function_attributes[] = { + &dev_attr_alsa, + NULL +}; + +static int create_alsa_device(struct usb_function_instance *fi) +{ + struct device *dev; + struct device_attribute **attrs; + struct device_attribute *attr; + int err = 0; + + dev = create_function_device("f_midi"); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + attrs = alsa_function_attributes; + if (attrs) { + while ((attr = *attrs++) && !err) + err = device_create_file(dev, attr); + if (err) { + device_destroy(dev->class, dev->devt); + return -EINVAL; + } + } + dev_set_drvdata(dev, fi); + return 0; +} +#else +static int create_alsa_device(struct usb_function_instance *fi) +{ + return 0; +} +#endif + static struct usb_function_instance *f_midi_alloc_inst(void) { struct f_midi_opts *opts; @@ -1057,6 +1116,11 @@ static struct usb_function_instance *f_midi_alloc_inst(void) opts->in_ports = 1; opts->out_ports = 1; + if (create_alsa_device(&opts->func_inst)) { + kfree(opts); + return ERR_PTR(-ENODEV); + } + config_group_init_type_name(&opts->func_inst.group, "", &midi_func_type); @@ -1158,6 +1222,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) midi->func.disable = f_midi_disable; midi->func.free_func = f_midi_free; + fi->f = &midi->func; return &midi->func; setup_fail: From fcabd00b4177edfcfd17606b268e10602267ba86 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 6 Oct 2015 20:53:27 +0530 Subject: [PATCH 176/475] usb: gadget: configfs: handle gadget reset request for android There is this new mandatory UDC->reset API in v3.18+ kernels, commit ef979a26 "usb: gadget: add reset API at usb_gadget_driver". Let android_disconnect handle that for Android, similar to how composite_disconnect is handling the generic ConfigFS gadget reset request. Signed-off-by: Amit Pundir --- drivers/usb/gadget/configfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 542d60792e2c..ac4c641eb20b 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1548,6 +1548,7 @@ static const struct usb_gadget_driver configfs_driver_template = { .unbind = configfs_composite_unbind, #ifdef CONFIG_USB_CONFIGFS_UEVENT .setup = android_setup, + .reset = android_disconnect, .disconnect = android_disconnect, #else .setup = composite_setup, From c886eb3e71d67ad2e1231158e44d3880e3821275 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 30 Oct 2015 03:00:20 +0530 Subject: [PATCH 177/475] usb: gadget: build audio_source function only if SND is enabled Also select SND_PCM while building f_audio_source otherwise we run into following build error: LD init/built-in.o drivers/built-in.o: In function `audio_data_complete': /linaro/android/kernel/linaro-android/drivers/usb/gadget/function/f_audio_source.c:458: undefined reference to `snd_pcm_period_elapsed' drivers/built-in.o: In function `audio_pcm_hw_free': /linaro/android/kernel/linaro-android/drivers/usb/gadget/function/f_audio_source.c:770: undefined reference to `snd_pcm_lib_free_vmalloc_buffer' drivers/built-in.o: In function `snd_pcm_lib_alloc_vmalloc_buffer': /linaro/android/kernel/linaro-android/include/sound/pcm.h:1179: undefined reference to `_snd_pcm_lib_alloc_vmalloc_buffer' drivers/built-in.o: In function `audio_pcm_open': /linaro/android/kernel/linaro-android/drivers/usb/gadget/function/f_audio_source.c:734: undefined reference to `snd_pcm_limit_hw_rates' drivers/built-in.o: In function `snd_card_setup': /linaro/android/kernel/linaro-android/drivers/usb/gadget/function/f_audio_source.c:888: undefined reference to `snd_pcm_new' /linaro/android/kernel/linaro-android/drivers/usb/gadget/function/f_audio_source.c:898: undefined reference to `snd_pcm_set_ops' /linaro/android/kernel/linaro-android/drivers/usb/gadget/function/f_audio_source.c:899: undefined reference to `snd_pcm_lib_preallocate_pages_for_all' drivers/built-in.o:(.data+0x1fd28): undefined reference to `snd_pcm_lib_ioctl' make: *** [vmlinux] Error 1 Signed-off-by: Amit Pundir --- drivers/usb/gadget/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 221f2c50d7e2..5cf6802b02ae 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -407,6 +407,8 @@ config USB_CONFIGFS_F_ACC config USB_CONFIGFS_F_AUDIO_SRC boolean "Audio Source gadget" depends on USB_CONFIGFS && USB_CONFIGFS_F_ACC + depends on SND + select SND_PCM select USB_F_AUDIO_SRC help USB gadget Audio Source support From 33975a41d921c21161b7d5075e84cd16e59ad153 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 25 Aug 2015 13:09:31 +0530 Subject: [PATCH 178/475] usb: gadget: cleanup: fix unused variable and function warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused variables and functions to fix following build warnings: CC drivers/usb/gadget/configfs.o drivers/usb/gadget/configfs.c: In function ‘gadgets_make’: drivers/usb/gadget/configfs.c:1710:6: warning: unused variable ‘err’ [-Wunused-variable] int err; ^ drivers/usb/gadget/configfs.c:1709:27: warning: unused variable ‘attr’ [-Wunused-variable] struct device_attribute *attr; ^ drivers/usb/gadget/configfs.c:1708:28: warning: unused variable ‘attrs’ [-Wunused-variable] struct device_attribute **attrs; ^ drivers/usb/gadget/configfs.c: In function ‘gadgets_drop’: drivers/usb/gadget/configfs.c:1774:27: warning: unused variable ‘attr’ [-Wunused-variable] struct device_attribute *attr; ^ drivers/usb/gadget/configfs.c:1773:28: warning: unused variable ‘attrs’ [-Wunused-variable] struct device_attribute **attrs; ^ ... CC drivers/usb/gadget/function/f_mtp.o drivers/usb/gadget/function/f_mtp.c:1219:12: warning: ‘mtp_bind_config’ defined but not used [-Wunused-function] static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) ^ drivers/usb/gadget/function/f_mtp.c:1300:12: warning: ‘mtp_setup’ defined but not used [-Wunused-function] static int mtp_setup(void) ^ ... CC drivers/usb/gadget/function/f_accessory.o drivers/usb/gadget/function/f_accessory.c:969:1: warning: ‘acc_function_bind’ defined but not used [-Wunused-function] acc_function_bind(struct usb_configuration *c, struct usb_function *f) { ^ drivers/usb/gadget/function/f_accessory.c:1172:12: warning: ‘acc_bind_config’ defined but not used [-Wunused-function] static int acc_bind_config(struct usb_configuration *c) ^ Signed-off-by: Amit Pundir --- drivers/usb/gadget/configfs.c | 6 ---- drivers/usb/gadget/function/f_accessory.c | 34 -------------------- drivers/usb/gadget/function/f_mtp.c | 39 ----------------------- 3 files changed, 79 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index ac4c641eb20b..b27ce0747c18 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1653,9 +1653,6 @@ static struct config_group *gadgets_make( const char *name) { struct gadget_info *gi; - struct device_attribute **attrs; - struct device_attribute *attr; - int err; gi = kzalloc(sizeof(*gi), GFP_KERNEL); if (!gi) @@ -1712,9 +1709,6 @@ err: static void gadgets_drop(struct config_group *group, struct config_item *item) { - struct device_attribute **attrs; - struct device_attribute *attr; - config_item_put(item); android_device_destroy(); } diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index d9db5c595b99..07732395eecb 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -943,11 +943,6 @@ __acc_function_bind(struct usb_configuration *c, return 0; } -static int -acc_function_bind(struct usb_configuration *c, struct usb_function *f) { - return __acc_function_bind(c, f, false); -} - static int acc_function_bind_configfs(struct usb_configuration *c, struct usb_function *f) { @@ -1147,35 +1142,6 @@ static void acc_function_disable(struct usb_function *f) 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.fs_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; diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index 03a61f8b9d48..aec7b8d61fe7 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -1216,40 +1216,6 @@ static void mtp_function_disable(struct usb_function *f) 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 = DRIVER_NAME; - dev->function.strings = mtp_strings; - if (ptp_config) { - dev->function.fs_descriptors = fs_ptp_descs; - dev->function.hs_descriptors = hs_ptp_descs; - } else { - dev->function.fs_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(struct mtp_instance *fi_mtp) { struct mtp_dev *dev; @@ -1297,11 +1263,6 @@ err1: return ret; } -static int mtp_setup(void) -{ - return __mtp_setup(NULL); -} - static int mtp_setup_configfs(struct mtp_instance *fi_mtp) { return __mtp_setup(fi_mtp); From 561085a191a24e070c9eea369be47fcc97688e08 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Wed, 30 Dec 2015 03:05:44 +0530 Subject: [PATCH 179/475] usb: gadget: rndis: use %z format specifier for size_t Use '%z' format specifier for sizeof operator instead of '%u' to fix build warnings like: warning: format '%u' expects type 'unsigned int', but argument 3 has type 'long unsigned int' Signed-off-by: Amit Pundir --- drivers/usb/gadget/function/rndis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index c3ecff8aff8a..16ae1d28daf6 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -1104,7 +1104,7 @@ int rndis_rm_hdr(struct gether *port, } if (skb->len < sizeof *hdr) { - pr_err("invalid rndis pkt: skblen:%u hdr_len:%u", + pr_err("invalid rndis pkt: skblen:%u hdr_len:%zu", skb->len, sizeof *hdr); dev_kfree_skb_any(skb); return -EINVAL; From bbc2ad37fd8757c6347d23d5310b5437b1b25ae9 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 21 Dec 2015 14:15:58 +0530 Subject: [PATCH 180/475] usb: gadget: rndis: fix broken data aggregation build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare "cdev" to fix broken AOSP commit "RNDIS: Add Data aggregation (multi packet) support", otherwise we run into following build failure: CC drivers/usb/gadget/function/f_rndis.o drivers/usb/gadget/function/f_rndis.c: In function ‘rndis_command_complete’: drivers/usb/gadget/function/f_rndis.c:479:3: error: ‘cdev’ undeclared (first use in this function) drivers/usb/gadget/function/f_rndis.c:479:3: note: each undeclared identifier is reported only once for each function it appears in make[4]: *** [drivers/usb/gadget/function/f_rndis.o] Error 1 Signed-off-by: Amit Pundir --- drivers/usb/gadget/function/f_rndis.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 2758522180f3..2bc1197074a8 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -459,6 +459,7 @@ static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) { struct f_rndis *rndis = req->context; + struct usb_composite_dev *cdev = rndis->port.func.config->cdev; int status; rndis_init_msg_type *buf; From 78263aa46bc28671f62d58c55205166648e93518 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 21 Dec 2015 14:03:15 +0530 Subject: [PATCH 181/475] usb: gadget: rndis: fix broken build for 4.4 Use rndis_params instead of configNr to align with changes from mainline commit 83210e59ee15 "usb: gadget: rndis: use rndis_params instead of configNr". Signed-off-by: Amit Pundir --- drivers/usb/gadget/function/f_rndis.c | 2 +- drivers/usb/gadget/function/rndis.c | 8 ++++---- drivers/usb/gadget/function/rndis.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 2bc1197074a8..3a28d8ac3b3d 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -819,7 +819,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); rndis_set_host_mac(rndis->params, rndis->ethaddr); - rndis_set_max_pkt_xfer(rndis->config, rndis_ul_max_pkt_per_xfer); + rndis_set_max_pkt_xfer(rndis->params, rndis_ul_max_pkt_per_xfer); if (rndis->manufacturer && rndis->vendorID && rndis_set_param_vendor(rndis->params, rndis->vendorID, diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 16ae1d28daf6..2ec7171b3f04 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -695,8 +695,8 @@ static int rndis_reset_response(struct rndis_params *params, u8 *xbuf; /* drain the response queue */ - while ((xbuf = rndis_get_next_response(configNr, &length))) - rndis_free_response(configNr, xbuf); + while ((xbuf = rndis_get_next_response(params, &length))) + rndis_free_response(params, xbuf); r = rndis_add_response(params, sizeof(rndis_reset_cmplt_type)); if (!r) @@ -1008,11 +1008,11 @@ int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed) } EXPORT_SYMBOL_GPL(rndis_set_param_medium); -void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer) +void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer) { pr_debug("%s:\n", __func__); - rndis_per_dev_params[configNr].max_pkt_per_xfer = max_pkt_per_xfer; + params->max_pkt_per_xfer = max_pkt_per_xfer; } void rndis_add_hdr(struct sk_buff *skb) diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index 2b92b745d292..310cac3f088e 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -207,7 +207,7 @@ int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, const char *vendorDescr); int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed); -void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer); +void rndis_set_max_pkt_xfer(struct rndis_params *params, u8 max_pkt_per_xfer); void rndis_add_hdr(struct sk_buff *skb); int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, struct sk_buff_head *list); From a6a18dc18a55798980383402051fabcca96754ee Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 8 Jan 2016 19:36:02 +0530 Subject: [PATCH 182/475] usb: gadget: u_ether: use %z format specifier for size_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use '%zd' format specifier for size_t type instead of '%d' to fix build warnings like: drivers/usb/gadget/function/u_ether.c: In function ‘rx_submit’: drivers/usb/gadget/function/u_ether.c:244:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘size_t’ [-Wformat=] DBG(dev, "%s: size: %d\n", __func__, size); ^ Signed-off-by: Amit Pundir --- drivers/usb/gadget/function/u_ether.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 565cde9fbcf1..76b25445c6ad 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -246,7 +246,7 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) if (dev->port_usb->is_fixed) size = max_t(size_t, size, dev->port_usb->fixed_out_len); - DBG(dev, "%s: size: %d\n", __func__, size); + DBG(dev, "%s: size: %zd\n", __func__, size); skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags); if (skb == NULL) { DBG(dev, "no rx skb\n"); From daafe226f73674d5246a642c2399b8e81afac540 Mon Sep 17 00:00:00 2001 From: keunyoung Date: Wed, 29 Jan 2014 12:41:50 -0800 Subject: [PATCH 183/475] fix false disconnect due to a signal sent to the reading process - In the current implementation, when a signal is sent to the reading process, read is cancelled by calling usb_ep_dequeue, which lead into calling acc_complete_out with ECONNRESET, but the current logic treats it as disconnection, which makes the device inaccessible until cable is actually disconnected. - The fix calls disconnect only when ESHUTDOWN error is passed. - If data has already arrived while trying cancelling, the data is marked as available, and it will be read out on the next read. This is necessary as USB bulk is assumed to guarantee no data loss. Signed-off-by: keunyoung --- drivers/usb/gadget/function/f_accessory.c | 32 +++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index 07732395eecb..1be93a7ca4a1 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -270,8 +270,10 @@ static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) { struct acc_dev *dev = _acc_dev; - if (req->status != 0) + if (req->status == -ESHUTDOWN) { + pr_debug("acc_complete_in set disconnected"); acc_set_disconnected(dev); + } req_put(dev, &dev->tx_idle, req); @@ -283,8 +285,10 @@ static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) struct acc_dev *dev = _acc_dev; dev->rx_done = 1; - if (req->status != 0) + if (req->status == -ESHUTDOWN) { + pr_debug("acc_complete_out set disconnected"); acc_set_disconnected(dev); + } wake_up(&dev->read_wq); } @@ -567,8 +571,10 @@ static ssize_t acc_read(struct file *fp, char __user *buf, pr_debug("acc_read(%zu)\n", count); - if (dev->disconnected) + if (dev->disconnected) { + pr_debug("acc_read disconnected"); return -ENODEV; + } if (count > BULK_BUFFER_SIZE) count = BULK_BUFFER_SIZE; @@ -581,6 +587,12 @@ static ssize_t acc_read(struct file *fp, char __user *buf, goto done; } + if (dev->rx_done) { + // last req cancelled. try to get it. + req = dev->rx_req[0]; + goto copy_data; + } + requeue_req: /* queue a request */ req = dev->rx_req[0]; @@ -598,9 +610,17 @@ requeue_req: ret = wait_event_interruptible(dev->read_wq, dev->rx_done); if (ret < 0) { r = ret; - usb_ep_dequeue(dev->ep_out, req); + ret = usb_ep_dequeue(dev->ep_out, req); + if (ret != 0) { + // cancel failed. There can be a data already received. + // it will be retrieved in the next read. + pr_debug("acc_read: cancelling failed %d", ret); + } goto done; } + +copy_data: + dev->rx_done = 0; if (dev->online) { /* If we got a 0-len packet, throw it back and try again. */ if (req->actual == 0) @@ -630,8 +650,10 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, pr_debug("acc_write(%zu)\n", count); - if (!dev->online || dev->disconnected) + if (!dev->online || dev->disconnected) { + pr_debug("acc_write disconnected or not online"); return -ENODEV; + } while (count > 0) { if (!dev->online) { From e9c1a826c0e85907571a9ce47c7e13c8accc66ec 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 184/475] 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 9353184d730d..ce01364a96e3 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -17,3 +17,7 @@ config SHARP_PARAM config SHARP_SCOOP bool + +config FIQ_GLUE + bool + select FIQ diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 27f23b15b1ea..04aca896b338 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 e7b792f79fbf0d680473ce3854e0ecc23457c9e0 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Sat, 5 Jun 2010 17:36:24 -0700 Subject: [PATCH 185/475] 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 ce01364a96e3..992d4046bb8a 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -21,3 +21,49 @@ config SHARP_SCOOP config FIQ_GLUE bool select FIQ + +config FIQ_DEBUGGER + bool "FIQ Mode Serial Debugger" + select FIQ + select FIQ_GLUE + default n + help + The FIQ serial debugger can accept commands even when the + kernel is unresponsive due to being stuck with interrupts + disabled. + + +config FIQ_DEBUGGER_NO_SLEEP + bool "Keep serial debugger active" + depends on FIQ_DEBUGGER + default n + help + Enables the serial debugger at boot. Passing + fiq_debugger.no_sleep on the kernel commandline will + override this config option. + +config FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON + bool "Don't disable wakeup IRQ when debugger is active" + depends on FIQ_DEBUGGER + default n + help + Don't disable the wakeup irq when enabling the uart clock. This will + cause extra interrupts, but it makes the serial debugger usable with + on some MSM radio builds that ignore the uart clock request in power + collapse. + +config FIQ_DEBUGGER_CONSOLE + bool "Console on FIQ Serial Debugger port" + depends on FIQ_DEBUGGER + default n + help + Enables a console so that printk messages are displayed on + the debugger serial port as the occur. + +config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE + bool "Put the FIQ debugger into console mode by default" + depends on FIQ_DEBUGGER_CONSOLE + default n + help + If enabled, this puts the fiq debugger into console mode by default. + Otherwise, the fiq debugger will start out in debug mode. diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 04aca896b338..707dcdf629d0 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 dcccb8f21c9df1016797f98a5fb595a23db0b3f5 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Mar 2012 16:28:45 -0700 Subject: [PATCH 186/475] 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 61b0d5a3c159d2ad9d9acdb48c68fcab5d0046f0 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Mar 2012 16:29:47 -0700 Subject: [PATCH 187/475] 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 7c4ff13273a070cdfaa01ca6883d55af7333f2ed Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 15 Mar 2012 12:57:20 -0700 Subject: [PATCH 188/475] 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 35d99e56465d412e1bf707c31363650506bd15fa Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Mar 2012 19:23:29 -0700 Subject: [PATCH 189/475] 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 c70fe44edf89b1d70b96540608cac56f81ff5667 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Mar 2012 19:26:53 -0700 Subject: [PATCH 190/475] 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 fc1ef736253c..0b891286a150 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 30f1c58cc5f8184725d20a964d0886e098d3b586 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Sun, 18 Mar 2012 15:25:55 -0700 Subject: [PATCH 191/475] 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 05c369015c46c8c3fbabffd0e15ec4a28a18212d Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 19 Jul 2012 18:40:04 -0700 Subject: [PATCH 192/475] 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 394668b4523a3bb88be279f1c11f812602d7971a Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 31 Oct 2012 17:41:39 -0700 Subject: [PATCH 193/475] 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 cdad2cdca20c2e5aa6451cbbeb6604736647f0f1 Mon Sep 17 00:00:00 2001 From: Mars Date: Sat, 3 Nov 2012 12:15:38 +0800 Subject: [PATCH 194/475] 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 9b6224b1f5d5208c1ea15b528d9d9be41776ba75 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 195/475] 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 a38d2154c8fef8b7589128d7d1b6e2397bf01c9e 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 196/475] 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 f8b0ad9d7bdd99952688c5317ec9b0bf254cf96b 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 197/475] 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 699df5adbbfd0469ffde71e9608d8e819fbae5cf Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 3 Jul 2013 15:48:04 -0700 Subject: [PATCH 198/475] 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 9232caee7060..f3c662299531 100644 --- a/arch/arm/kernel/kgdb.c +++ b/arch/arm/kernel/kgdb.c @@ -140,6 +140,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; @@ -147,6 +149,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 35ccfaff94dd538025adf89fe7b70dfa270d9001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 24 Jun 2013 18:02:05 -0700 Subject: [PATCH 199/475] ARM: fiq_glue: Add custom fiq return handler api. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5ff2764e85151ca0a88576542fda07c2d33dd065 Signed-off-by: Arve Hjønnevåg --- arch/arm/common/fiq_glue.S | 25 +++++++++------ arch/arm/common/fiq_glue_setup.c | 53 ++++++++++++++++++++++++++++++-- arch/arm/include/asm/fiq_glue.h | 3 ++ 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/arch/arm/common/fiq_glue.S b/arch/arm/common/fiq_glue.S index 9e3455a09f8f..24b42cec4813 100644 --- a/arch/arm/common/fiq_glue.S +++ b/arch/arm/common/fiq_glue.S @@ -22,13 +22,14 @@ /* fiq stack: r0-r15,cpsr,spsr of interrupted mode */ ENTRY(fiq_glue) - /* store pc, cpsr from previous mode */ + /* store pc, cpsr from previous mode, reserve space for spsr */ mrs r12, spsr - sub r11, lr, #4 + sub lr, lr, #4 subs r10, #1 bne nested_fiq - stmfd sp!, {r11-r12, lr} + str r12, [sp, #-8]! + str lr, [sp, #-4]! /* store r8-r14 from previous mode */ sub sp, sp, #(7 * 4) @@ -85,12 +86,15 @@ 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} + ldr lr, [sp, #(4 * 7)] + ldr r12, [sp, #(4 * 8)] + add sp, sp, #(10 * 4) exit_fiq: msr spsr_cxsf, r12 add r10, #1 - movs pc, r11 + cmp r11, #0 + moveqs pc, lr + bx r11 /* jump to custom fiq return function */ nested_fiq: orr r12, r12, #(PSR_F_BIT) @@ -98,14 +102,17 @@ nested_fiq: fiq_glue_end: -ENTRY(fiq_glue_setup) /* func, data, sp */ - mrs r3, cpsr +ENTRY(fiq_glue_setup) /* func, data, sp, smc call number */ + stmfd sp!, {r4} + mrs r4, cpsr msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) movs r8, r0 mov r9, r1 mov sp, r2 + mov r11, r3 moveq r10, #0 movne r10, #1 - msr cpsr_c, r3 + msr cpsr_c, r4 + ldmfd sp!, {r4} bx lr diff --git a/arch/arm/common/fiq_glue_setup.c b/arch/arm/common/fiq_glue_setup.c index 4044c7db95c8..8cb1b611c6d5 100644 --- a/arch/arm/common/fiq_glue_setup.c +++ b/arch/arm/common/fiq_glue_setup.c @@ -18,20 +18,23 @@ #include extern unsigned char fiq_glue, fiq_glue_end; -extern void fiq_glue_setup(void *func, void *data, void *sp); +extern void fiq_glue_setup(void *func, void *data, void *sp, + fiq_return_handler_t fiq_return_handler); 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 fiq_return_handler_t fiq_return_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); + __get_cpu_var(fiq_stack) + THREAD_START_SP, + fiq_return_handler); } int fiq_glue_register_handler(struct fiq_glue_handler *handler) @@ -80,6 +83,49 @@ err_busy: return ret; } +static void fiq_glue_update_return_handler(void (*fiq_return)(void)) +{ + fiq_return_handler = fiq_return; + if (current_handler) + on_each_cpu(fiq_glue_setup_helper, current_handler, true); +} + +int fiq_glue_set_return_handler(void (*fiq_return)(void)) +{ + int ret; + + mutex_lock(&fiq_glue_lock); + if (fiq_return_handler) { + ret = -EBUSY; + goto err_busy; + } + fiq_glue_update_return_handler(fiq_return); + ret = 0; +err_busy: + mutex_unlock(&fiq_glue_lock); + + return ret; +} +EXPORT_SYMBOL(fiq_glue_set_return_handler); + +int fiq_glue_clear_return_handler(void (*fiq_return)(void)) +{ + int ret; + + mutex_lock(&fiq_glue_lock); + if (WARN_ON(fiq_return_handler != fiq_return)) { + ret = -EINVAL; + goto err_inval; + } + fiq_glue_update_return_handler(NULL); + ret = 0; +err_inval: + mutex_unlock(&fiq_glue_lock); + + return ret; +} +EXPORT_SYMBOL(fiq_glue_clear_return_handler); + /** * fiq_glue_resume - Restore fiqs after suspend or low power idle states * @@ -93,7 +139,8 @@ 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); + __get_cpu_var(fiq_stack) + THREAD_START_SP, + fiq_return_handler); 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 index d54c29db97a8..a9e244f9f197 100644 --- a/arch/arm/include/asm/fiq_glue.h +++ b/arch/arm/include/asm/fiq_glue.h @@ -18,8 +18,11 @@ struct fiq_glue_handler { void (*fiq)(struct fiq_glue_handler *h, void *regs, void *svc_sp); void (*resume)(struct fiq_glue_handler *h); }; +typedef void (*fiq_return_handler_t)(void); int fiq_glue_register_handler(struct fiq_glue_handler *handler); +int fiq_glue_set_return_handler(fiq_return_handler_t fiq_return); +int fiq_glue_clear_return_handler(fiq_return_handler_t fiq_return); #ifdef CONFIG_FIQ_GLUE void fiq_glue_resume(void); From 4ef006294ab0b396394dd93d6b93fc0c6b63f75f Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 2 Apr 2014 18:30:04 -0700 Subject: [PATCH 200/475] fiq_debugger: move into drivers/staging/android/fiq_debugger/ Move fiq_debugger into drivers/staging/android/fiq_debugger/ to allow for sharing between ARM and ARM64. Change-Id: I6ca5e8b7e3d000f57da3234260261c5592cef2a8 Signed-off-by: Colin Cross --- arch/arm/common/Kconfig | 46 ------------------- arch/arm/common/Makefile | 1 - drivers/staging/android/Kconfig | 2 + drivers/staging/android/Makefile | 1 + drivers/staging/android/fiq_debugger/Kconfig | 43 +++++++++++++++++ drivers/staging/android/fiq_debugger/Makefile | 1 + .../android/fiq_debugger}/fiq_debugger.c | 4 +- .../android/fiq_debugger}/fiq_debugger.h | 2 +- .../fiq_debugger}/fiq_debugger_ringbuf.h | 2 +- 9 files changed, 51 insertions(+), 51 deletions(-) create mode 100644 drivers/staging/android/fiq_debugger/Kconfig create mode 100644 drivers/staging/android/fiq_debugger/Makefile rename {arch/arm/common => drivers/staging/android/fiq_debugger}/fiq_debugger.c (99%) rename {arch/arm/include/asm => drivers/staging/android/fiq_debugger}/fiq_debugger.h (97%) rename {arch/arm/common => drivers/staging/android/fiq_debugger}/fiq_debugger_ringbuf.h (96%) diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 992d4046bb8a..ce01364a96e3 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -21,49 +21,3 @@ config SHARP_SCOOP config FIQ_GLUE bool select FIQ - -config FIQ_DEBUGGER - bool "FIQ Mode Serial Debugger" - select FIQ - select FIQ_GLUE - default n - help - The FIQ serial debugger can accept commands even when the - kernel is unresponsive due to being stuck with interrupts - disabled. - - -config FIQ_DEBUGGER_NO_SLEEP - bool "Keep serial debugger active" - depends on FIQ_DEBUGGER - default n - help - Enables the serial debugger at boot. Passing - fiq_debugger.no_sleep on the kernel commandline will - override this config option. - -config FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON - bool "Don't disable wakeup IRQ when debugger is active" - depends on FIQ_DEBUGGER - default n - help - Don't disable the wakeup irq when enabling the uart clock. This will - cause extra interrupts, but it makes the serial debugger usable with - on some MSM radio builds that ignore the uart clock request in power - collapse. - -config FIQ_DEBUGGER_CONSOLE - bool "Console on FIQ Serial Debugger port" - depends on FIQ_DEBUGGER - default n - help - Enables a console so that printk messages are displayed on - the debugger serial port as the occur. - -config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE - bool "Put the FIQ debugger into console mode by default" - depends on FIQ_DEBUGGER_CONSOLE - default n - help - If enabled, this puts the fiq debugger into console mode by default. - Otherwise, the fiq debugger will start out in debug mode. diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 707dcdf629d0..04aca896b338 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -4,7 +4,6 @@ obj-y += firmware.o -obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_ICST) += icst.o obj-$(CONFIG_SA1111) += sa1111.o diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 6e8557fc2200..0c0c092ce354 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -77,6 +77,8 @@ config SW_SYNC_USER source "drivers/staging/android/ion/Kconfig" +source "drivers/staging/android/fiq_debugger/Kconfig" + endif # if ANDROID endmenu diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index c7b6c99cc5ce..fcb7edca24c9 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -1,6 +1,7 @@ ccflags-y += -I$(src) # needed for trace events obj-y += ion/ +obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o diff --git a/drivers/staging/android/fiq_debugger/Kconfig b/drivers/staging/android/fiq_debugger/Kconfig new file mode 100644 index 000000000000..803fb59cc82f --- /dev/null +++ b/drivers/staging/android/fiq_debugger/Kconfig @@ -0,0 +1,43 @@ +config FIQ_DEBUGGER + bool "FIQ Mode Serial Debugger" + default n + depends on ARM + 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/drivers/staging/android/fiq_debugger/Makefile b/drivers/staging/android/fiq_debugger/Makefile new file mode 100644 index 000000000000..437037852963 --- /dev/null +++ b/drivers/staging/android/fiq_debugger/Makefile @@ -0,0 +1 @@ +obj-y += fiq_debugger.o diff --git a/arch/arm/common/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c similarity index 99% rename from arch/arm/common/fiq_debugger.c rename to drivers/staging/android/fiq_debugger/fiq_debugger.c index 65b943c76300..f93d7526b87a 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -1,5 +1,5 @@ /* - * arch/arm/common/fiq_debugger.c + * drivers/staging/android/fiq_debugger.c * * Serial Debugger Interface accessed through an FIQ interrupt. * @@ -35,12 +35,12 @@ #include #include -#include #include #include #include +#include "fiq_debugger.h" #include "fiq_debugger_ringbuf.h" #define DEBUG_MAX 64 diff --git a/arch/arm/include/asm/fiq_debugger.h b/drivers/staging/android/fiq_debugger/fiq_debugger.h similarity index 97% rename from arch/arm/include/asm/fiq_debugger.h rename to drivers/staging/android/fiq_debugger/fiq_debugger.h index 4d274883ba6a..c9ec4f8db086 100644 --- a/arch/arm/include/asm/fiq_debugger.h +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.h @@ -1,5 +1,5 @@ /* - * arch/arm/include/asm/fiq_debugger.h + * drivers/staging/android/fiq_debugger/fiq_debugger.h * * Copyright (C) 2010 Google, Inc. * Author: Colin Cross diff --git a/arch/arm/common/fiq_debugger_ringbuf.h b/drivers/staging/android/fiq_debugger/fiq_debugger_ringbuf.h similarity index 96% rename from arch/arm/common/fiq_debugger_ringbuf.h rename to drivers/staging/android/fiq_debugger/fiq_debugger_ringbuf.h index 2649b5581088..10c3c5d09098 100644 --- a/arch/arm/common/fiq_debugger_ringbuf.h +++ b/drivers/staging/android/fiq_debugger/fiq_debugger_ringbuf.h @@ -1,5 +1,5 @@ /* - * arch/arm/common/fiq_debugger_ringbuf.c + * drivers/staging/android/fiq_debugger/fiq_debugger_ringbuf.h * * simple lockless ringbuffer * From cfbb5d42645d4756b119ed0ca082b89c543d64a1 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 4 Apr 2014 22:58:23 -0700 Subject: [PATCH 201/475] fiq_debugger: rename debug->fiq_debugger Rename variables and functions in the global namespace to avoid future collisions. Change-Id: Ic23a304b0f794efc94cc6d086fddd63231d99c98 Signed-off-by: Colin Cross --- .../android/fiq_debugger/fiq_debugger.c | 405 ++++++++++-------- 1 file changed, 220 insertions(+), 185 deletions(-) diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c index f93d7526b87a..c3a862790239 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -124,10 +124,13 @@ 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) {} -static inline void disable_wakeup_irq(struct fiq_debugger_state *state) {} +static inline +void fiq_debugger_enable_wakeup_irq(struct fiq_debugger_state *state) {} +static inline +void fiq_debugger_disable_wakeup_irq(struct fiq_debugger_state *state) {} #else -static inline void enable_wakeup_irq(struct fiq_debugger_state *state) +static inline +void fiq_debugger_enable_wakeup_irq(struct fiq_debugger_state *state) { if (state->wakeup_irq < 0) return; @@ -135,7 +138,8 @@ static inline void enable_wakeup_irq(struct fiq_debugger_state *state) if (!state->wakeup_irq_no_set_wake) enable_irq_wake(state->wakeup_irq); } -static inline void disable_wakeup_irq(struct fiq_debugger_state *state) +static inline +void fiq_debugger_disable_wakeup_irq(struct fiq_debugger_state *state) { if (state->wakeup_irq < 0) return; @@ -145,16 +149,16 @@ static inline void disable_wakeup_irq(struct fiq_debugger_state *state) } #endif -static bool inline debug_have_fiq(struct fiq_debugger_state *state) +static inline bool fiq_debugger_have_fiq(struct fiq_debugger_state *state) { return (state->fiq >= 0); } -static void debug_force_irq(struct fiq_debugger_state *state) +static void fiq_debugger_force_irq(struct fiq_debugger_state *state) { unsigned int irq = state->signal_irq; - if (WARN_ON(!debug_have_fiq(state))) + if (WARN_ON(!fiq_debugger_have_fiq(state))) return; if (state->pdata->force_irq) { state->pdata->force_irq(state->pdev, irq); @@ -165,7 +169,7 @@ static void debug_force_irq(struct fiq_debugger_state *state) } } -static void debug_uart_enable(struct fiq_debugger_state *state) +static void fiq_debugger_uart_enable(struct fiq_debugger_state *state) { if (state->clk) clk_enable(state->clk); @@ -173,7 +177,7 @@ static void debug_uart_enable(struct fiq_debugger_state *state) state->pdata->uart_enable(state->pdev); } -static void debug_uart_disable(struct fiq_debugger_state *state) +static void fiq_debugger_uart_disable(struct fiq_debugger_state *state) { if (state->pdata->uart_disable) state->pdata->uart_disable(state->pdev); @@ -181,33 +185,33 @@ static void debug_uart_disable(struct fiq_debugger_state *state) clk_disable(state->clk); } -static void debug_uart_flush(struct fiq_debugger_state *state) +static void fiq_debugger_uart_flush(struct fiq_debugger_state *state) { if (state->pdata->uart_flush) state->pdata->uart_flush(state->pdev); } -static void debug_putc(struct fiq_debugger_state *state, char c) +static void fiq_debugger_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) +static void fiq_debugger_puts(struct fiq_debugger_state *state, char *s) { unsigned c; while ((c = *s++)) { if (c == '\n') - debug_putc(state, '\r'); - debug_putc(state, c); + fiq_debugger_putc(state, '\r'); + fiq_debugger_putc(state, c); } } -static void debug_prompt(struct fiq_debugger_state *state) +static void fiq_debugger_prompt(struct fiq_debugger_state *state) { - debug_puts(state, "debug> "); + fiq_debugger_puts(state, "debug> "); } -static void dump_kernel_log(struct fiq_debugger_state *state) +static void fiq_debugger_dump_kernel_log(struct fiq_debugger_state *state) { char buf[512]; size_t len; @@ -218,7 +222,7 @@ static void dump_kernel_log(struct fiq_debugger_state *state) while (kmsg_dump_get_line_nolock(&dumper, true, buf, sizeof(buf) - 1, &len)) { buf[len] = 0; - debug_puts(state, buf); + fiq_debugger_puts(state, buf); } } @@ -236,7 +240,7 @@ static char *mode_name(unsigned cpsr) } } -static int debug_printf(void *cookie, const char *fmt, ...) +static int fiq_debugger_printf(void *cookie, const char *fmt, ...) { struct fiq_debugger_state *state = cookie; char buf[256]; @@ -246,12 +250,12 @@ static int debug_printf(void *cookie, const char *fmt, ...) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); - debug_puts(state, buf); + fiq_debugger_puts(state, buf); return state->debug_abort; } /* Safe outside fiq context */ -static int debug_printf_nfiq(void *cookie, const char *fmt, ...) +static int fiq_debugger_printf_nfiq(void *cookie, const char *fmt, ...) { struct fiq_debugger_state *state = cookie; char buf[256]; @@ -263,29 +267,35 @@ static int debug_printf_nfiq(void *cookie, const char *fmt, ...) va_end(ap); local_irq_save(irq_flags); - debug_puts(state, buf); - debug_uart_flush(state); + fiq_debugger_puts(state, buf); + fiq_debugger_uart_flush(state); local_irq_restore(irq_flags); return state->debug_abort; } -static void dump_regs(struct fiq_debugger_state *state, unsigned *regs) +static void fiq_debugger_dump_regs(struct fiq_debugger_state *state, + unsigned *regs) { - debug_printf(state, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + fiq_debugger_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", + fiq_debugger_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", + fiq_debugger_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]); + fiq_debugger_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]); + fiq_debugger_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 { @@ -340,38 +350,45 @@ void __naked get_mode_regs(struct mode_regs *regs) } -static void dump_allregs(struct fiq_debugger_state *state, unsigned *regs) +static void fiq_debugger_dump_allregs(struct fiq_debugger_state *state, + unsigned *regs) { struct mode_regs mode_regs; - dump_regs(state, regs); + fiq_debugger_dump_regs(state, regs); get_mode_regs(&mode_regs); - debug_printf(state, " svc: sp %08x lr %08x spsr %08x\n", + fiq_debugger_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", + fiq_debugger_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", + fiq_debugger_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", + fiq_debugger_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", + fiq_debugger_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", + fiq_debugger_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) +static void fiq_debugger_dump_irqs(struct fiq_debugger_state *state) { int n; struct irq_desc *desc; - debug_printf(state, "irqnr total since-last status name\n"); + fiq_debugger_printf(state, + "irqnr total since-last status name\n"); 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, + fiq_debugger_printf(state, "%5d: %10u %11u %8x %s\n", n, kstat_irqs(n), kstat_irqs(n) - state->last_irqs[n], desc->status_use_accessors, @@ -390,14 +407,14 @@ static int report_trace(struct stackframe *frame, void *d) struct stacktrace_state *sts = d; if (sts->depth) { - debug_printf(sts->state, + fiq_debugger_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"); + fiq_debugger_printf(sts->state, " ...\n"); return sts->depth == 0; } @@ -415,16 +432,17 @@ static struct frame_tail *user_backtrace(struct fiq_debugger_state *state, /* 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); + fiq_debugger_printf(state, " invalid frame pointer %p\n", + tail); return NULL; } if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) { - debug_printf(state, + fiq_debugger_printf(state, " failed to copy frame pointer %p\n", tail); return NULL; } - debug_printf(state, " %p\n", buftail[0].lr); + fiq_debugger_printf(state, " %p\n", buftail[0].lr); /* frame pointers should strictly progress back up the stack * (towards higher addresses) */ @@ -434,7 +452,7 @@ static struct frame_tail *user_backtrace(struct fiq_debugger_state *state, return buftail[0].fp-1; } -void dump_stacktrace(struct fiq_debugger_state *state, +void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, struct pt_regs * const regs, unsigned int depth, void *ssp) { struct frame_tail *tail; @@ -446,11 +464,11 @@ void dump_stacktrace(struct fiq_debugger_state *state, *current_thread_info() = *real_thread_info; if (!current) - debug_printf(state, "current NULL\n"); + fiq_debugger_printf(state, "current NULL\n"); else - debug_printf(state, "pid: %d comm: %s\n", + fiq_debugger_printf(state, "pid: %d comm: %s\n", current->pid, current->comm); - dump_regs(state, (unsigned *)regs); + fiq_debugger_dump_regs(state, (unsigned *)regs); if (!user_mode(regs)) { struct stackframe frame; @@ -458,7 +476,7 @@ void dump_stacktrace(struct fiq_debugger_state *state, frame.sp = regs->ARM_sp; frame.lr = regs->ARM_lr; frame.pc = regs->ARM_pc; - debug_printf(state, + fiq_debugger_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); @@ -471,84 +489,86 @@ void dump_stacktrace(struct fiq_debugger_state *state, tail = user_backtrace(state, tail); } -static void do_ps(struct fiq_debugger_state *state) +static void fiq_debugger_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"); + fiq_debugger_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, + fiq_debugger_printf(state, "%5d %5d %4d ", p->pid, p->parent->pid, p->prio); - debug_printf(state, "%-13.13s %c", p->comm, + fiq_debugger_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"); + fiq_debugger_printf(state, " running\n"); else - debug_printf(state, " %08lx\n", thread_saved_pc(p)); + fiq_debugger_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) +static void fiq_debugger_begin_syslog_dump(struct fiq_debugger_state *state) { state->syslog_dumping = true; } -static void end_syslog_dump(struct fiq_debugger_state *state) +static void fiq_debugger_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) +static void fiq_debugger_begin_syslog_dump(struct fiq_debugger_state *state) { do_syslog(5 /* clear */, NULL, 0); } -static void end_syslog_dump(struct fiq_debugger_state *state) +static void fiq_debugger_end_syslog_dump(struct fiq_debugger_state *state) { - dump_kernel_log(state); + fiq_debugger_dump_kernel_log(state); } #endif -static void do_sysrq(struct fiq_debugger_state *state, char rq) +static void fiq_debugger_do_sysrq(struct fiq_debugger_state *state, char rq) { if ((rq == 'g' || rq == 'G') && !fiq_kgdb_enable) { - debug_printf(state, "sysrq-g blocked\n"); + fiq_debugger_printf(state, "sysrq-g blocked\n"); return; } - begin_syslog_dump(state); + fiq_debugger_begin_syslog_dump(state); handle_sysrq(rq); - end_syslog_dump(state); + fiq_debugger_end_syslog_dump(state); } #ifdef CONFIG_KGDB -static void do_kgdb(struct fiq_debugger_state *state) +static void fiq_debugger_do_kgdb(struct fiq_debugger_state *state) { if (!fiq_kgdb_enable) { - debug_printf(state, "kgdb through fiq debugger not enabled\n"); + fiq_debugger_printf(state, "kgdb through fiq debugger not enabled\n"); return; } - debug_printf(state, "enabling console and triggering kgdb\n"); + fiq_debugger_printf(state, "enabling console and triggering kgdb\n"); state->console_enable = true; handle_sysrq('g'); } #endif -static void debug_schedule_work(struct fiq_debugger_state *state, char *cmd) +static void fiq_debugger_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"); + fiq_debugger_printf(state, "work command processor busy\n"); spin_unlock_irqrestore(&state->work_lock, flags); return; } @@ -559,7 +579,7 @@ static void debug_schedule_work(struct fiq_debugger_state *state, char *cmd) schedule_work(&state->work); } -static void debug_work(struct work_struct *work) +static void fiq_debugger_work(struct work_struct *work) { struct fiq_debugger_state *state; char work_cmd[DEBUG_MAX]; @@ -585,30 +605,32 @@ static void debug_work(struct work_struct *work) else kernel_restart(NULL); } else { - debug_printf(state, "unknown work command '%s'\n", work_cmd); + fiq_debugger_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) +static void fiq_debugger_irq_exec(struct fiq_debugger_state *state, char *cmd) { if (!strcmp(cmd, "ps")) - do_ps(state); + fiq_debugger_do_ps(state); if (!strcmp(cmd, "sysrq")) - do_sysrq(state, 'h'); + fiq_debugger_do_sysrq(state, 'h'); if (!strncmp(cmd, "sysrq ", 6)) - do_sysrq(state, cmd[6]); + fiq_debugger_do_sysrq(state, cmd[6]); #ifdef CONFIG_KGDB if (!strcmp(cmd, "kgdb")) - do_kgdb(state); + fiq_debugger_do_kgdb(state); #endif if (!strncmp(cmd, "reboot", 6)) - debug_schedule_work(state, cmd); + fiq_debugger_schedule_work(state, cmd); } -static void debug_help(struct fiq_debugger_state *state) +static void fiq_debugger_help(struct fiq_debugger_state *state) { - debug_printf(state, "FIQ Debugger commands:\n" + fiq_debugger_printf(state, + "FIQ Debugger commands:\n" " pc PC status\n" " regs Register dump\n" " allregs Extended Register dump\n" @@ -618,20 +640,23 @@ static void debug_help(struct fiq_debugger_state *state) " irqs Interupt status\n" " kmsg Kernel log\n" " version Kernel version\n"); - debug_printf(state, " sleep Allow sleep while in FIQ\n" + fiq_debugger_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" + fiq_debugger_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"); + fiq_debugger_printf(state, + " kgdb Enter kernel debugger\n"); #endif } -static void take_affinity(void *info) +static void fiq_debugger_take_affinity(void *info) { struct fiq_debugger_state *state = info; struct cpumask cpumask; @@ -642,29 +667,31 @@ static void take_affinity(void *info) irq_set_affinity(state->uart_irq, &cpumask); } -static void switch_cpu(struct fiq_debugger_state *state, int cpu) +static void fiq_debugger_switch_cpu(struct fiq_debugger_state *state, int cpu) { - if (!debug_have_fiq(state)) - smp_call_function_single(cpu, take_affinity, state, false); + if (!fiq_debugger_have_fiq(state)) + smp_call_function_single(cpu, fiq_debugger_take_affinity, state, + false); state->current_cpu = cpu; } -static bool debug_fiq_exec(struct fiq_debugger_state *state, +static bool fiq_debugger_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); + fiq_debugger_help(state); } else if (!strcmp(cmd, "pc")) { - debug_printf(state, " pc %08x cpsr %08x mode %s\n", + fiq_debugger_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); + fiq_debugger_dump_regs(state, regs); } else if (!strcmp(cmd, "allregs")) { - dump_allregs(state, regs); + fiq_debugger_dump_allregs(state, regs); } else if (!strcmp(cmd, "bt")) { - dump_stacktrace(state, (struct pt_regs *)regs, 100, svc_sp); + fiq_debugger_dump_stacktrace(state, (struct pt_regs *)regs, 100, + svc_sp); } else if (!strncmp(cmd, "reset", 5)) { cmd += 5; while (*cmd == ' ') @@ -677,33 +704,33 @@ static bool debug_fiq_exec(struct fiq_debugger_state *state, machine_restart(NULL); } } else if (!strcmp(cmd, "irqs")) { - dump_irqs(state); + fiq_debugger_dump_irqs(state); } else if (!strcmp(cmd, "kmsg")) { - dump_kernel_log(state); + fiq_debugger_dump_kernel_log(state); } else if (!strcmp(cmd, "version")) { - debug_printf(state, "%s\n", linux_banner); + fiq_debugger_printf(state, "%s\n", linux_banner); } else if (!strcmp(cmd, "sleep")) { state->no_sleep = false; - debug_printf(state, "enabling sleep\n"); + fiq_debugger_printf(state, "enabling sleep\n"); } else if (!strcmp(cmd, "nosleep")) { state->no_sleep = true; - debug_printf(state, "disabling sleep\n"); + fiq_debugger_printf(state, "disabling sleep\n"); } else if (!strcmp(cmd, "console")) { - debug_printf(state, "console mode\n"); - debug_uart_flush(state); + fiq_debugger_printf(state, "console mode\n"); + fiq_debugger_uart_flush(state); state->console_enable = true; } else if (!strcmp(cmd, "cpu")) { - debug_printf(state, "cpu %d\n", state->current_cpu); + fiq_debugger_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); + fiq_debugger_switch_cpu(state, cpu); else - debug_printf(state, "invalid cpu\n"); - debug_printf(state, "cpu %d\n", state->current_cpu); + fiq_debugger_printf(state, "invalid cpu\n"); + fiq_debugger_printf(state, "cpu %d\n", state->current_cpu); } else { if (state->debug_busy) { - debug_printf(state, + fiq_debugger_printf(state, "command processor busy. trying to abort.\n"); state->debug_abort = -1; } else { @@ -714,12 +741,12 @@ static bool debug_fiq_exec(struct fiq_debugger_state *state, return true; } if (!state->console_enable) - debug_prompt(state); + fiq_debugger_prompt(state); return signal_helper; } -static void sleep_timer_expired(unsigned long data) +static void fiq_debugger_sleep_timer_expired(unsigned long data) { struct fiq_debugger_state *state = (struct fiq_debugger_state *)data; unsigned long flags; @@ -728,18 +755,19 @@ static void sleep_timer_expired(unsigned long data) 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"); + fiq_debugger_printf_nfiq(state, + "suspending fiq debugger\n"); } state->ignore_next_wakeup_irq = true; - debug_uart_disable(state); + fiq_debugger_uart_disable(state); state->uart_enabled = false; - enable_wakeup_irq(state); + fiq_debugger_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) +static void fiq_debugger_handle_wakeup(struct fiq_debugger_state *state) { unsigned long flags; @@ -748,26 +776,27 @@ static void handle_wakeup(struct fiq_debugger_state *state) state->ignore_next_wakeup_irq = false; } else if (!state->uart_enabled) { wake_lock(&state->debugger_wake_lock); - debug_uart_enable(state); + fiq_debugger_uart_enable(state); state->uart_enabled = true; - disable_wakeup_irq(state); + fiq_debugger_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) +static irqreturn_t fiq_debugger_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); + fiq_debugger_puts(state, "WAKEUP\n"); + fiq_debugger_handle_wakeup(state); return IRQ_HANDLED; } -static void debug_handle_console_irq_context(struct fiq_debugger_state *state) +static +void fiq_debugger_handle_console_irq_context(struct fiq_debugger_state *state) { #if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) if (state->tty_port.ops) { @@ -784,7 +813,7 @@ static void debug_handle_console_irq_context(struct fiq_debugger_state *state) #endif } -static void debug_handle_irq_context(struct fiq_debugger_state *state) +static void fiq_debugger_handle_irq_context(struct fiq_debugger_state *state) { if (!state->no_sleep) { unsigned long flags; @@ -794,22 +823,22 @@ 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); } - debug_handle_console_irq_context(state); + fiq_debugger_handle_console_irq_context(state); if (state->debug_busy) { - debug_irq_exec(state, state->debug_cmd); + fiq_debugger_irq_exec(state, state->debug_cmd); if (!state->console_enable) - debug_prompt(state); + fiq_debugger_prompt(state); state->debug_busy = 0; } } -static int debug_getc(struct fiq_debugger_state *state) +static int fiq_debugger_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) +static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state, + int this_cpu, struct pt_regs *regs, void *svc_sp) { int c; static int last_c; @@ -824,30 +853,31 @@ static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state, MAX_UNHANDLED_FIQ_COUNT) return false; - debug_printf(state, "fiq_debugger: cpu %d not responding, " + fiq_debugger_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); + fiq_debugger_switch_cpu(state, this_cpu); return false; } state->in_fiq = true; - while ((c = debug_getc(state)) != FIQ_DEBUGGER_NO_CHAR) { + while ((c = fiq_debugger_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); + fiq_debugger_prompt(state); } } else if (c == FIQ_DEBUGGER_BREAK) { state->console_enable = false; - debug_puts(state, "fiq debugger mode\n"); + fiq_debugger_puts(state, "fiq debugger mode\n"); state->debug_count = 0; - debug_prompt(state); + fiq_debugger_prompt(state); #ifdef CONFIG_FIQ_DEBUGGER_CONSOLE } else if (state->console_enable && state->tty_rbuf) { fiq_debugger_ringbuf_push(state->tty_rbuf, c); @@ -856,34 +886,35 @@ 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; - debug_putc(state, c); + fiq_debugger_putc(state, c); } } else if ((c == 8) || (c == 127)) { if (state->debug_count > 0) { state->debug_count--; - debug_putc(state, 8); - debug_putc(state, ' '); - debug_putc(state, 8); + fiq_debugger_putc(state, 8); + fiq_debugger_putc(state, ' '); + fiq_debugger_putc(state, 8); } } else if ((c == 13) || (c == 10)) { if (c == '\r' || (c == '\n' && last_c != '\r')) { - debug_putc(state, '\r'); - debug_putc(state, '\n'); + fiq_debugger_putc(state, '\r'); + fiq_debugger_putc(state, '\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); + fiq_debugger_fiq_exec(state, + state->debug_buf, + regs, svc_sp); } else { - debug_prompt(state); + fiq_debugger_prompt(state); } } last_c = c; } if (!state->console_enable) - debug_uart_flush(state); + fiq_debugger_uart_flush(state); if (state->pdata->fiq_ack) state->pdata->fiq_ack(state->pdev, state->fiq); @@ -897,16 +928,18 @@ static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state, return signal_helper; } -static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp) +static void fiq_debugger_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); + need_irq = fiq_debugger_handle_uart_interrupt(state, this_cpu, regs, + svc_sp); if (need_irq) - debug_force_irq(state); + fiq_debugger_force_irq(state); } /* @@ -914,19 +947,19 @@ static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp) * 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) +static irqreturn_t fiq_debugger_uart_irq(int irq, void *dev) { struct fiq_debugger_state *state = dev; bool not_done; - handle_wakeup(state); + fiq_debugger_handle_wakeup(state); /* handle the debugger irq in regular context */ - not_done = debug_handle_uart_interrupt(state, smp_processor_id(), + not_done = fiq_debugger_handle_uart_interrupt(state, smp_processor_id(), get_irq_regs(), current_thread_info()); if (not_done) - debug_handle_irq_context(state); + fiq_debugger_handle_irq_context(state); return IRQ_HANDLED; } @@ -936,19 +969,19 @@ static irqreturn_t debug_uart_irq(int irq, void *dev) * 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) +static irqreturn_t fiq_debugger_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); + fiq_debugger_handle_irq_context(state); return IRQ_HANDLED; } -static void debug_resume(struct fiq_glue_handler *h) +static void fiq_debugger_resume(struct fiq_glue_handler *h) { struct fiq_debugger_state *state = container_of(h, struct fiq_debugger_state, handler); @@ -957,13 +990,13 @@ 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 tty_driver *fiq_debugger_console_device(struct console *co, int *index) { *index = co->index; return fiq_tty_driver; } -static void debug_console_write(struct console *co, +static void fiq_debugger_console_write(struct console *co, const char *s, unsigned int count) { struct fiq_debugger_state *state; @@ -974,22 +1007,22 @@ static void debug_console_write(struct console *co, if (!state->console_enable && !state->syslog_dumping) return; - debug_uart_enable(state); + fiq_debugger_uart_enable(state); spin_lock_irqsave(&state->console_lock, flags); while (count--) { if (*s == '\n') - debug_putc(state, '\r'); - debug_putc(state, *s++); + fiq_debugger_putc(state, '\r'); + fiq_debugger_putc(state, *s++); } - debug_uart_flush(state); + fiq_debugger_uart_flush(state); spin_unlock_irqrestore(&state->console_lock, flags); - debug_uart_disable(state); + fiq_debugger_uart_disable(state); } static struct console fiq_debugger_console = { .name = "ttyFIQ", - .device = debug_console_device, - .write = debug_console_write, + .device = fiq_debugger_console_device, + .write = fiq_debugger_console_write, .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, }; @@ -1017,12 +1050,12 @@ int fiq_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) if (!state->console_enable) return count; - debug_uart_enable(state); + fiq_debugger_uart_enable(state); spin_lock_irq(&state->console_lock); for (i = 0; i < count; i++) - debug_putc(state, *buf++); + fiq_debugger_putc(state, *buf++); spin_unlock_irq(&state->console_lock); - debug_uart_disable(state); + fiq_debugger_uart_disable(state); return count; } @@ -1044,19 +1077,19 @@ static int fiq_tty_poll_get_char(struct tty_driver *driver, int line) struct fiq_debugger_state *state = states[line]; int c = NO_POLL_CHAR; - debug_uart_enable(state); - if (debug_have_fiq(state)) { + fiq_debugger_uart_enable(state); + if (fiq_debugger_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); + c = fiq_debugger_getc(state); if (c == FIQ_DEBUGGER_NO_CHAR) c = NO_POLL_CHAR; } - debug_uart_disable(state); + fiq_debugger_uart_disable(state); return c; } @@ -1065,9 +1098,9 @@ static void fiq_tty_poll_put_char(struct tty_driver *driver, int line, char ch) { 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); + fiq_debugger_uart_enable(state); + fiq_debugger_putc(state, ch); + fiq_debugger_uart_disable(state); } #endif @@ -1224,7 +1257,7 @@ static int fiq_debugger_probe(struct platform_device *pdev) return -EINVAL; state = kzalloc(sizeof(*state), GFP_KERNEL); - setup_timer(&state->sleep_timer, sleep_timer_expired, + setup_timer(&state->sleep_timer, fiq_debugger_sleep_timer_expired, (unsigned long)state); state->pdata = pdata; state->pdev = pdev; @@ -1237,14 +1270,14 @@ 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); + INIT_WORK(&state->work, fiq_debugger_work); spin_lock_init(&state->work_lock); platform_set_drvdata(pdev, state); spin_lock_init(&state->sleep_timer_lock); - if (state->wakeup_irq < 0 && debug_have_fiq(state)) + if (state->wakeup_irq < 0 && fiq_debugger_have_fiq(state)) state->no_sleep = true; state->ignore_next_wakeup_irq = !state->no_sleep; @@ -1268,12 +1301,13 @@ static int fiq_debugger_probe(struct platform_device *pdev) goto err_uart_init; } - debug_printf_nfiq(state, "\n", + fiq_debugger_printf_nfiq(state, + "\n", state->no_sleep ? "" : "twice "); - if (debug_have_fiq(state)) { - state->handler.fiq = debug_fiq; - state->handler.resume = debug_resume; + if (fiq_debugger_have_fiq(state)) { + state->handler.fiq = fiq_debugger_fiq; + state->handler.resume = fiq_debugger_resume; ret = fiq_glue_register_handler(&state->handler); if (ret) { pr_err("%s: could not install fiq handler\n", __func__); @@ -1282,7 +1316,7 @@ static int fiq_debugger_probe(struct platform_device *pdev) pdata->fiq_enable(pdev, state->fiq, 1); } else { - ret = request_irq(state->uart_irq, debug_uart_irq, + ret = request_irq(state->uart_irq, fiq_debugger_uart_irq, IRQF_NO_SUSPEND, "debug", state); if (ret) { pr_err("%s: could not install irq handler\n", __func__); @@ -1299,14 +1333,15 @@ static int fiq_debugger_probe(struct platform_device *pdev) clk_disable(state->clk); if (state->signal_irq >= 0) { - ret = request_irq(state->signal_irq, debug_signal_irq, + ret = request_irq(state->signal_irq, fiq_debugger_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, + ret = request_irq(state->wakeup_irq, + fiq_debugger_wakeup_irq_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED, "debug-wakeup", state); if (ret) { @@ -1323,7 +1358,7 @@ static int fiq_debugger_probe(struct platform_device *pdev) } } if (state->no_sleep) - handle_wakeup(state); + fiq_debugger_handle_wakeup(state); #if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) spin_lock_init(&state->console_lock); From 10ea8c38eb083c3614d7fc632af72fe92d53b9ce Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 2 Apr 2014 18:37:29 -0700 Subject: [PATCH 202/475] fiq_debugger: allow compiling without CONFIG_FIQ_GLUE Allow compiling fiq_debugger.c without CONFIG_FIQ_GLUE for platforms that don't support FIQs. Change-Id: Iabdfd790d24fa9d47b29d2f850c567af2dcad78f Signed-off-by: Colin Cross --- .../android/fiq_debugger/fiq_debugger.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c index c3a862790239..4c392729fc68 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -35,7 +35,9 @@ #include #include +#ifdef CONFIG_FIQ_GLUE #include +#endif #include #include @@ -52,7 +54,9 @@ ((unsigned long)(sp) & ~(THREAD_SIZE - 1))) struct fiq_debugger_state { +#ifdef CONFIG_FIQ_GLUE struct fiq_glue_handler handler; +#endif int fiq; int uart_irq; @@ -154,6 +158,7 @@ static inline bool fiq_debugger_have_fiq(struct fiq_debugger_state *state) return (state->fiq >= 0); } +#ifdef CONFIG_FIQ_GLUE static void fiq_debugger_force_irq(struct fiq_debugger_state *state) { unsigned int irq = state->signal_irq; @@ -168,6 +173,7 @@ static void fiq_debugger_force_irq(struct fiq_debugger_state *state) chip->irq_retrigger(irq_get_irq_data(irq)); } } +#endif static void fiq_debugger_uart_enable(struct fiq_debugger_state *state) { @@ -928,6 +934,7 @@ static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state, return signal_helper; } +#ifdef CONFIG_FIQ_GLUE static void fiq_debugger_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp) { @@ -941,6 +948,7 @@ static void fiq_debugger_fiq(struct fiq_glue_handler *h, void *regs, if (need_irq) fiq_debugger_force_irq(state); } +#endif /* * When not using FIQs, we only use this single interrupt as an entry point. @@ -981,6 +989,7 @@ static irqreturn_t fiq_debugger_signal_irq(int irq, void *dev) return IRQ_HANDLED; } +#ifdef CONFIG_FIQ_GLUE static void fiq_debugger_resume(struct fiq_glue_handler *h) { struct fiq_debugger_state *state = @@ -988,6 +997,7 @@ static void fiq_debugger_resume(struct fiq_glue_handler *h) if (state->pdata->uart_resume) state->pdata->uart_resume(state->pdev); } +#endif #if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) struct tty_driver *fiq_debugger_console_device(struct console *co, int *index) @@ -1305,17 +1315,20 @@ static int fiq_debugger_probe(struct platform_device *pdev) "\n", state->no_sleep ? "" : "twice "); +#ifdef CONFIG_FIQ_GLUE if (fiq_debugger_have_fiq(state)) { state->handler.fiq = fiq_debugger_fiq; state->handler.resume = fiq_debugger_resume; ret = fiq_glue_register_handler(&state->handler); if (ret) { pr_err("%s: could not install fiq handler\n", __func__); - goto err_register_fiq; + goto err_register_irq; } pdata->fiq_enable(pdev, state->fiq, 1); - } else { + } else +#endif + { ret = request_irq(state->uart_irq, fiq_debugger_uart_irq, IRQF_NO_SUSPEND, "debug", state); if (ret) { @@ -1373,7 +1386,6 @@ static int fiq_debugger_probe(struct platform_device *pdev) return 0; err_register_irq: -err_register_fiq: if (pdata->uart_free) pdata->uart_free(pdev); err_uart_init: From a5c052a3e4de072c70ccf1c564b9912be8721483 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 4 Apr 2014 17:05:19 -0700 Subject: [PATCH 203/475] fiq_debugger: use pt_regs for registers IRQ mode already passes in a struct pt_regs from get_irq_regs(). FIQ mode passes in something similar but not identical to a struct pt_regs - FIQ mode stores the spsr of the interrupted mode in slot 17, while pt_regs expects orig_r0. Replace the existing mixture of void *regs, unsigned *regs, and struct pt_regs * const with const struct pt_regs *. Modify dump_regs not to print the spsr since it won't be there in a struct pt_regs anyways. Modify dump_allregs to highlight the mode that was interrupted, making spsr easy to find there. Change-Id: Ibfe1723d702306c7605fd071737d7be9ee9d8c12 Signed-off-by: Colin Cross --- .../android/fiq_debugger/fiq_debugger.c | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c index 4c392729fc68..d660a46e7893 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -280,28 +280,22 @@ static int fiq_debugger_printf_nfiq(void *cookie, const char *fmt, ...) } static void fiq_debugger_dump_regs(struct fiq_debugger_state *state, - unsigned *regs) + const struct pt_regs *regs) { fiq_debugger_printf(state, " r0 %08x r1 %08x r2 %08x r3 %08x\n", - regs[0], regs[1], regs[2], regs[3]); + regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); fiq_debugger_printf(state, " r4 %08x r5 %08x r6 %08x r7 %08x\n", - regs[4], regs[5], regs[6], regs[7]); + regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7); fiq_debugger_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) - fiq_debugger_printf(state, - " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", - regs[12], regs[13], regs[14], regs[15], - regs[16]); - else - fiq_debugger_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]); + regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp, + mode_name(regs->ARM_cpsr)); + fiq_debugger_printf(state, + " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc, + regs->ARM_cpsr); } struct mode_regs { @@ -357,25 +351,33 @@ void __naked get_mode_regs(struct mode_regs *regs) static void fiq_debugger_dump_allregs(struct fiq_debugger_state *state, - unsigned *regs) + const struct pt_regs *regs) { struct mode_regs mode_regs; + unsigned long mode = regs->ARM_cpsr & MODE_MASK; + fiq_debugger_dump_regs(state, regs); get_mode_regs(&mode_regs); + fiq_debugger_printf(state, - " svc: sp %08x lr %08x spsr %08x\n", + "%csvc: sp %08x lr %08x spsr %08x\n", + mode == SVC_MODE ? '*' : ' ', mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc); fiq_debugger_printf(state, - " abt: sp %08x lr %08x spsr %08x\n", + "%cabt: sp %08x lr %08x spsr %08x\n", + mode == ABT_MODE ? '*' : ' ', mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt); fiq_debugger_printf(state, - " und: sp %08x lr %08x spsr %08x\n", + "%cund: sp %08x lr %08x spsr %08x\n", + mode == UND_MODE ? '*' : ' ', mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und); fiq_debugger_printf(state, - " irq: sp %08x lr %08x spsr %08x\n", + "%cirq: sp %08x lr %08x spsr %08x\n", + mode == IRQ_MODE ? '*' : ' ', mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq); fiq_debugger_printf(state, - " fiq: r8 %08x r9 %08x r10 %08x r11 %08x r12 %08x\n", + "%cfiq: r8 %08x r9 %08x r10 %08x r11 %08x r12 %08x\n", + mode == FIQ_MODE ? '*' : ' ', mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq, mode_regs.r11_fiq, mode_regs.r12_fiq); fiq_debugger_printf(state, @@ -459,7 +461,7 @@ static struct frame_tail *user_backtrace(struct fiq_debugger_state *state, } void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, - struct pt_regs * const regs, unsigned int depth, void *ssp) + const struct pt_regs *regs, unsigned int depth, void *ssp) { struct frame_tail *tail; struct thread_info *real_thread_info = THREAD_INFO(ssp); @@ -474,7 +476,7 @@ void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, else fiq_debugger_printf(state, "pid: %d comm: %s\n", current->pid, current->comm); - fiq_debugger_dump_regs(state, (unsigned *)regs); + fiq_debugger_dump_regs(state, regs); if (!user_mode(regs)) { struct stackframe frame; @@ -682,7 +684,8 @@ static void fiq_debugger_switch_cpu(struct fiq_debugger_state *state, int cpu) } static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, - const char *cmd, unsigned *regs, void *svc_sp) + const char *cmd, const struct pt_regs *regs, + void *svc_sp) { bool signal_helper = false; @@ -690,14 +693,14 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, fiq_debugger_help(state); } else if (!strcmp(cmd, "pc")) { fiq_debugger_printf(state, " pc %08x cpsr %08x mode %s\n", - regs[15], regs[16], mode_name(regs[16])); + regs->ARM_pc, regs->ARM_cpsr, + mode_name(regs->ARM_cpsr)); } else if (!strcmp(cmd, "regs")) { fiq_debugger_dump_regs(state, regs); } else if (!strcmp(cmd, "allregs")) { fiq_debugger_dump_allregs(state, regs); } else if (!strcmp(cmd, "bt")) { - fiq_debugger_dump_stacktrace(state, (struct pt_regs *)regs, 100, - svc_sp); + fiq_debugger_dump_stacktrace(state, regs, 100, svc_sp); } else if (!strncmp(cmd, "reset", 5)) { cmd += 5; while (*cmd == ' ') @@ -844,7 +847,7 @@ static int fiq_debugger_getc(struct fiq_debugger_state *state) } static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state, - int this_cpu, struct pt_regs *regs, void *svc_sp) + int this_cpu, const struct pt_regs *regs, void *svc_sp) { int c; static int last_c; @@ -935,8 +938,8 @@ static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state, } #ifdef CONFIG_FIQ_GLUE -static void fiq_debugger_fiq(struct fiq_glue_handler *h, void *regs, - void *svc_sp) +static void fiq_debugger_fiq(struct fiq_glue_handler *h, + const struct pt_regs *regs, void *svc_sp) { struct fiq_debugger_state *state = container_of(h, struct fiq_debugger_state, handler); From 6f7487e127f5cbfc6fbc9b159cae98677b2111e3 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 2 Apr 2014 18:42:13 -0700 Subject: [PATCH 204/475] fiq_debugger: split arm support into fiq_debugger_arm.c Split arm support into a separate .c file that is only built for CONFIG_ARM. Change-Id: Iba16f4d51608bf9c3e5c8acefefcd38fead9797c Signed-off-by: Colin Cross --- drivers/staging/android/fiq_debugger/Makefile | 1 + .../android/fiq_debugger/fiq_debugger.c | 223 +--------------- .../android/fiq_debugger/fiq_debugger_arm.c | 240 ++++++++++++++++++ .../android/fiq_debugger/fiq_debugger_priv.h | 36 +++ 4 files changed, 280 insertions(+), 220 deletions(-) create mode 100644 drivers/staging/android/fiq_debugger/fiq_debugger_arm.c create mode 100644 drivers/staging/android/fiq_debugger/fiq_debugger_priv.h diff --git a/drivers/staging/android/fiq_debugger/Makefile b/drivers/staging/android/fiq_debugger/Makefile index 437037852963..1e203aed8c62 100644 --- a/drivers/staging/android/fiq_debugger/Makefile +++ b/drivers/staging/android/fiq_debugger/Makefile @@ -1 +1,2 @@ obj-y += fiq_debugger.o +obj-$(CONFIG_ARM) += fiq_debugger_arm.o diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c index d660a46e7893..5516d3113b94 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -38,11 +38,11 @@ #ifdef CONFIG_FIQ_GLUE #include #endif -#include #include #include "fiq_debugger.h" +#include "fiq_debugger_priv.h" #include "fiq_debugger_ringbuf.h" #define DEBUG_MAX 64 @@ -50,9 +50,6 @@ #define MAX_FIQ_DEBUGGER_PORTS 4 -#define THREAD_INFO(sp) ((struct thread_info *) \ - ((unsigned long)(sp) & ~(THREAD_SIZE - 1))) - struct fiq_debugger_state { #ifdef CONFIG_FIQ_GLUE struct fiq_glue_handler handler; @@ -232,21 +229,7 @@ static void fiq_debugger_dump_kernel_log(struct fiq_debugger_state *state) } } -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 fiq_debugger_printf(void *cookie, const char *fmt, ...) +int fiq_debugger_printf(void *cookie, const char *fmt, ...) { struct fiq_debugger_state *state = cookie; char buf[256]; @@ -279,112 +262,6 @@ static int fiq_debugger_printf_nfiq(void *cookie, const char *fmt, ...) return state->debug_abort; } -static void fiq_debugger_dump_regs(struct fiq_debugger_state *state, - const struct pt_regs *regs) -{ - fiq_debugger_printf(state, - " r0 %08x r1 %08x r2 %08x r3 %08x\n", - regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); - fiq_debugger_printf(state, - " r4 %08x r5 %08x r6 %08x r7 %08x\n", - regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7); - fiq_debugger_printf(state, - " r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n", - regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp, - mode_name(regs->ARM_cpsr)); - fiq_debugger_printf(state, - " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", - regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc, - regs->ARM_cpsr); -} - -struct mode_regs { - unsigned long sp_svc; - unsigned long lr_svc; - unsigned long spsr_svc; - - unsigned long sp_abt; - unsigned long lr_abt; - unsigned long spsr_abt; - - unsigned long sp_und; - unsigned long lr_und; - unsigned long spsr_und; - - unsigned long sp_irq; - unsigned long lr_irq; - unsigned long spsr_irq; - - unsigned long r8_fiq; - unsigned long r9_fiq; - unsigned long r10_fiq; - unsigned long r11_fiq; - unsigned long r12_fiq; - unsigned long sp_fiq; - unsigned long lr_fiq; - unsigned long spsr_fiq; -}; - -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 fiq_debugger_dump_allregs(struct fiq_debugger_state *state, - const struct pt_regs *regs) -{ - struct mode_regs mode_regs; - unsigned long mode = regs->ARM_cpsr & MODE_MASK; - - fiq_debugger_dump_regs(state, regs); - get_mode_regs(&mode_regs); - - fiq_debugger_printf(state, - "%csvc: sp %08x lr %08x spsr %08x\n", - mode == SVC_MODE ? '*' : ' ', - mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc); - fiq_debugger_printf(state, - "%cabt: sp %08x lr %08x spsr %08x\n", - mode == ABT_MODE ? '*' : ' ', - mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt); - fiq_debugger_printf(state, - "%cund: sp %08x lr %08x spsr %08x\n", - mode == UND_MODE ? '*' : ' ', - mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und); - fiq_debugger_printf(state, - "%cirq: sp %08x lr %08x spsr %08x\n", - mode == IRQ_MODE ? '*' : ' ', - mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq); - fiq_debugger_printf(state, - "%cfiq: r8 %08x r9 %08x r10 %08x r11 %08x r12 %08x\n", - mode == FIQ_MODE ? '*' : ' ', - mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq, - mode_regs.r11_fiq, mode_regs.r12_fiq); - fiq_debugger_printf(state, - " fiq: sp %08x lr %08x spsr %08x\n", - mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq); -} - static void fiq_debugger_dump_irqs(struct fiq_debugger_state *state) { int n; @@ -405,98 +282,6 @@ static void fiq_debugger_dump_irqs(struct fiq_debugger_state *state) } } -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) { - fiq_debugger_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; - } - fiq_debugger_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))) { - fiq_debugger_printf(state, " invalid frame pointer %p\n", - tail); - return NULL; - } - if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) { - fiq_debugger_printf(state, - " failed to copy frame pointer %p\n", tail); - return NULL; - } - - fiq_debugger_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 fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, - const struct pt_regs *regs, unsigned int depth, void *ssp) -{ - struct frame_tail *tail; - struct thread_info *real_thread_info = THREAD_INFO(ssp); - struct stacktrace_state sts; - - sts.depth = depth; - sts.state = state; - *current_thread_info() = *real_thread_info; - - if (!current) - fiq_debugger_printf(state, "current NULL\n"); - else - fiq_debugger_printf(state, "pid: %d comm: %s\n", - current->pid, current->comm); - fiq_debugger_dump_regs(state, 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; - fiq_debugger_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 fiq_debugger_do_ps(struct fiq_debugger_state *state) { struct task_struct *g; @@ -692,9 +477,7 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) { fiq_debugger_help(state); } else if (!strcmp(cmd, "pc")) { - fiq_debugger_printf(state, " pc %08x cpsr %08x mode %s\n", - regs->ARM_pc, regs->ARM_cpsr, - mode_name(regs->ARM_cpsr)); + fiq_debugger_dump_pc(state, regs); } else if (!strcmp(cmd, "regs")) { fiq_debugger_dump_regs(state, regs); } else if (!strcmp(cmd, "allregs")) { diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_arm.c b/drivers/staging/android/fiq_debugger/fiq_debugger_arm.c new file mode 100644 index 000000000000..fca12737d224 --- /dev/null +++ b/drivers/staging/android/fiq_debugger/fiq_debugger_arm.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2014 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. + * + */ + +#include +#include + +#include + +#include "fiq_debugger_priv.h" + +static char *mode_name(unsigned cpsr) +{ + switch (cpsr & MODE_MASK) { + case USR_MODE: return "USR"; + case FIQ_MODE: return "FIQ"; + case IRQ_MODE: return "IRQ"; + case SVC_MODE: return "SVC"; + case ABT_MODE: return "ABT"; + case UND_MODE: return "UND"; + case SYSTEM_MODE: return "SYS"; + default: return "???"; + } +} + +void fiq_debugger_dump_pc(struct fiq_debugger_state *state, + const struct pt_regs *regs) +{ + fiq_debugger_printf(state, " pc %08x cpsr %08x mode %s\n", + regs->ARM_pc, regs->ARM_cpsr, mode_name(regs->ARM_cpsr)); +} + +void fiq_debugger_dump_regs(struct fiq_debugger_state *state, + const struct pt_regs *regs) +{ + fiq_debugger_printf(state, + " r0 %08x r1 %08x r2 %08x r3 %08x\n", + regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); + fiq_debugger_printf(state, + " r4 %08x r5 %08x r6 %08x r7 %08x\n", + regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7); + fiq_debugger_printf(state, + " r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n", + regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp, + mode_name(regs->ARM_cpsr)); + fiq_debugger_printf(state, + " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc, + regs->ARM_cpsr); +} + +struct mode_regs { + unsigned long sp_svc; + unsigned long lr_svc; + unsigned long spsr_svc; + + unsigned long sp_abt; + unsigned long lr_abt; + unsigned long spsr_abt; + + unsigned long sp_und; + unsigned long lr_und; + unsigned long spsr_und; + + unsigned long sp_irq; + unsigned long lr_irq; + unsigned long spsr_irq; + + unsigned long r8_fiq; + unsigned long r9_fiq; + unsigned long r10_fiq; + unsigned long r11_fiq; + unsigned long r12_fiq; + unsigned long sp_fiq; + unsigned long lr_fiq; + unsigned long spsr_fiq; +}; + +static void __naked get_mode_regs(struct mode_regs *regs) +{ + asm volatile ( + "mrs r1, cpsr\n" + "msr cpsr_c, #0xd3 @(SVC_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xd7 @(ABT_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xdb @(UND_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xd2 @(IRQ_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xd1 @(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r8 - r14}\n" + "mrs r2, spsr\n" + "stmia r0!, {r2}\n" + "msr cpsr_c, r1\n" + "bx lr\n"); +} + + +void fiq_debugger_dump_allregs(struct fiq_debugger_state *state, + const struct pt_regs *regs) +{ + struct mode_regs mode_regs; + unsigned long mode = regs->ARM_cpsr & MODE_MASK; + + fiq_debugger_dump_regs(state, regs); + get_mode_regs(&mode_regs); + + fiq_debugger_printf(state, + "%csvc: sp %08x lr %08x spsr %08x\n", + mode == SVC_MODE ? '*' : ' ', + mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc); + fiq_debugger_printf(state, + "%cabt: sp %08x lr %08x spsr %08x\n", + mode == ABT_MODE ? '*' : ' ', + mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt); + fiq_debugger_printf(state, + "%cund: sp %08x lr %08x spsr %08x\n", + mode == UND_MODE ? '*' : ' ', + mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und); + fiq_debugger_printf(state, + "%cirq: sp %08x lr %08x spsr %08x\n", + mode == IRQ_MODE ? '*' : ' ', + mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq); + fiq_debugger_printf(state, + "%cfiq: r8 %08x r9 %08x r10 %08x r11 %08x r12 %08x\n", + mode == FIQ_MODE ? '*' : ' ', + mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq, + mode_regs.r11_fiq, mode_regs.r12_fiq); + fiq_debugger_printf(state, + " fiq: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq); +} + +struct stacktrace_state { + struct fiq_debugger_state *state; + unsigned int depth; +}; + +static int report_trace(struct stackframe *frame, void *d) +{ + struct stacktrace_state *sts = d; + + if (sts->depth) { + fiq_debugger_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; + } + fiq_debugger_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))) { + fiq_debugger_printf(state, " invalid frame pointer %p\n", + tail); + return NULL; + } + if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) { + fiq_debugger_printf(state, + " failed to copy frame pointer %p\n", tail); + return NULL; + } + + fiq_debugger_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 fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, + const struct pt_regs *regs, unsigned int depth, void *ssp) +{ + struct frame_tail *tail; + struct thread_info *real_thread_info = THREAD_INFO(ssp); + struct stacktrace_state sts; + + sts.depth = depth; + sts.state = state; + *current_thread_info() = *real_thread_info; + + if (!current) + fiq_debugger_printf(state, "current NULL\n"); + else + fiq_debugger_printf(state, "pid: %d comm: %s\n", + current->pid, current->comm); + fiq_debugger_dump_regs(state, 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; + fiq_debugger_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); +} diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_priv.h b/drivers/staging/android/fiq_debugger/fiq_debugger_priv.h new file mode 100644 index 000000000000..746cf1b5d55b --- /dev/null +++ b/drivers/staging/android/fiq_debugger/fiq_debugger_priv.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 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 _FIQ_DEBUGGER_PRIV_H_ +#define _FIQ_DEBUGGER_PRIV_H_ + +#define THREAD_INFO(sp) ((struct thread_info *) \ + ((unsigned long)(sp) & ~(THREAD_SIZE - 1))) + +struct fiq_debugger_state; +struct pt_regs; + +int fiq_debugger_printf(void *cookie, const char *fmt, ...); + +void fiq_debugger_dump_pc(struct fiq_debugger_state *state, + const struct pt_regs *regs); +void fiq_debugger_dump_regs(struct fiq_debugger_state *state, + const struct pt_regs *regs); +void fiq_debugger_dump_allregs(struct fiq_debugger_state *state, + const struct pt_regs *regs); +void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, + const struct pt_regs *regs, unsigned int depth, void *ssp); + +#endif From 0fe3a992c344cd7d79d97537a9aafb58ad97189e Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 2 Apr 2014 18:49:39 -0700 Subject: [PATCH 205/475] fiq_debugger: add ARM64 support Add fiq_debugger_arm64.c that implements the platform-specific functions. Change-Id: I4d8b96777bb8503a93d4eb47bbde8e018740a5bf Signed-off-by: Colin Cross --- drivers/staging/android/fiq_debugger/Kconfig | 2 +- drivers/staging/android/fiq_debugger/Makefile | 1 + .../android/fiq_debugger/fiq_debugger_arm64.c | 202 ++++++++++++++++++ 3 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c diff --git a/drivers/staging/android/fiq_debugger/Kconfig b/drivers/staging/android/fiq_debugger/Kconfig index 803fb59cc82f..d87978cc7dee 100644 --- a/drivers/staging/android/fiq_debugger/Kconfig +++ b/drivers/staging/android/fiq_debugger/Kconfig @@ -1,7 +1,7 @@ config FIQ_DEBUGGER bool "FIQ Mode Serial Debugger" default n - depends on ARM + depends on ARM || ARM64 help The FIQ serial debugger can accept commands even when the kernel is unresponsive due to being stuck with interrupts diff --git a/drivers/staging/android/fiq_debugger/Makefile b/drivers/staging/android/fiq_debugger/Makefile index 1e203aed8c62..c95da7eb1b33 100644 --- a/drivers/staging/android/fiq_debugger/Makefile +++ b/drivers/staging/android/fiq_debugger/Makefile @@ -1,2 +1,3 @@ obj-y += fiq_debugger.o obj-$(CONFIG_ARM) += fiq_debugger_arm.o +obj-$(CONFIG_ARM64) += fiq_debugger_arm64.o \ No newline at end of file diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c new file mode 100644 index 000000000000..cbca82b70533 --- /dev/null +++ b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2014 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. + * + */ + +#include +#include + +#include "fiq_debugger_priv.h" + +static char *mode_name(const struct pt_regs *regs) +{ + if (compat_user_mode(regs)) { + return "USR"; + } else { + switch (processor_mode(regs)) { + case PSR_MODE_EL0t: return "EL0t"; + case PSR_MODE_EL1t: return "EL1t"; + case PSR_MODE_EL1h: return "EL1h"; + case PSR_MODE_EL2t: return "EL2t"; + case PSR_MODE_EL2h: return "EL2h"; + default: return "???"; + } + } +} + +void fiq_debugger_dump_pc(struct fiq_debugger_state *state, + const struct pt_regs *regs) +{ + fiq_debugger_printf(state, " pc %016lx cpsr %08lx mode %s\n", + regs->pc, regs->pstate, mode_name(regs)); +} + +void fiq_debugger_dump_regs_aarch32(struct fiq_debugger_state *state, + const struct pt_regs *regs) +{ + fiq_debugger_printf(state, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + regs->compat_usr(0), regs->compat_usr(1), + regs->compat_usr(2), regs->compat_usr(3)); + fiq_debugger_printf(state, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + regs->compat_usr(4), regs->compat_usr(5), + regs->compat_usr(6), regs->compat_usr(7)); + fiq_debugger_printf(state, " r8 %08x r9 %08x r10 %08x r11 %08x\n", + regs->compat_usr(8), regs->compat_usr(9), + regs->compat_usr(10), regs->compat_usr(11)); + fiq_debugger_printf(state, " ip %08x sp %08x lr %08x pc %08x\n", + regs->compat_usr(12), regs->compat_sp, + regs->compat_lr, regs->pc); + fiq_debugger_printf(state, " cpsr %08x (%s)\n", + regs->pstate, mode_name(regs)); +} + +void fiq_debugger_dump_regs_aarch64(struct fiq_debugger_state *state, + const struct pt_regs *regs) +{ + + fiq_debugger_printf(state, " x0 %016lx x1 %016lx\n", + regs->regs[0], regs->regs[1]); + fiq_debugger_printf(state, " x2 %016lx x3 %016lx\n", + regs->regs[2], regs->regs[3]); + fiq_debugger_printf(state, " x4 %016lx x5 %016lx\n", + regs->regs[4], regs->regs[5]); + fiq_debugger_printf(state, " x6 %016lx x7 %016lx\n", + regs->regs[6], regs->regs[7]); + fiq_debugger_printf(state, " x8 %016lx x9 %016lx\n", + regs->regs[8], regs->regs[9]); + fiq_debugger_printf(state, " x10 %016lx x11 %016lx\n", + regs->regs[10], regs->regs[11]); + fiq_debugger_printf(state, " x12 %016lx x13 %016lx\n", + regs->regs[12], regs->regs[13]); + fiq_debugger_printf(state, " x14 %016lx x15 %016lx\n", + regs->regs[14], regs->regs[15]); + fiq_debugger_printf(state, " x16 %016lx x17 %016lx\n", + regs->regs[16], regs->regs[17]); + fiq_debugger_printf(state, " x18 %016lx x19 %016lx\n", + regs->regs[18], regs->regs[19]); + fiq_debugger_printf(state, " x20 %016lx x21 %016lx\n", + regs->regs[20], regs->regs[21]); + fiq_debugger_printf(state, " x22 %016lx x23 %016lx\n", + regs->regs[22], regs->regs[23]); + fiq_debugger_printf(state, " x24 %016lx x25 %016lx\n", + regs->regs[24], regs->regs[25]); + fiq_debugger_printf(state, " x26 %016lx x27 %016lx\n", + regs->regs[26], regs->regs[27]); + fiq_debugger_printf(state, " x28 %016lx x29 %016lx\n", + regs->regs[28], regs->regs[29]); + fiq_debugger_printf(state, " x30 %016lx sp %016lx\n", + regs->regs[30], regs->sp); + fiq_debugger_printf(state, " pc %016lx cpsr %08x (%s)\n", + regs->pc, regs->pstate, mode_name(regs)); +} + +void fiq_debugger_dump_regs(struct fiq_debugger_state *state, + const struct pt_regs *regs) +{ + if (compat_user_mode(regs)) + fiq_debugger_dump_regs_aarch32(state, regs); + else + fiq_debugger_dump_regs_aarch64(state, regs); +} + +#define READ_SPECIAL_REG(x) ({ \ + u64 val; \ + asm volatile ("mrs %0, " # x : "=r"(val)); \ + val; \ +}) + +void fiq_debugger_dump_allregs(struct fiq_debugger_state *state, + const struct pt_regs *regs) +{ + u32 pstate = READ_SPECIAL_REG(CurrentEl); + bool in_el2 = (pstate & PSR_MODE_MASK) >= PSR_MODE_EL2t; + + fiq_debugger_dump_regs(state, regs); + + fiq_debugger_printf(state, " sp_el0 %016lx\n", + READ_SPECIAL_REG(sp_el0)); + + if (in_el2) + fiq_debugger_printf(state, " sp_el1 %016lx\n", + READ_SPECIAL_REG(sp_el1)); + + fiq_debugger_printf(state, " elr_el1 %016lx\n", + READ_SPECIAL_REG(elr_el1)); + + fiq_debugger_printf(state, " spsr_el1 %08lx\n", + READ_SPECIAL_REG(spsr_el1)); + + if (in_el2) { + fiq_debugger_printf(state, " spsr_irq %08lx\n", + READ_SPECIAL_REG(spsr_irq)); + fiq_debugger_printf(state, " spsr_abt %08lx\n", + READ_SPECIAL_REG(spsr_abt)); + fiq_debugger_printf(state, " spsr_und %08lx\n", + READ_SPECIAL_REG(spsr_und)); + fiq_debugger_printf(state, " spsr_fiq %08lx\n", + READ_SPECIAL_REG(spsr_fiq)); + fiq_debugger_printf(state, " spsr_el2 %08lx\n", + READ_SPECIAL_REG(elr_el2)); + fiq_debugger_printf(state, " spsr_el2 %08lx\n", + READ_SPECIAL_REG(spsr_el2)); + } +} + +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) { + fiq_debugger_printf(sts->state, "%pF:\n", frame->pc); + fiq_debugger_printf(sts->state, + " pc %016lx sp %016lx fp %016lx\n", + frame->pc, frame->sp, frame->fp); + sts->depth--; + return 0; + } + fiq_debugger_printf(sts->state, " ...\n"); + + return sts->depth == 0; +} + +void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, + const struct pt_regs *regs, unsigned int depth, void *ssp) +{ + struct thread_info *real_thread_info = THREAD_INFO(ssp); + struct stacktrace_state sts; + + sts.depth = depth; + sts.state = state; + *current_thread_info() = *real_thread_info; + + if (!current) + fiq_debugger_printf(state, "current NULL\n"); + else + fiq_debugger_printf(state, "pid: %d comm: %s\n", + current->pid, current->comm); + fiq_debugger_dump_regs(state, regs); + + if (!user_mode(regs)) { + struct stackframe frame; + frame.fp = regs->regs[29]; + frame.sp = regs->sp; + frame.pc = regs->pc; + fiq_debugger_printf(state, "\n"); + walk_stackframe(&frame, report_trace, &sts); + } +} From 592a91a75606da03b6bbd494522a0490115c5a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 2 May 2014 19:52:54 -0700 Subject: [PATCH 206/475] fiq_debugger: Call fiq_debugger_printf through a function pointer from cpu specific code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows the output from the register and stack trace code to be sent elsewhere. Change-Id: I41bb0d5a25e1b9ca55feef5dbd675818b2f832d5 Signed-off-by: Arve Hjønnevåg --- .../android/fiq_debugger/fiq_debugger.c | 67 ++++++------ .../android/fiq_debugger/fiq_debugger_arm.c | 58 +++++----- .../android/fiq_debugger/fiq_debugger_arm64.c | 102 +++++++++--------- .../android/fiq_debugger/fiq_debugger_priv.h | 15 +-- 4 files changed, 123 insertions(+), 119 deletions(-) diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c index 5516d3113b94..7d6b4ae8a2cd 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -54,6 +54,7 @@ struct fiq_debugger_state { #ifdef CONFIG_FIQ_GLUE struct fiq_glue_handler handler; #endif + struct fiq_debugger_output output; int fiq; int uart_irq; @@ -229,18 +230,19 @@ static void fiq_debugger_dump_kernel_log(struct fiq_debugger_state *state) } } -int fiq_debugger_printf(void *cookie, const char *fmt, ...) +static void fiq_debugger_printf(struct fiq_debugger_output *output, + const char *fmt, ...) { - struct fiq_debugger_state *state = cookie; + struct fiq_debugger_state *state; char buf[256]; va_list ap; + state = container_of(output, struct fiq_debugger_state, output); va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); fiq_debugger_puts(state, buf); - return state->debug_abort; } /* Safe outside fiq context */ @@ -267,13 +269,13 @@ static void fiq_debugger_dump_irqs(struct fiq_debugger_state *state) int n; struct irq_desc *desc; - fiq_debugger_printf(state, + fiq_debugger_printf(&state->output, "irqnr total since-last status name\n"); for_each_irq_desc(n, desc) { struct irqaction *act = desc->action; if (!act && !kstat_irqs(n)) continue; - fiq_debugger_printf(state, "%5d: %10u %11u %8x %s\n", n, + fiq_debugger_printf(&state->output, "%5d: %10u %11u %8x %s\n", n, kstat_irqs(n), kstat_irqs(n) - state->last_irqs[n], desc->status_use_accessors, @@ -289,18 +291,18 @@ static void fiq_debugger_do_ps(struct fiq_debugger_state *state) unsigned task_state; static const char stat_nam[] = "RSDTtZX"; - fiq_debugger_printf(state, "pid ppid prio task pc\n"); + fiq_debugger_printf(&state->output, "pid ppid prio task pc\n"); read_lock(&tasklist_lock); do_each_thread(g, p) { task_state = p->state ? __ffs(p->state) + 1 : 0; - fiq_debugger_printf(state, + fiq_debugger_printf(&state->output, "%5d %5d %4d ", p->pid, p->parent->pid, p->prio); - fiq_debugger_printf(state, "%-13.13s %c", p->comm, + fiq_debugger_printf(&state->output, "%-13.13s %c", p->comm, task_state >= sizeof(stat_nam) ? '?' : stat_nam[task_state]); if (task_state == TASK_RUNNING) - fiq_debugger_printf(state, " running\n"); + fiq_debugger_printf(&state->output, " running\n"); else - fiq_debugger_printf(state, " %08lx\n", + fiq_debugger_printf(&state->output, " %08lx\n", thread_saved_pc(p)); } while_each_thread(g, p); read_unlock(&tasklist_lock); @@ -332,7 +334,7 @@ static void fiq_debugger_end_syslog_dump(struct fiq_debugger_state *state) static void fiq_debugger_do_sysrq(struct fiq_debugger_state *state, char rq) { if ((rq == 'g' || rq == 'G') && !fiq_kgdb_enable) { - fiq_debugger_printf(state, "sysrq-g blocked\n"); + fiq_debugger_printf(&state->output, "sysrq-g blocked\n"); return; } fiq_debugger_begin_syslog_dump(state); @@ -344,11 +346,11 @@ static void fiq_debugger_do_sysrq(struct fiq_debugger_state *state, char rq) static void fiq_debugger_do_kgdb(struct fiq_debugger_state *state) { if (!fiq_kgdb_enable) { - fiq_debugger_printf(state, "kgdb through fiq debugger not enabled\n"); + fiq_debugger_printf(&state->output, "kgdb through fiq debugger not enabled\n"); return; } - fiq_debugger_printf(state, "enabling console and triggering kgdb\n"); + fiq_debugger_printf(&state->output, "enabling console and triggering kgdb\n"); state->console_enable = true; handle_sysrq('g'); } @@ -361,7 +363,7 @@ static void fiq_debugger_schedule_work(struct fiq_debugger_state *state, spin_lock_irqsave(&state->work_lock, flags); if (state->work_cmd[0] != '\0') { - fiq_debugger_printf(state, "work command processor busy\n"); + fiq_debugger_printf(&state->output, "work command processor busy\n"); spin_unlock_irqrestore(&state->work_lock, flags); return; } @@ -398,7 +400,7 @@ static void fiq_debugger_work(struct work_struct *work) else kernel_restart(NULL); } else { - fiq_debugger_printf(state, "unknown work command '%s'\n", + fiq_debugger_printf(&state->output, "unknown work command '%s'\n", work_cmd); } } @@ -422,7 +424,7 @@ static void fiq_debugger_irq_exec(struct fiq_debugger_state *state, char *cmd) static void fiq_debugger_help(struct fiq_debugger_state *state) { - fiq_debugger_printf(state, + fiq_debugger_printf(&state->output, "FIQ Debugger commands:\n" " pc PC status\n" " regs Register dump\n" @@ -433,18 +435,18 @@ static void fiq_debugger_help(struct fiq_debugger_state *state) " irqs Interupt status\n" " kmsg Kernel log\n" " version Kernel version\n"); - fiq_debugger_printf(state, + fiq_debugger_printf(&state->output, " 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"); - fiq_debugger_printf(state, + fiq_debugger_printf(&state->output, " ps Process list\n" " sysrq sysrq options\n" " sysrq Execute sysrq with \n"); #ifdef CONFIG_KGDB - fiq_debugger_printf(state, + fiq_debugger_printf(&state->output, " kgdb Enter kernel debugger\n"); #endif } @@ -477,13 +479,13 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) { fiq_debugger_help(state); } else if (!strcmp(cmd, "pc")) { - fiq_debugger_dump_pc(state, regs); + fiq_debugger_dump_pc(&state->output, regs); } else if (!strcmp(cmd, "regs")) { - fiq_debugger_dump_regs(state, regs); + fiq_debugger_dump_regs(&state->output, regs); } else if (!strcmp(cmd, "allregs")) { - fiq_debugger_dump_allregs(state, regs); + fiq_debugger_dump_allregs(&state->output, regs); } else if (!strcmp(cmd, "bt")) { - fiq_debugger_dump_stacktrace(state, regs, 100, svc_sp); + fiq_debugger_dump_stacktrace(&state->output, regs, 100, svc_sp); } else if (!strncmp(cmd, "reset", 5)) { cmd += 5; while (*cmd == ' ') @@ -500,29 +502,29 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, } else if (!strcmp(cmd, "kmsg")) { fiq_debugger_dump_kernel_log(state); } else if (!strcmp(cmd, "version")) { - fiq_debugger_printf(state, "%s\n", linux_banner); + fiq_debugger_printf(&state->output, "%s\n", linux_banner); } else if (!strcmp(cmd, "sleep")) { state->no_sleep = false; - fiq_debugger_printf(state, "enabling sleep\n"); + fiq_debugger_printf(&state->output, "enabling sleep\n"); } else if (!strcmp(cmd, "nosleep")) { state->no_sleep = true; - fiq_debugger_printf(state, "disabling sleep\n"); + fiq_debugger_printf(&state->output, "disabling sleep\n"); } else if (!strcmp(cmd, "console")) { - fiq_debugger_printf(state, "console mode\n"); + fiq_debugger_printf(&state->output, "console mode\n"); fiq_debugger_uart_flush(state); state->console_enable = true; } else if (!strcmp(cmd, "cpu")) { - fiq_debugger_printf(state, "cpu %d\n", state->current_cpu); + fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu); } else if (!strncmp(cmd, "cpu ", 4)) { unsigned long cpu = 0; if (strict_strtoul(cmd + 4, 10, &cpu) == 0) fiq_debugger_switch_cpu(state, cpu); else - fiq_debugger_printf(state, "invalid cpu\n"); - fiq_debugger_printf(state, "cpu %d\n", state->current_cpu); + fiq_debugger_printf(&state->output, "invalid cpu\n"); + fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu); } else { if (state->debug_busy) { - fiq_debugger_printf(state, + fiq_debugger_printf(&state->output, "command processor busy. trying to abort.\n"); state->debug_abort = -1; } else { @@ -645,7 +647,7 @@ static bool fiq_debugger_handle_uart_interrupt(struct fiq_debugger_state *state, MAX_UNHANDLED_FIQ_COUNT) return false; - fiq_debugger_printf(state, + fiq_debugger_printf(&state->output, "fiq_debugger: cpu %d not responding, " "reverting to cpu %d\n", state->current_cpu, this_cpu); @@ -1053,6 +1055,7 @@ static int fiq_debugger_probe(struct platform_device *pdev) return -EINVAL; state = kzalloc(sizeof(*state), GFP_KERNEL); + state->output.printf = fiq_debugger_printf; setup_timer(&state->sleep_timer, fiq_debugger_sleep_timer_expired, (unsigned long)state); state->pdata = pdata; diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_arm.c b/drivers/staging/android/fiq_debugger/fiq_debugger_arm.c index fca12737d224..8b3e0137be1a 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger_arm.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger_arm.c @@ -34,27 +34,27 @@ static char *mode_name(unsigned cpsr) } } -void fiq_debugger_dump_pc(struct fiq_debugger_state *state, +void fiq_debugger_dump_pc(struct fiq_debugger_output *output, const struct pt_regs *regs) { - fiq_debugger_printf(state, " pc %08x cpsr %08x mode %s\n", + output->printf(output, " pc %08x cpsr %08x mode %s\n", regs->ARM_pc, regs->ARM_cpsr, mode_name(regs->ARM_cpsr)); } -void fiq_debugger_dump_regs(struct fiq_debugger_state *state, +void fiq_debugger_dump_regs(struct fiq_debugger_output *output, const struct pt_regs *regs) { - fiq_debugger_printf(state, + output->printf(output, " r0 %08x r1 %08x r2 %08x r3 %08x\n", regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3); - fiq_debugger_printf(state, + output->printf(output, " r4 %08x r5 %08x r6 %08x r7 %08x\n", regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7); - fiq_debugger_printf(state, + output->printf(output, " r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n", regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp, mode_name(regs->ARM_cpsr)); - fiq_debugger_printf(state, + output->printf(output, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc, regs->ARM_cpsr); @@ -112,43 +112,43 @@ static void __naked get_mode_regs(struct mode_regs *regs) } -void fiq_debugger_dump_allregs(struct fiq_debugger_state *state, +void fiq_debugger_dump_allregs(struct fiq_debugger_output *output, const struct pt_regs *regs) { struct mode_regs mode_regs; unsigned long mode = regs->ARM_cpsr & MODE_MASK; - fiq_debugger_dump_regs(state, regs); + fiq_debugger_dump_regs(output, regs); get_mode_regs(&mode_regs); - fiq_debugger_printf(state, + output->printf(output, "%csvc: sp %08x lr %08x spsr %08x\n", mode == SVC_MODE ? '*' : ' ', mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc); - fiq_debugger_printf(state, + output->printf(output, "%cabt: sp %08x lr %08x spsr %08x\n", mode == ABT_MODE ? '*' : ' ', mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt); - fiq_debugger_printf(state, + output->printf(output, "%cund: sp %08x lr %08x spsr %08x\n", mode == UND_MODE ? '*' : ' ', mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und); - fiq_debugger_printf(state, + output->printf(output, "%cirq: sp %08x lr %08x spsr %08x\n", mode == IRQ_MODE ? '*' : ' ', mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq); - fiq_debugger_printf(state, + output->printf(output, "%cfiq: r8 %08x r9 %08x r10 %08x r11 %08x r12 %08x\n", mode == FIQ_MODE ? '*' : ' ', mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq, mode_regs.r11_fiq, mode_regs.r12_fiq); - fiq_debugger_printf(state, + output->printf(output, " fiq: sp %08x lr %08x spsr %08x\n", mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq); } struct stacktrace_state { - struct fiq_debugger_state *state; + struct fiq_debugger_output *output; unsigned int depth; }; @@ -157,14 +157,14 @@ static int report_trace(struct stackframe *frame, void *d) struct stacktrace_state *sts = d; if (sts->depth) { - fiq_debugger_printf(sts->state, + sts->output->printf(sts->output, " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n", frame->pc, frame->pc, frame->lr, frame->lr, frame->sp, frame->fp); sts->depth--; return 0; } - fiq_debugger_printf(sts->state, " ...\n"); + sts->output->printf(sts->output, " ...\n"); return sts->depth == 0; } @@ -175,24 +175,24 @@ struct frame_tail { unsigned long lr; } __attribute__((packed)); -static struct frame_tail *user_backtrace(struct fiq_debugger_state *state, +static struct frame_tail *user_backtrace(struct fiq_debugger_output *output, struct frame_tail *tail) { struct frame_tail buftail[2]; /* Also check accessibility of one struct frame_tail beyond */ if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) { - fiq_debugger_printf(state, " invalid frame pointer %p\n", + output->printf(output, " invalid frame pointer %p\n", tail); return NULL; } if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) { - fiq_debugger_printf(state, + output->printf(output, " failed to copy frame pointer %p\n", tail); return NULL; } - fiq_debugger_printf(state, " %p\n", buftail[0].lr); + output->printf(output, " %p\n", buftail[0].lr); /* frame pointers should strictly progress back up the stack * (towards higher addresses) */ @@ -202,7 +202,7 @@ static struct frame_tail *user_backtrace(struct fiq_debugger_state *state, return buftail[0].fp-1; } -void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, +void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output, const struct pt_regs *regs, unsigned int depth, void *ssp) { struct frame_tail *tail; @@ -210,15 +210,15 @@ void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, struct stacktrace_state sts; sts.depth = depth; - sts.state = state; + sts.output = output; *current_thread_info() = *real_thread_info; if (!current) - fiq_debugger_printf(state, "current NULL\n"); + output->printf(output, "current NULL\n"); else - fiq_debugger_printf(state, "pid: %d comm: %s\n", + output->printf(output, "pid: %d comm: %s\n", current->pid, current->comm); - fiq_debugger_dump_regs(state, regs); + fiq_debugger_dump_regs(output, regs); if (!user_mode(regs)) { struct stackframe frame; @@ -226,7 +226,7 @@ void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, frame.sp = regs->ARM_sp; frame.lr = regs->ARM_lr; frame.pc = regs->ARM_pc; - fiq_debugger_printf(state, + output->printf(output, " pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n", regs->ARM_pc, regs->ARM_pc, regs->ARM_lr, regs->ARM_lr, regs->ARM_sp, regs->ARM_fp); @@ -236,5 +236,5 @@ void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, tail = ((struct frame_tail *) regs->ARM_fp) - 1; while (depth-- && tail && !((unsigned long) tail & 3)) - tail = user_backtrace(state, tail); + tail = user_backtrace(output, tail); } diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c index cbca82b70533..99c6584fcfa5 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c @@ -34,79 +34,79 @@ static char *mode_name(const struct pt_regs *regs) } } -void fiq_debugger_dump_pc(struct fiq_debugger_state *state, +void fiq_debugger_dump_pc(struct fiq_debugger_output *output, const struct pt_regs *regs) { - fiq_debugger_printf(state, " pc %016lx cpsr %08lx mode %s\n", + output->printf(output, " pc %016lx cpsr %08lx mode %s\n", regs->pc, regs->pstate, mode_name(regs)); } -void fiq_debugger_dump_regs_aarch32(struct fiq_debugger_state *state, +void fiq_debugger_dump_regs_aarch32(struct fiq_debugger_output *output, const struct pt_regs *regs) { - fiq_debugger_printf(state, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + output->printf(output, " r0 %08x r1 %08x r2 %08x r3 %08x\n", regs->compat_usr(0), regs->compat_usr(1), regs->compat_usr(2), regs->compat_usr(3)); - fiq_debugger_printf(state, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + output->printf(output, " r4 %08x r5 %08x r6 %08x r7 %08x\n", regs->compat_usr(4), regs->compat_usr(5), regs->compat_usr(6), regs->compat_usr(7)); - fiq_debugger_printf(state, " r8 %08x r9 %08x r10 %08x r11 %08x\n", + output->printf(output, " r8 %08x r9 %08x r10 %08x r11 %08x\n", regs->compat_usr(8), regs->compat_usr(9), regs->compat_usr(10), regs->compat_usr(11)); - fiq_debugger_printf(state, " ip %08x sp %08x lr %08x pc %08x\n", + output->printf(output, " ip %08x sp %08x lr %08x pc %08x\n", regs->compat_usr(12), regs->compat_sp, regs->compat_lr, regs->pc); - fiq_debugger_printf(state, " cpsr %08x (%s)\n", + output->printf(output, " cpsr %08x (%s)\n", regs->pstate, mode_name(regs)); } -void fiq_debugger_dump_regs_aarch64(struct fiq_debugger_state *state, +void fiq_debugger_dump_regs_aarch64(struct fiq_debugger_output *output, const struct pt_regs *regs) { - fiq_debugger_printf(state, " x0 %016lx x1 %016lx\n", + output->printf(output, " x0 %016lx x1 %016lx\n", regs->regs[0], regs->regs[1]); - fiq_debugger_printf(state, " x2 %016lx x3 %016lx\n", + output->printf(output, " x2 %016lx x3 %016lx\n", regs->regs[2], regs->regs[3]); - fiq_debugger_printf(state, " x4 %016lx x5 %016lx\n", + output->printf(output, " x4 %016lx x5 %016lx\n", regs->regs[4], regs->regs[5]); - fiq_debugger_printf(state, " x6 %016lx x7 %016lx\n", + output->printf(output, " x6 %016lx x7 %016lx\n", regs->regs[6], regs->regs[7]); - fiq_debugger_printf(state, " x8 %016lx x9 %016lx\n", + output->printf(output, " x8 %016lx x9 %016lx\n", regs->regs[8], regs->regs[9]); - fiq_debugger_printf(state, " x10 %016lx x11 %016lx\n", + output->printf(output, " x10 %016lx x11 %016lx\n", regs->regs[10], regs->regs[11]); - fiq_debugger_printf(state, " x12 %016lx x13 %016lx\n", + output->printf(output, " x12 %016lx x13 %016lx\n", regs->regs[12], regs->regs[13]); - fiq_debugger_printf(state, " x14 %016lx x15 %016lx\n", + output->printf(output, " x14 %016lx x15 %016lx\n", regs->regs[14], regs->regs[15]); - fiq_debugger_printf(state, " x16 %016lx x17 %016lx\n", + output->printf(output, " x16 %016lx x17 %016lx\n", regs->regs[16], regs->regs[17]); - fiq_debugger_printf(state, " x18 %016lx x19 %016lx\n", + output->printf(output, " x18 %016lx x19 %016lx\n", regs->regs[18], regs->regs[19]); - fiq_debugger_printf(state, " x20 %016lx x21 %016lx\n", + output->printf(output, " x20 %016lx x21 %016lx\n", regs->regs[20], regs->regs[21]); - fiq_debugger_printf(state, " x22 %016lx x23 %016lx\n", + output->printf(output, " x22 %016lx x23 %016lx\n", regs->regs[22], regs->regs[23]); - fiq_debugger_printf(state, " x24 %016lx x25 %016lx\n", + output->printf(output, " x24 %016lx x25 %016lx\n", regs->regs[24], regs->regs[25]); - fiq_debugger_printf(state, " x26 %016lx x27 %016lx\n", + output->printf(output, " x26 %016lx x27 %016lx\n", regs->regs[26], regs->regs[27]); - fiq_debugger_printf(state, " x28 %016lx x29 %016lx\n", + output->printf(output, " x28 %016lx x29 %016lx\n", regs->regs[28], regs->regs[29]); - fiq_debugger_printf(state, " x30 %016lx sp %016lx\n", + output->printf(output, " x30 %016lx sp %016lx\n", regs->regs[30], regs->sp); - fiq_debugger_printf(state, " pc %016lx cpsr %08x (%s)\n", + output->printf(output, " pc %016lx cpsr %08x (%s)\n", regs->pc, regs->pstate, mode_name(regs)); } -void fiq_debugger_dump_regs(struct fiq_debugger_state *state, +void fiq_debugger_dump_regs(struct fiq_debugger_output *output, const struct pt_regs *regs) { if (compat_user_mode(regs)) - fiq_debugger_dump_regs_aarch32(state, regs); + fiq_debugger_dump_regs_aarch32(output, regs); else - fiq_debugger_dump_regs_aarch64(state, regs); + fiq_debugger_dump_regs_aarch64(output, regs); } #define READ_SPECIAL_REG(x) ({ \ @@ -115,45 +115,45 @@ void fiq_debugger_dump_regs(struct fiq_debugger_state *state, val; \ }) -void fiq_debugger_dump_allregs(struct fiq_debugger_state *state, +void fiq_debugger_dump_allregs(struct fiq_debugger_output *output, const struct pt_regs *regs) { u32 pstate = READ_SPECIAL_REG(CurrentEl); bool in_el2 = (pstate & PSR_MODE_MASK) >= PSR_MODE_EL2t; - fiq_debugger_dump_regs(state, regs); + fiq_debugger_dump_regs(output, regs); - fiq_debugger_printf(state, " sp_el0 %016lx\n", + output->printf(output, " sp_el0 %016lx\n", READ_SPECIAL_REG(sp_el0)); if (in_el2) - fiq_debugger_printf(state, " sp_el1 %016lx\n", + output->printf(output, " sp_el1 %016lx\n", READ_SPECIAL_REG(sp_el1)); - fiq_debugger_printf(state, " elr_el1 %016lx\n", + output->printf(output, " elr_el1 %016lx\n", READ_SPECIAL_REG(elr_el1)); - fiq_debugger_printf(state, " spsr_el1 %08lx\n", + output->printf(output, " spsr_el1 %08lx\n", READ_SPECIAL_REG(spsr_el1)); if (in_el2) { - fiq_debugger_printf(state, " spsr_irq %08lx\n", + output->printf(output, " spsr_irq %08lx\n", READ_SPECIAL_REG(spsr_irq)); - fiq_debugger_printf(state, " spsr_abt %08lx\n", + output->printf(output, " spsr_abt %08lx\n", READ_SPECIAL_REG(spsr_abt)); - fiq_debugger_printf(state, " spsr_und %08lx\n", + output->printf(output, " spsr_und %08lx\n", READ_SPECIAL_REG(spsr_und)); - fiq_debugger_printf(state, " spsr_fiq %08lx\n", + output->printf(output, " spsr_fiq %08lx\n", READ_SPECIAL_REG(spsr_fiq)); - fiq_debugger_printf(state, " spsr_el2 %08lx\n", + output->printf(output, " spsr_el2 %08lx\n", READ_SPECIAL_REG(elr_el2)); - fiq_debugger_printf(state, " spsr_el2 %08lx\n", + output->printf(output, " spsr_el2 %08lx\n", READ_SPECIAL_REG(spsr_el2)); } } struct stacktrace_state { - struct fiq_debugger_state *state; + struct fiq_debugger_output *output; unsigned int depth; }; @@ -162,41 +162,41 @@ static int report_trace(struct stackframe *frame, void *d) struct stacktrace_state *sts = d; if (sts->depth) { - fiq_debugger_printf(sts->state, "%pF:\n", frame->pc); - fiq_debugger_printf(sts->state, + sts->output->printf(sts->output, "%pF:\n", frame->pc); + sts->output->printf(sts->output, " pc %016lx sp %016lx fp %016lx\n", frame->pc, frame->sp, frame->fp); sts->depth--; return 0; } - fiq_debugger_printf(sts->state, " ...\n"); + sts->output->printf(sts->output, " ...\n"); return sts->depth == 0; } -void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, +void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output, const struct pt_regs *regs, unsigned int depth, void *ssp) { struct thread_info *real_thread_info = THREAD_INFO(ssp); struct stacktrace_state sts; sts.depth = depth; - sts.state = state; + sts.output = output; *current_thread_info() = *real_thread_info; if (!current) - fiq_debugger_printf(state, "current NULL\n"); + output->printf(output, "current NULL\n"); else - fiq_debugger_printf(state, "pid: %d comm: %s\n", + output->printf(output, "pid: %d comm: %s\n", current->pid, current->comm); - fiq_debugger_dump_regs(state, regs); + fiq_debugger_dump_regs(output, regs); if (!user_mode(regs)) { struct stackframe frame; frame.fp = regs->regs[29]; frame.sp = regs->sp; frame.pc = regs->pc; - fiq_debugger_printf(state, "\n"); + output->printf(output, "\n"); walk_stackframe(&frame, report_trace, &sts); } } diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_priv.h b/drivers/staging/android/fiq_debugger/fiq_debugger_priv.h index 746cf1b5d55b..d5d051f727a8 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger_priv.h +++ b/drivers/staging/android/fiq_debugger/fiq_debugger_priv.h @@ -19,18 +19,19 @@ #define THREAD_INFO(sp) ((struct thread_info *) \ ((unsigned long)(sp) & ~(THREAD_SIZE - 1))) -struct fiq_debugger_state; +struct fiq_debugger_output { + void (*printf)(struct fiq_debugger_output *output, const char *fmt, ...); +}; + struct pt_regs; -int fiq_debugger_printf(void *cookie, const char *fmt, ...); - -void fiq_debugger_dump_pc(struct fiq_debugger_state *state, +void fiq_debugger_dump_pc(struct fiq_debugger_output *output, const struct pt_regs *regs); -void fiq_debugger_dump_regs(struct fiq_debugger_state *state, +void fiq_debugger_dump_regs(struct fiq_debugger_output *output, const struct pt_regs *regs); -void fiq_debugger_dump_allregs(struct fiq_debugger_state *state, +void fiq_debugger_dump_allregs(struct fiq_debugger_output *output, const struct pt_regs *regs); -void fiq_debugger_dump_stacktrace(struct fiq_debugger_state *state, +void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output, const struct pt_regs *regs, unsigned int depth, void *ssp); #endif From 8591e0429ecd6379ce33596f65fe8cfed47433ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 2 May 2014 20:31:07 -0700 Subject: [PATCH 207/475] fiq_debugger: Add fiq_watchdog_triggered api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dumps registers and stacktrace into console-ramoops when called from a watchdog fiq. Change-Id: Ib6fab5a52f670db18e64214d5e4890e8292a749c Signed-off-by: Arve Hjønnevåg --- drivers/staging/android/fiq_debugger/Kconfig | 6 ++ drivers/staging/android/fiq_debugger/Makefile | 3 +- .../android/fiq_debugger/fiq_watchdog.c | 56 +++++++++++++++++++ .../android/fiq_debugger/fiq_watchdog.h | 20 +++++++ 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/android/fiq_debugger/fiq_watchdog.c create mode 100644 drivers/staging/android/fiq_debugger/fiq_watchdog.h diff --git a/drivers/staging/android/fiq_debugger/Kconfig b/drivers/staging/android/fiq_debugger/Kconfig index d87978cc7dee..56f7f999377e 100644 --- a/drivers/staging/android/fiq_debugger/Kconfig +++ b/drivers/staging/android/fiq_debugger/Kconfig @@ -41,3 +41,9 @@ config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE help If enabled, this puts the fiq debugger into console mode by default. Otherwise, the fiq debugger will start out in debug mode. + +config FIQ_WATCHDOG + bool + select FIQ_DEBUGGER + select PSTORE_RAM + default n diff --git a/drivers/staging/android/fiq_debugger/Makefile b/drivers/staging/android/fiq_debugger/Makefile index c95da7eb1b33..a7ca4871cad3 100644 --- a/drivers/staging/android/fiq_debugger/Makefile +++ b/drivers/staging/android/fiq_debugger/Makefile @@ -1,3 +1,4 @@ obj-y += fiq_debugger.o obj-$(CONFIG_ARM) += fiq_debugger_arm.o -obj-$(CONFIG_ARM64) += fiq_debugger_arm64.o \ No newline at end of file +obj-$(CONFIG_ARM64) += fiq_debugger_arm64.o +obj-$(CONFIG_FIQ_WATCHDOG) += fiq_watchdog.o diff --git a/drivers/staging/android/fiq_debugger/fiq_watchdog.c b/drivers/staging/android/fiq_debugger/fiq_watchdog.c new file mode 100644 index 000000000000..194b54138417 --- /dev/null +++ b/drivers/staging/android/fiq_debugger/fiq_watchdog.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#include "fiq_watchdog.h" +#include "fiq_debugger_priv.h" + +static DEFINE_RAW_SPINLOCK(fiq_watchdog_lock); + +static void fiq_watchdog_printf(struct fiq_debugger_output *output, + const char *fmt, ...) +{ + char buf[256]; + va_list ap; + int len; + + va_start(ap, fmt); + len = vscnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + ramoops_console_write_buf(buf, len); +} + +struct fiq_debugger_output fiq_watchdog_output = { + .printf = fiq_watchdog_printf, +}; + +void fiq_watchdog_triggered(const struct pt_regs *regs, void *svc_sp) +{ + char msg[24]; + int len; + + raw_spin_lock(&fiq_watchdog_lock); + + len = scnprintf(msg, sizeof(msg), "watchdog fiq cpu %d\n", + THREAD_INFO(svc_sp)->cpu); + ramoops_console_write_buf(msg, len); + + fiq_debugger_dump_stacktrace(&fiq_watchdog_output, regs, 100, svc_sp); + + raw_spin_unlock(&fiq_watchdog_lock); +} diff --git a/drivers/staging/android/fiq_debugger/fiq_watchdog.h b/drivers/staging/android/fiq_debugger/fiq_watchdog.h new file mode 100644 index 000000000000..c6b507f8d976 --- /dev/null +++ b/drivers/staging/android/fiq_debugger/fiq_watchdog.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _FIQ_WATCHDOG_H_ +#define _FIQ_WATCHDOG_H_ + +void fiq_watchdog_triggered(const struct pt_regs *regs, void *svc_sp); + +#endif From e4123d4ac6df42faa5b51c3410349578648e6003 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 4 Dec 2015 11:32:21 -0800 Subject: [PATCH 208/475] fiq_debugger: Build fixes for 4.1 Small build updates for 4.1 Signed-off-by: John Stultz --- drivers/staging/android/fiq_debugger/fiq_debugger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c index 7d6b4ae8a2cd..7f056831dbff 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -517,7 +517,7 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state, fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu); } else if (!strncmp(cmd, "cpu ", 4)) { unsigned long cpu = 0; - if (strict_strtoul(cmd + 4, 10, &cpu) == 0) + if (kstrtoul(cmd + 4, 10, &cpu) == 0) fiq_debugger_switch_cpu(state, cpu); else fiq_debugger_printf(&state->output, "invalid cpu\n"); @@ -1144,7 +1144,7 @@ static int fiq_debugger_probe(struct platform_device *pdev) if (state->wakeup_irq >= 0) { ret = request_irq(state->wakeup_irq, fiq_debugger_wakeup_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_DISABLED, + IRQF_TRIGGER_FALLING, "debug-wakeup", state); if (ret) { pr_err("serial_debugger: " From b3bb0e5bbf24780300621d69e6ffbdcc11c72358 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 209/475] 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 | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 732e993b564b..67359c7f35db 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7542,6 +7542,14 @@ 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) { /* @@ -7566,8 +7574,10 @@ void ___might_sleep(const char *file, int line, int preempt_offset) rcu_sleep_check(); /* WARN_ON_ONCE() by default, no rate limit reqd. */ if ((preempt_count_equals(preempt_offset) && !irqs_disabled() && - !is_idle_task(current)) || - system_state != SYSTEM_RUNNING || oops_in_progress) + !is_idle_task(current)) || 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 7abc800d049ee8e673acab90032e7cfbad1600a6 Mon Sep 17 00:00:00 2001 From: Riley Andrews Date: Fri, 2 Oct 2015 00:39:53 -0700 Subject: [PATCH 210/475] sched: add sched blocked tracepoint which dumps out context of sleep. Decare war on uninterruptible sleep. Add a tracepoint which walks the kernel stack and dumps the first non-scheduler function called before the scheduler is invoked. Change-Id: I19e965d5206329360a92cbfe2afcc8c30f65c229 Signed-off-by: Riley Andrews --- include/trace/events/sched.h | 26 +++++++++++++++++++++++++- kernel/sched/fair.c | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 9b90c57517a9..3211890ee7d5 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -219,7 +219,7 @@ DECLARE_EVENT_CLASS(sched_process_template, DEFINE_EVENT(sched_process_template, sched_process_free, TP_PROTO(struct task_struct *p), TP_ARGS(p)); - + /* * Tracepoint for a task exiting: @@ -373,6 +373,30 @@ DEFINE_EVENT(sched_stat_template, sched_stat_blocked, TP_PROTO(struct task_struct *tsk, u64 delay), TP_ARGS(tsk, delay)); +/* + * Tracepoint for recording the cause of uninterruptible sleep. + */ +TRACE_EVENT(sched_blocked_reason, + + TP_PROTO(struct task_struct *tsk), + + TP_ARGS(tsk), + + TP_STRUCT__entry( + __field( pid_t, pid ) + __field( void*, caller ) + __field( bool, io_wait ) + ), + + TP_fast_assign( + __entry->pid = tsk->pid; + __entry->caller = (void*)get_wchan(tsk); + __entry->io_wait = tsk->in_iowait; + ), + + TP_printk("pid=%d iowait=%d caller=%pS", __entry->pid, __entry->io_wait, __entry->caller) +); + /* * Tracepoint for accounting runtime (time the task is executing * on a CPU). diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index cfdc0e61066c..ba24bfe4ac51 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2931,6 +2931,7 @@ static void enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se) } trace_sched_stat_blocked(tsk, delta); + trace_sched_blocked_reason(tsk); /* * Blocking time is in units of nanosecs, so shift by From 4dedd9f124703207895777ac6e91dacde0f7cc17 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 19 Sep 2011 16:42:44 -0700 Subject: [PATCH 211/475] 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 22e4463d1787..0846b39b4ca6 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -178,7 +178,12 @@ static inline int performance_multiplier(unsigned long nr_iowaiters, unsigned lo /* for higher loadavg, we are more reluctant */ - mult += 2 * get_loadavg(load); + /* + * 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_iowaiters; From 90fbee8f979d91bb43e9923135000424adfdcaf0 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 15 Jun 2011 17:21:57 -0700 Subject: [PATCH 212/475] 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 9f7c21c22477..57eca132962f 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -62,19 +62,6 @@ EXPORT_PER_CPU_SYMBOL(cpu_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 /* @@ -251,14 +238,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 d2ca8c38f9c4..7c73824def83 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -290,4 +290,11 @@ bool cpu_wait_death(unsigned int cpu, int seconds); bool cpu_report_death(void); #endif /* #ifdef CONFIG_HOTPLUG_CPU */ +#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 85ff5e26e23b..74e411acb1f2 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -827,3 +827,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 0ba0a4266372e054ab0d5a9a2b3293301a302d89 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 15 Jun 2011 17:44:50 -0700 Subject: [PATCH 213/475] 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 4adfb46e3ee9..b208a85012f5 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -80,6 +80,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(); @@ -89,6 +90,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); } void __show_regs(struct pt_regs *regs) From 008bd616dd9b039a6bcf888b22ba9145a1b1c0b3 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Tue, 22 Jun 2010 11:26:45 -0700 Subject: [PATCH 214/475] 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 c15aa75f5227..b262c5336e72 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 659879a56dba..6e099e577578 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 c0af1a1281c8..b02ae1463a15 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -10,6 +10,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_CPUFREQ_DT) += cpufreq-dt.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 177c7680c1a8..3ac01621cd1f 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -487,6 +487,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 2561571b3b47325944e1550ac8ab2d558b3627b2 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 16 Feb 2012 16:27:59 -0800 Subject: [PATCH 215/475] 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 077dfe8ba59ef1b167a91d58bb56e051eed52348 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 6 Apr 2012 01:13:09 -0700 Subject: [PATCH 216/475] 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 7b2dc7eab966e0f4b175b58349d127438720c76a Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 6 Apr 2012 19:50:12 -0700 Subject: [PATCH 217/475] 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 27c22a708fc9ee9c40a6724872c02bea043677cb Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 6 Apr 2012 19:59:36 -0700 Subject: [PATCH 218/475] 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 4ca4034e0b618f47b1fcf0485ab01753a65bb9b5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 13 Apr 2012 20:18:02 -0700 Subject: [PATCH 219/475] 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 3b14df50831d1268a80963dbfb2c7a36b87f3057 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 17 Apr 2012 17:39:34 -0700 Subject: [PATCH 220/475] 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 b262c5336e72..40894dadabcd 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 759fcdd8d72529e83bdf3eb261d69aa85fcf5bda Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 19 Apr 2012 12:52:48 -0700 Subject: [PATCH 221/475] 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 1c31ed48a26e4cd4220ee1d4129ab947666f2e03 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 20 Apr 2012 13:18:32 -0700 Subject: [PATCH 222/475] 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 c48fcaa0882efb934aab89a4b5cca893b88b845c Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 2 Apr 2012 17:17:14 -0700 Subject: [PATCH 223/475] 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 40894dadabcd..4172cada2c0e 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 910dea04b441f2dc8836bc632123b2aa73848f3b Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 23 Apr 2012 21:22:45 -0700 Subject: [PATCH 224/475] 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 1ea7b7724c5bfca1536e794217cc9ef3fabcab29 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 23 Apr 2012 20:42:41 -0700 Subject: [PATCH 225/475] 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 4172cada2c0e..4fcaa43dbe5c 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 b486bd1808cf53408fb7800b477cde1693f58318 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 26 Apr 2012 21:41:40 -0700 Subject: [PATCH 226/475] 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 c4241c980ca37eb60a6d0c3a1d0b5054151c6e7a Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 3 May 2012 00:16:55 -0700 Subject: [PATCH 227/475] 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 4fcaa43dbe5c..509b179cba0e 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 b62e5a1c49ff7bfff874cefbc467782da78263f6 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 1 May 2012 14:10:31 -0700 Subject: [PATCH 228/475] 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 122c60b938ff48f6069757ca5d9e59b5ab4e1e01 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 10 May 2012 23:28:06 -0700 Subject: [PATCH 229/475] 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 f0b83a6710a6c363e6bc1665ccd18f0bfb6f0dc1 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 11 May 2012 11:06:09 -0700 Subject: [PATCH 230/475] 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 5722666d2d623b470cd2311a4e819925ce1cbc87 Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Wed, 27 Jun 2012 10:12:04 -0700 Subject: [PATCH 231/475] 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 d00caa6461d6e4832999a9db21b55fb13d79c31a Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Wed, 27 Jun 2012 12:55:56 -0700 Subject: [PATCH 232/475] 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 4429f8b070267aa3c01c617ca0909f237aad27a5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 16 Jul 2012 17:07:15 -0700 Subject: [PATCH 233/475] 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 f9e8727032d1a979aa8d57aec1b68062520c4dd5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 16 Jul 2012 17:32:44 -0700 Subject: [PATCH 234/475] 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 984e8bbd617e89650dae9b08a986b1737f2ec018 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 24 Sep 2012 18:03:58 -0700 Subject: [PATCH 235/475] 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 53d77c97f2d9f5884f056434c9692397d167f20e Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 3 Oct 2012 00:39:56 -0700 Subject: [PATCH 236/475] 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 916d0566e4a0d474e6fbee59d76e579c740684ce Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 5 Nov 2012 13:09:03 -0800 Subject: [PATCH 237/475] 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 f5b4e661c15486616c73eafb739337c7a6e46427 Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Thu, 1 Nov 2012 09:59:52 +0800 Subject: [PATCH 238/475] 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 2aadfa75842fa8925fe665cb38eb54dadef92f68 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 8 Oct 2012 20:14:34 -0700 Subject: [PATCH 239/475] 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 dc202c38d36161b1b8fe6f1cd49b690ee6d08dcc Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 28 Nov 2012 17:56:09 -0800 Subject: [PATCH 240/475] 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 4727e1aa45b97397fdc32d805f2b40906eddf55d Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 28 Nov 2012 17:58:17 -0800 Subject: [PATCH 241/475] 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 e337153485e0e7a394215014f4b8035b7eee5aea Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 8 Nov 2012 15:06:55 -0800 Subject: [PATCH 242/475] 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 fbc1d52f84d6acdefdfc1a174b17e6e9a475b855 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 14 Nov 2012 11:41:21 -0800 Subject: [PATCH 243/475] 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 509b179cba0e..b15f6d265eda 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 482f37e3d8128fc370b68a7a0979fb66211f8461 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 7 Dec 2012 20:08:45 -0800 Subject: [PATCH 244/475] 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 2c2b492e77e9b46f6db04059c9cc6068f538b239 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 11 Dec 2012 16:05:03 -0800 Subject: [PATCH 245/475] 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 a6d605140ef01ff7d3c5ce2ce3d78f5e806907b8 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 14 Dec 2012 17:31:19 -0800 Subject: [PATCH 246/475] 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 959433fa2f30b74f4310375443a677e324607dc5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 18 Dec 2012 17:50:10 -0800 Subject: [PATCH 247/475] 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 74f0d695dd30194ecd9edebb10a4375abc4d79fb Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 19 Dec 2012 16:06:48 -0800 Subject: [PATCH 248/475] 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 583695f13bbcc33f356a3fe598c30b10bd0c5c48 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 18 Dec 2012 17:50:44 -0800 Subject: [PATCH 249/475] 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 1dc7486fd81019314463d5077d8b4d13c41f3b34 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 20 Dec 2012 15:51:00 -0800 Subject: [PATCH 250/475] 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 b4f2820d791c456ba57ea2ab7848dae2f8135af4 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 21 Dec 2012 15:13:01 -0800 Subject: [PATCH 251/475] 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 b15f6d265eda..4d8ae34f7bba 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 c5ec6c67245652b5f268afbe200e88da867acf9d Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 21 Dec 2012 15:32:21 -0800 Subject: [PATCH 252/475] 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 06371b575fa7823a9a95fbf27f82b4a706e7847e Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Sun, 23 Dec 2012 12:28:49 -0800 Subject: [PATCH 253/475] 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 c7ad1e1b900f63c175f16b503755402c48fef0c6 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 2 Jan 2013 13:14:00 -0800 Subject: [PATCH 254/475] 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 939e7f1aa6120c60efd45908abfc9cc73850629e Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Mon, 7 Jan 2013 14:15:51 +0800 Subject: [PATCH 255/475] 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 c9d7bc6364eab675ab5b6c390570f03a97c30300 Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Mon, 25 Feb 2013 23:48:04 +0900 Subject: [PATCH 256/475] 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 4d8ae34f7bba..ac8a37e0c76a 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 6b2fd6c4f23cbeb7ce522b833c26e86848ad3dc9 Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Fri, 22 Feb 2013 11:39:18 +0800 Subject: [PATCH 257/475] 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 cc80bd4dfe9d61b2b56bea6e632eac8b70614cf2 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 20 Mar 2013 15:40:46 -0700 Subject: [PATCH 258/475] 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 75f9b06a38c833ad66dd41e8e066aa0012b2d421 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 21 Mar 2013 20:46:00 -0700 Subject: [PATCH 259/475] 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 f587d09cb66b24d441d9df08a9fbce5ab7af9102 Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Tue, 16 Apr 2013 21:52:50 +0900 Subject: [PATCH 260/475] 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 f71b480fd5f272d929084800e37b5b07cbd3ffdf Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 22 Apr 2013 16:44:58 -0700 Subject: [PATCH 261/475] 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 aedd63a502f2796d7dd0bf3d3144d13610a27419 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 5 Apr 2013 13:25:21 -0700 Subject: [PATCH 262/475] 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 83720c33a9005029e98a8e730af34d9347c69896 Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Tue, 23 Apr 2013 22:32:01 +0900 Subject: [PATCH 263/475] 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 7a5c8bae5cbe4922225071786be8b1809f06ea1f Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Thu, 16 May 2013 12:07:23 +0800 Subject: [PATCH 264/475] 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 e8ad1a85f65221837ec3a16f725ddad8271b851d Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Fri, 26 Apr 2013 13:30:51 +0800 Subject: [PATCH 265/475] 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 6728dfa91a0c77293c3f6d71b86054a593fc39bf Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Sun, 25 Aug 2013 19:23:34 +0900 Subject: [PATCH 266/475] 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 8db7e96a2cc871710e3e9fe88c57f06391366d42 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 16 May 2013 14:58:52 +0530 Subject: [PATCH 267/475] cpufreq: interactive: Remove unnecessary cpu_online() check Cpufreq no longer calls governor callback for offlined cpus. i.e. All policy->cpus are guaranteed to be online. Hence we don't need explicit check to see if cpu is online or not. Change-Id: I9ad85ea4addd5b4a40952e59ed730dd15e328690 Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq_interactive.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index a66748e13b38..378835baa844 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -1073,9 +1073,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, switch (event) { case CPUFREQ_GOV_START: - if (!cpu_online(policy->cpu)) - return -EINVAL; - mutex_lock(&gov_lock); freq_table = From 4d300368ff3648ed99c33d78cc614d1d39d59a6b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 16 May 2013 14:58:53 +0530 Subject: [PATCH 268/475] cpufreq: interactive: Move definition of cpufreq_gov_interactive downwards This moves definition of cpufreq_gov_interactive towards the bottom of file, so that we don't have to add prototype of cpufreq_governor_interactive() in the beginning of file. Change-Id: I04bd1004954eb36502c5cd7e35d3d7274cddaf95 Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq_interactive.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 378835baa844..b0685c74a907 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -117,19 +117,6 @@ 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); - -#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 inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, cputime64_t *wall) { @@ -1182,6 +1169,16 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, return 0; } +#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_nop_timer(unsigned long data) { } From 5690bb14628d1ff8874ed82c2284066096f1eb38 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 16 May 2013 14:58:54 +0530 Subject: [PATCH 269/475] cpufreq: Interactive: Implement per policy instances of governor If we have a multi-package system, where we have multiple instances of struct policy (per package), currently we can't have multiple instances of same governor. i.e. We can't have multiple instances of Interactive governor for multiple packages. This is a bottleneck for multicluster system, where we want different packages to use Interactive governor, but with different tunables. This patch uses the infrastructure provided by earlier patches pushed in Mainline in v3.10-rc1/rc2 and implements per policy instances of Interactive governor. Change-Id: I70436d4a5a45c6cb6edf37f3e46d0b9fbc930982 [toddpoynor@google.com: merge with later code, minor changes] Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq_interactive.c | 627 ++++++++++++++++---------- 1 file changed, 382 insertions(+), 245 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index b0685c74a907..66c096dcb1a2 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -36,8 +36,6 @@ #define CREATE_TRACE_POINTS #include -static int active_count; - struct cpufreq_interactive_cpuinfo { struct timer_list cpu_timer; struct timer_list cpu_slack_timer; @@ -64,58 +62,62 @@ 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; - -/* Go to hi speed when CPU load at or above this value. */ -#define DEFAULT_GO_HISPEED_LOAD 99 -static unsigned long go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; - /* Target load. Lower values result in higher CPU speeds. */ #define DEFAULT_TARGET_LOAD 90 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. - */ -#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) -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 = 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 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; -/* 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; - -/* - * Max additional time to wait in idle, beyond timer_rate, at speeds above - * minimum before wakeup to reduce speed, or -1 if unnecessary. - */ +struct cpufreq_interactive_tunables { + int usage_count; + /* Hi speed to bump to from lo speed when load burst (default max) */ + unsigned int hispeed_freq; + /* Go to hi speed when CPU load at or above this value. */ +#define DEFAULT_GO_HISPEED_LOAD 99 + unsigned long go_hispeed_load; + /* Target load. Lower values result in higher CPU speeds. */ + spinlock_t target_loads_lock; + unsigned int *target_loads; + int ntarget_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) + unsigned long min_sample_time; + /* + * The sample rate of the timer used to increase frequency + */ + unsigned long timer_rate; + /* + * Wait this long before raising speed above hispeed, by default a + * single timer interval. + */ + spinlock_t above_hispeed_delay_lock; + unsigned int *above_hispeed_delay; + int nabove_hispeed_delay; + /* Non-zero means indefinite speed boost active */ + int boost_val; + /* Duration of a boot pulse in usecs */ + int boostpulse_duration_val; + /* End time of boost pulse in ktime converted to usecs */ + u64 boostpulse_endtime; + /* + * 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; + int timer_slack_val; + bool io_is_busy; +}; -static bool io_is_busy; +/* For cases where we have single governor instance for system */ +struct cpufreq_interactive_tunables *common_tunables; + +static struct attribute_group *get_sysfs_attr(void); static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, cputime64_t *wall) @@ -140,8 +142,10 @@ static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, return jiffies_to_usecs(idle_time); } -static inline cputime64_t get_cpu_idle_time(unsigned int cpu, - cputime64_t *wall) +static inline cputime64_t get_cpu_idle_time( + unsigned int cpu, + cputime64_t *wall, + bool io_is_busy) { u64 idle_time = get_cpu_idle_time_us(cpu, wall); @@ -156,20 +160,24 @@ static inline cputime64_t get_cpu_idle_time(unsigned int cpu, static void cpufreq_interactive_timer_resched( struct cpufreq_interactive_cpuinfo *pcpu) { + struct cpufreq_interactive_tunables *tunables = + pcpu->policy->governor_data; unsigned long expires; unsigned long flags; spin_lock_irqsave(&pcpu->load_lock, flags); pcpu->time_in_idle = get_cpu_idle_time(smp_processor_id(), - &pcpu->time_in_idle_timestamp); + &pcpu->time_in_idle_timestamp, + tunables->io_is_busy); pcpu->cputime_speedadj = 0; pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; - expires = jiffies + usecs_to_jiffies(timer_rate); + expires = jiffies + usecs_to_jiffies(tunables->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); + if (tunables->timer_slack_val >= 0 && + pcpu->target_freq > pcpu->policy->min) { + expires += usecs_to_jiffies(tunables->timer_slack_val); mod_timer_pinned(&pcpu->cpu_slack_timer, expires); } @@ -180,58 +188,66 @@ static void cpufreq_interactive_timer_resched( * The cpu_timer and cpu_slack_timer must be deactivated when calling this * function. */ -static void cpufreq_interactive_timer_start(int cpu) +static void cpufreq_interactive_timer_start( + struct cpufreq_interactive_tunables *tunables, int cpu) { struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); - unsigned long expires = jiffies + usecs_to_jiffies(timer_rate); + unsigned long expires = jiffies + + usecs_to_jiffies(tunables->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); + if (tunables->timer_slack_val >= 0 && + pcpu->target_freq > pcpu->policy->min) { + expires += usecs_to_jiffies(tunables->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); + get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp, + tunables->io_is_busy); 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) +static unsigned int freq_to_above_hispeed_delay( + struct cpufreq_interactive_tunables *tunables, + unsigned int freq) { int i; unsigned int ret; unsigned long flags; - spin_lock_irqsave(&above_hispeed_delay_lock, flags); + spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); - for (i = 0; i < nabove_hispeed_delay - 1 && - freq >= above_hispeed_delay[i+1]; i += 2) + for (i = 0; i < tunables->nabove_hispeed_delay - 1 && + freq >= tunables->above_hispeed_delay[i+1]; i += 2) ; - ret = above_hispeed_delay[i]; - spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + ret = tunables->above_hispeed_delay[i]; + spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); return ret; } -static unsigned int freq_to_targetload(unsigned int freq) +static unsigned int freq_to_targetload( + struct cpufreq_interactive_tunables *tunables, unsigned int freq) { int i; unsigned int ret; unsigned long flags; - spin_lock_irqsave(&target_loads_lock, flags); + spin_lock_irqsave(&tunables->target_loads_lock, flags); - for (i = 0; i < ntarget_loads - 1 && freq >= target_loads[i+1]; i += 2) + for (i = 0; i < tunables->ntarget_loads - 1 && + freq >= tunables->target_loads[i+1]; i += 2) ; - ret = target_loads[i]; - spin_unlock_irqrestore(&target_loads_lock, flags); + ret = tunables->target_loads[i]; + spin_unlock_irqrestore(&tunables->target_loads_lock, flags); return ret; } @@ -240,9 +256,8 @@ static unsigned int freq_to_targetload(unsigned int freq) * 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 loadadjfreq) +static unsigned int choose_freq(struct cpufreq_interactive_cpuinfo *pcpu, + unsigned int loadadjfreq) { unsigned int freq = pcpu->policy->cur; unsigned int prevfreq, freqmin, freqmax; @@ -254,7 +269,7 @@ static unsigned int choose_freq( do { prevfreq = freq; - tl = freq_to_targetload(freq); + tl = freq_to_targetload(pcpu->policy->governor_data, freq); /* * Find the lowest frequency where the computed load is less @@ -329,13 +344,15 @@ static unsigned int choose_freq( static u64 update_load(int cpu) { struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); + struct cpufreq_interactive_tunables *tunables = + pcpu->policy->governor_data; u64 now; u64 now_idle; unsigned int delta_idle; unsigned int delta_time; u64 active_time; - now_idle = get_cpu_idle_time(cpu, &now); + now_idle = get_cpu_idle_time(cpu, &now, tunables->io_is_busy); delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle); delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp); @@ -359,6 +376,8 @@ static void cpufreq_interactive_timer(unsigned long data) int cpu_load; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, data); + struct cpufreq_interactive_tunables *tunables = + pcpu->policy->governor_data; unsigned int new_freq; unsigned int loadadjfreq; unsigned int index; @@ -382,25 +401,25 @@ 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; + boosted = tunables->boost_val || now < tunables->boostpulse_endtime; - if (cpu_load >= go_hispeed_load || boosted) { - if (pcpu->target_freq < hispeed_freq) { - new_freq = hispeed_freq; + if (cpu_load >= tunables->go_hispeed_load || boosted) { + if (pcpu->target_freq < tunables->hispeed_freq) { + new_freq = tunables->hispeed_freq; } else { new_freq = choose_freq(pcpu, loadadjfreq); - if (new_freq < hispeed_freq) - new_freq = hispeed_freq; + if (new_freq < tunables->hispeed_freq) + new_freq = tunables->hispeed_freq; } } else { new_freq = choose_freq(pcpu, loadadjfreq); } - if (pcpu->target_freq >= hispeed_freq && + if (pcpu->target_freq >= tunables->hispeed_freq && new_freq > pcpu->target_freq && now - pcpu->hispeed_validate_time < - freq_to_above_hispeed_delay(pcpu->target_freq)) { + freq_to_above_hispeed_delay(tunables, pcpu->target_freq)) { trace_cpufreq_interactive_notyet( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); @@ -421,7 +440,8 @@ 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 (now - pcpu->floor_validate_time < min_sample_time) { + if (now - pcpu->floor_validate_time < + tunables->min_sample_time) { trace_cpufreq_interactive_notyet( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); @@ -437,7 +457,7 @@ static void cpufreq_interactive_timer(unsigned long data) * (or the indefinite boost is turned off). */ - if (!boosted || new_freq > hispeed_freq) { + if (!boosted || new_freq > tunables->hispeed_freq) { pcpu->floor_freq = new_freq; pcpu->floor_validate_time = now; } @@ -598,14 +618,16 @@ static void cpufreq_interactive_boost(void) int anyboost = 0; unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; + struct cpufreq_interactive_tunables *tunables; spin_lock_irqsave(&speedchange_cpumask_lock, flags); for_each_online_cpu(i) { pcpu = &per_cpu(cpuinfo, i); + tunables = pcpu->policy->governor_data; - if (pcpu->target_freq < hispeed_freq) { - pcpu->target_freq = hispeed_freq; + if (pcpu->target_freq < tunables->hispeed_freq) { + pcpu->target_freq = tunables->hispeed_freq; cpumask_set_cpu(i, &speedchange_cpumask); pcpu->hispeed_validate_time = ktime_to_us(ktime_get()); @@ -617,7 +639,7 @@ static void cpufreq_interactive_boost(void) * validated. */ - pcpu->floor_freq = hispeed_freq; + pcpu->floor_freq = tunables->hispeed_freq; pcpu->floor_validate_time = ktime_to_us(ktime_get()); } @@ -717,26 +739,27 @@ err: } static ssize_t show_target_loads( - struct kobject *kobj, struct attribute *attr, char *buf) + struct cpufreq_interactive_tunables *tunables, + char *buf) { int i; ssize_t ret = 0; unsigned long flags; - spin_lock_irqsave(&target_loads_lock, flags); + spin_lock_irqsave(&tunables->target_loads_lock, flags); - for (i = 0; i < ntarget_loads; i++) - ret += sprintf(buf + ret, "%u%s", target_loads[i], + for (i = 0; i < tunables->ntarget_loads; i++) + ret += sprintf(buf + ret, "%u%s", tunables->target_loads[i], i & 0x1 ? ":" : " "); ret += sprintf(buf + --ret, "\n"); - spin_unlock_irqrestore(&target_loads_lock, flags); + spin_unlock_irqrestore(&tunables->target_loads_lock, flags); return ret; } static ssize_t store_target_loads( - struct kobject *kobj, struct attribute *attr, const char *buf, - size_t count) + struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ntokens; unsigned int *new_target_loads = NULL; @@ -746,40 +769,37 @@ static ssize_t store_target_loads( 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) - kfree(target_loads); - target_loads = new_target_loads; - ntarget_loads = ntokens; - spin_unlock_irqrestore(&target_loads_lock, flags); + spin_lock_irqsave(&tunables->target_loads_lock, flags); + if (tunables->target_loads != default_target_loads) + kfree(tunables->target_loads); + tunables->target_loads = new_target_loads; + tunables->ntarget_loads = ntokens; + spin_unlock_irqrestore(&tunables->target_loads_lock, flags); return count; } -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) + struct cpufreq_interactive_tunables *tunables, char *buf) { int i; ssize_t ret = 0; unsigned long flags; - spin_lock_irqsave(&above_hispeed_delay_lock, flags); + spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); - for (i = 0; i < nabove_hispeed_delay; i++) - ret += sprintf(buf + ret, "%u%s", above_hispeed_delay[i], + for (i = 0; i < tunables->nabove_hispeed_delay; i++) + ret += sprintf(buf + ret, "%u%s", + tunables->above_hispeed_delay[i], i & 0x1 ? ":" : " "); ret += sprintf(buf + --ret, "\n"); - spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + spin_unlock_irqrestore(&tunables->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) + struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ntokens; unsigned int *new_above_hispeed_delay = NULL; @@ -789,29 +809,24 @@ static ssize_t store_above_hispeed_delay( 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); + spin_lock_irqsave(&tunables->above_hispeed_delay_lock, flags); + if (tunables->above_hispeed_delay != default_above_hispeed_delay) + kfree(tunables->above_hispeed_delay); + tunables->above_hispeed_delay = new_above_hispeed_delay; + tunables->nabove_hispeed_delay = ntokens; + spin_unlock_irqrestore(&tunables->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) +static ssize_t show_hispeed_freq(struct cpufreq_interactive_tunables *tunables, + char *buf) { - return sprintf(buf, "%u\n", hispeed_freq); + return sprintf(buf, "%u\n", tunables->hispeed_freq); } -static ssize_t store_hispeed_freq(struct kobject *kobj, - struct attribute *attr, const char *buf, - size_t count) +static ssize_t store_hispeed_freq(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ret; long unsigned int val; @@ -819,22 +834,18 @@ static ssize_t store_hispeed_freq(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - hispeed_freq = val; + tunables->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) +static ssize_t show_go_hispeed_load(struct cpufreq_interactive_tunables + *tunables, char *buf) { - return sprintf(buf, "%lu\n", go_hispeed_load); + return sprintf(buf, "%lu\n", tunables->go_hispeed_load); } -static ssize_t store_go_hispeed_load(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) +static ssize_t store_go_hispeed_load(struct cpufreq_interactive_tunables + *tunables, const char *buf, size_t count) { int ret; unsigned long val; @@ -842,21 +853,18 @@ static ssize_t store_go_hispeed_load(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - go_hispeed_load = val; + tunables->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) +static ssize_t show_min_sample_time(struct cpufreq_interactive_tunables + *tunables, char *buf) { - return sprintf(buf, "%lu\n", min_sample_time); + return sprintf(buf, "%lu\n", tunables->min_sample_time); } -static ssize_t store_min_sample_time(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) +static ssize_t store_min_sample_time(struct cpufreq_interactive_tunables + *tunables, const char *buf, size_t count) { int ret; unsigned long val; @@ -864,21 +872,18 @@ static ssize_t store_min_sample_time(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - min_sample_time = val; + tunables->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) +static ssize_t show_timer_rate(struct cpufreq_interactive_tunables *tunables, + char *buf) { - return sprintf(buf, "%lu\n", timer_rate); + return sprintf(buf, "%lu\n", tunables->timer_rate); } -static ssize_t store_timer_rate(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) +static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ret; unsigned long val; @@ -886,22 +891,18 @@ static ssize_t store_timer_rate(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - timer_rate = val; + tunables->timer_rate = val; return count; } -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) +static ssize_t show_timer_slack(struct cpufreq_interactive_tunables *tunables, + char *buf) { - return sprintf(buf, "%d\n", timer_slack_val); + return sprintf(buf, "%d\n", tunables->timer_slack_val); } -static ssize_t store_timer_slack( - struct kobject *kobj, struct attribute *attr, const char *buf, - size_t count) +static ssize_t store_timer_slack(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ret; unsigned long val; @@ -910,19 +911,17 @@ static ssize_t store_timer_slack( if (ret < 0) return ret; - timer_slack_val = val; + tunables->timer_slack_val = val; return count; } -define_one_global_rw(timer_slack); - -static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, +static ssize_t show_boost(struct cpufreq_interactive_tunables *tunables, char *buf) { - return sprintf(buf, "%d\n", boost_val); + return sprintf(buf, "%d\n", tunables->boost_val); } -static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, +static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables, const char *buf, size_t count) { int ret; @@ -932,9 +931,9 @@ static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, if (ret < 0) return ret; - boost_val = val; + tunables->boost_val = val; - if (boost_val) { + if (tunables->boost_val) { trace_cpufreq_interactive_boost("on"); cpufreq_interactive_boost(); } else { @@ -944,9 +943,7 @@ static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, return count; } -define_one_global_rw(boost); - -static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, +static ssize_t store_boostpulse(struct cpufreq_interactive_tunables *tunables, const char *buf, size_t count) { int ret; @@ -956,24 +953,21 @@ 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; + tunables->boostpulse_endtime = ktime_to_us(ktime_get()) + + tunables->boostpulse_duration_val; trace_cpufreq_interactive_boost("pulse"); cpufreq_interactive_boost(); return count; } -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) +static ssize_t show_boostpulse_duration(struct cpufreq_interactive_tunables + *tunables, char *buf) { - return sprintf(buf, "%d\n", boostpulse_duration_val); + return sprintf(buf, "%d\n", tunables->boostpulse_duration_val); } -static ssize_t store_boostpulse_duration( - struct kobject *kobj, struct attribute *attr, const char *buf, - size_t count) +static ssize_t store_boostpulse_duration(struct cpufreq_interactive_tunables + *tunables, const char *buf, size_t count) { int ret; unsigned long val; @@ -982,20 +976,18 @@ static ssize_t store_boostpulse_duration( if (ret < 0) return ret; - boostpulse_duration_val = val; + tunables->boostpulse_duration_val = val; return count; } -define_one_global_rw(boostpulse_duration); - -static ssize_t show_io_is_busy(struct kobject *kobj, - struct attribute *attr, char *buf) +static ssize_t show_io_is_busy(struct cpufreq_interactive_tunables *tunables, + char *buf) { - return sprintf(buf, "%u\n", io_is_busy); + return sprintf(buf, "%u\n", tunables->io_is_busy); } -static ssize_t store_io_is_busy(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) +static ssize_t store_io_is_busy(struct cpufreq_interactive_tunables *tunables, + const char *buf, size_t count) { int ret; unsigned long val; @@ -1003,33 +995,137 @@ static ssize_t store_io_is_busy(struct kobject *kobj, ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; - io_is_busy = val; + tunables->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); +/* + * Create show/store routines + * - sys: One governor instance for complete SYSTEM + * - pol: One governor instance per struct cpufreq_policy + */ +#define show_gov_pol_sys(file_name) \ +static ssize_t show_##file_name##_gov_sys \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return show_##file_name(common_tunables, buf); \ +} \ + \ +static ssize_t show_##file_name##_gov_pol \ +(struct cpufreq_policy *policy, char *buf) \ +{ \ + return show_##file_name(policy->governor_data, buf); \ +} -static struct attribute *interactive_attributes[] = { - &target_loads_attr.attr, - &above_hispeed_delay_attr.attr, - &hispeed_freq_attr.attr, - &go_hispeed_load_attr.attr, - &min_sample_time_attr.attr, - &timer_rate_attr.attr, - &timer_slack.attr, - &boost.attr, - &boostpulse.attr, - &boostpulse_duration.attr, - &io_is_busy_attr.attr, +#define store_gov_pol_sys(file_name) \ +static ssize_t store_##file_name##_gov_sys \ +(struct kobject *kobj, struct attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return store_##file_name(common_tunables, buf, count); \ +} \ + \ +static ssize_t store_##file_name##_gov_pol \ +(struct cpufreq_policy *policy, const char *buf, size_t count) \ +{ \ + return store_##file_name(policy->governor_data, buf, count); \ +} + +#define show_store_gov_pol_sys(file_name) \ +show_gov_pol_sys(file_name); \ +store_gov_pol_sys(file_name) + +show_store_gov_pol_sys(target_loads); +show_store_gov_pol_sys(above_hispeed_delay); +show_store_gov_pol_sys(hispeed_freq); +show_store_gov_pol_sys(go_hispeed_load); +show_store_gov_pol_sys(min_sample_time); +show_store_gov_pol_sys(timer_rate); +show_store_gov_pol_sys(timer_slack); +show_store_gov_pol_sys(boost); +store_gov_pol_sys(boostpulse); +show_store_gov_pol_sys(boostpulse_duration); +show_store_gov_pol_sys(io_is_busy); + +#define gov_sys_attr_rw(_name) \ +static struct global_attr _name##_gov_sys = \ +__ATTR(_name, 0644, show_##_name##_gov_sys, store_##_name##_gov_sys) + +#define gov_pol_attr_rw(_name) \ +static struct freq_attr _name##_gov_pol = \ +__ATTR(_name, 0644, show_##_name##_gov_pol, store_##_name##_gov_pol) + +#define gov_sys_pol_attr_rw(_name) \ + gov_sys_attr_rw(_name); \ + gov_pol_attr_rw(_name) + +gov_sys_pol_attr_rw(target_loads); +gov_sys_pol_attr_rw(above_hispeed_delay); +gov_sys_pol_attr_rw(hispeed_freq); +gov_sys_pol_attr_rw(go_hispeed_load); +gov_sys_pol_attr_rw(min_sample_time); +gov_sys_pol_attr_rw(timer_rate); +gov_sys_pol_attr_rw(timer_slack); +gov_sys_pol_attr_rw(boost); +gov_sys_pol_attr_rw(boostpulse_duration); +gov_sys_pol_attr_rw(io_is_busy); + +static struct global_attr boostpulse_gov_sys = + __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_sys); + +static struct freq_attr boostpulse_gov_pol = + __ATTR(boostpulse, 0200, NULL, store_boostpulse_gov_pol); + +/* One Governor instance for entire system */ +static struct attribute *interactive_attributes_gov_sys[] = { + &target_loads_gov_sys.attr, + &above_hispeed_delay_gov_sys.attr, + &hispeed_freq_gov_sys.attr, + &go_hispeed_load_gov_sys.attr, + &min_sample_time_gov_sys.attr, + &timer_rate_gov_sys.attr, + &timer_slack_gov_sys.attr, + &boost_gov_sys.attr, + &boostpulse_gov_sys.attr, + &boostpulse_duration_gov_sys.attr, + &io_is_busy_gov_sys.attr, NULL, }; -static struct attribute_group interactive_attr_group = { - .attrs = interactive_attributes, +static struct attribute_group interactive_attr_group_gov_sys = { + .attrs = interactive_attributes_gov_sys, .name = "interactive", }; +/* Per policy governor instance */ +static struct attribute *interactive_attributes_gov_pol[] = { + &target_loads_gov_pol.attr, + &above_hispeed_delay_gov_pol.attr, + &hispeed_freq_gov_pol.attr, + &go_hispeed_load_gov_pol.attr, + &min_sample_time_gov_pol.attr, + &timer_rate_gov_pol.attr, + &timer_slack_gov_pol.attr, + &boost_gov_pol.attr, + &boostpulse_gov_pol.attr, + &boostpulse_duration_gov_pol.attr, + &io_is_busy_gov_pol.attr, + NULL, +}; + +static struct attribute_group interactive_attr_group_gov_pol = { + .attrs = interactive_attributes_gov_pol, + .name = "interactive", +}; + +static struct attribute_group *get_sysfs_attr(void) +{ + if (have_governor_per_policy()) + return &interactive_attr_group_gov_pol; + else + return &interactive_attr_group_gov_sys; +} + static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, unsigned long val, void *data) @@ -1057,15 +1153,88 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int j; struct cpufreq_interactive_cpuinfo *pcpu; struct cpufreq_frequency_table *freq_table; + struct cpufreq_interactive_tunables *tunables; + + if (have_governor_per_policy()) + tunables = policy->governor_data; + else + tunables = common_tunables; + + WARN_ON(!tunables && (event != CPUFREQ_GOV_POLICY_INIT)); switch (event) { + case CPUFREQ_GOV_POLICY_INIT: + if (have_governor_per_policy()) { + WARN_ON(tunables); + } else if (tunables) { + tunables->usage_count++; + policy->governor_data = tunables; + return 0; + } + + tunables = kzalloc(sizeof(*tunables), GFP_KERNEL); + if (!tunables) { + pr_err("%s: POLICY_INIT: kzalloc failed\n", __func__); + return -ENOMEM; + } + + rc = sysfs_create_group(get_governor_parent_kobj(policy), + get_sysfs_attr()); + if (rc) { + kfree(tunables); + return rc; + } + + tunables->usage_count = 1; + tunables->above_hispeed_delay = default_above_hispeed_delay; + tunables->nabove_hispeed_delay = + ARRAY_SIZE(default_above_hispeed_delay); + tunables->go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; + tunables->target_loads = default_target_loads; + tunables->ntarget_loads = ARRAY_SIZE(default_target_loads); + tunables->min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + tunables->timer_rate = DEFAULT_TIMER_RATE; + tunables->boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; + tunables->timer_slack_val = DEFAULT_TIMER_SLACK; + + spin_lock_init(&tunables->target_loads_lock); + spin_lock_init(&tunables->above_hispeed_delay_lock); + + if (!policy->governor->initialized) { + idle_notifier_register(&cpufreq_interactive_idle_nb); + cpufreq_register_notifier(&cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + } + + policy->governor_data = tunables; + if (!have_governor_per_policy()) + common_tunables = tunables; + + break; + + case CPUFREQ_GOV_POLICY_EXIT: + if (!--tunables->usage_count) { + if (policy->governor->initialized == 1) { + cpufreq_unregister_notifier(&cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + idle_notifier_unregister(&cpufreq_interactive_idle_nb); + } + + sysfs_remove_group(get_governor_parent_kobj(policy), + get_sysfs_attr()); + kfree(tunables); + common_tunables = NULL; + } + + policy->governor_data = NULL; + break; + case CPUFREQ_GOV_START: mutex_lock(&gov_lock); - freq_table = - cpufreq_frequency_get_table(policy->cpu); - if (!hispeed_freq) - hispeed_freq = policy->max; + freq_table = cpufreq_frequency_get_table(policy->cpu); + if (!tunables->hispeed_freq) + tunables->hispeed_freq = policy->max; for_each_cpu(j, policy->cpus) { pcpu = &per_cpu(cpuinfo, j); @@ -1078,30 +1247,11 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->hispeed_validate_time = pcpu->floor_validate_time; down_write(&pcpu->enable_sem); - cpufreq_interactive_timer_start(j); + cpufreq_interactive_timer_start(tunables, j); pcpu->governor_enabled = 1; up_write(&pcpu->enable_sem); } - /* - * Do not register the idle hook and create sysfs - * entries if we have already done so. - */ - if (++active_count > 1) { - mutex_unlock(&gov_lock); - return 0; - } - - rc = sysfs_create_group(cpufreq_global_kobject, - &interactive_attr_group); - 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; @@ -1116,18 +1266,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, up_write(&pcpu->enable_sem); } - 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; case CPUFREQ_GOV_LIMITS: @@ -1161,7 +1300,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, */ del_timer_sync(&pcpu->cpu_timer); del_timer_sync(&pcpu->cpu_slack_timer); - cpufreq_interactive_timer_start(j); + cpufreq_interactive_timer_start(tunables, j); up_write(&pcpu->enable_sem); } break; @@ -1201,9 +1340,7 @@ static int __init cpufreq_interactive_init(void) init_rwsem(&pcpu->enable_sem); } - 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 8fbe05e54b4ceab2e11ca8f7b0a82055e2c29810 Mon Sep 17 00:00:00 2001 From: Shridhar Rasal Date: Mon, 9 Sep 2013 19:17:14 +0530 Subject: [PATCH 270/475] cpufreq: interactive: delete timers for GOV_START Make sure that timers cpu_timer and cpu_slack_timer deactivated before addition of new. Change-Id: If31c4049606871df6f00efdc24b1d713c86a6f69 Signed-off-by: Shridhar Rasal Signed-off-by: Bharat Nihalani --- drivers/cpufreq/cpufreq_interactive.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 66c096dcb1a2..d72e8c458f69 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -1247,6 +1247,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->hispeed_validate_time = pcpu->floor_validate_time; down_write(&pcpu->enable_sem); + del_timer_sync(&pcpu->cpu_timer); + del_timer_sync(&pcpu->cpu_slack_timer); cpufreq_interactive_timer_start(tunables, j); pcpu->governor_enabled = 1; up_write(&pcpu->enable_sem); From ef37f1f216d88f9353bf4ac81ce365482d0a9f49 Mon Sep 17 00:00:00 2001 From: Chih-Wei Huang Date: Tue, 24 Dec 2013 17:51:55 +0800 Subject: [PATCH 271/475] cpufreq: interactive: fix compiling warnings The gcc warns like: cpufreq_interactive.c:745:6: warning: operation on 'ret' may be undefined [-Wsequence-point] It was introduced by commit cf0fad49d17cb8273ce555dd5b7afab67d7923bf. Since sprintf(...) just return 1 (one character) in this case, ret should not changed. Just discarding the result of sprintf(...) leads to the result that the committer of cf0fad49d17cb8273ce555dd5b7afab67d7923bf wants. Change-Id: Ifed1cef6d6a31c3ed23dad03a567b3b9eddf3a57 Signed-off-by: Chih-Wei Huang --- 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 d72e8c458f69..9f63bd1e4fee 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -752,7 +752,7 @@ static ssize_t show_target_loads( ret += sprintf(buf + ret, "%u%s", tunables->target_loads[i], i & 0x1 ? ":" : " "); - ret += sprintf(buf + --ret, "\n"); + sprintf(buf + ret - 1, "\n"); spin_unlock_irqrestore(&tunables->target_loads_lock, flags); return ret; } @@ -792,7 +792,7 @@ static ssize_t show_above_hispeed_delay( tunables->above_hispeed_delay[i], i & 0x1 ? ":" : " "); - ret += sprintf(buf + --ret, "\n"); + sprintf(buf + ret - 1, "\n"); spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags); return ret; } From 8385286d765f9bd4cdb3eff094a000af32eb8c3b Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Sun, 19 Jan 2014 14:32:42 +0900 Subject: [PATCH 272/475] cpufreq: interactive: fix NULL pointer dereference at sysfs ops sysfs ops for target_loads and above_hispeed_delay can be called before initializing tunables at CPUFREQ_GOV_POLICY_INIT. Create sysfs entries after initialization. Change-Id: I50356198d7629731c0d32a3066d61fe8354e0001 Signed-off-by: Minsung Kim --- drivers/cpufreq/cpufreq_interactive.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 9f63bd1e4fee..8297ac69863b 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -1178,13 +1178,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, return -ENOMEM; } - rc = sysfs_create_group(get_governor_parent_kobj(policy), - get_sysfs_attr()); - if (rc) { - kfree(tunables); - return rc; - } - tunables->usage_count = 1; tunables->above_hispeed_delay = default_above_hispeed_delay; tunables->nabove_hispeed_delay = @@ -1200,16 +1193,26 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, spin_lock_init(&tunables->target_loads_lock); spin_lock_init(&tunables->above_hispeed_delay_lock); + policy->governor_data = tunables; + if (!have_governor_per_policy()) + common_tunables = tunables; + + rc = sysfs_create_group(get_governor_parent_kobj(policy), + get_sysfs_attr()); + if (rc) { + kfree(tunables); + policy->governor_data = NULL; + if (!have_governor_per_policy()) + common_tunables = NULL; + return rc; + } + if (!policy->governor->initialized) { idle_notifier_register(&cpufreq_interactive_idle_nb); cpufreq_register_notifier(&cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); } - policy->governor_data = tunables; - if (!have_governor_per_policy()) - common_tunables = tunables; - break; case CPUFREQ_GOV_POLICY_EXIT: From 0be8516aaa8e1367beb1a401f8a4ed559f8b15b4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 16 May 2013 14:22:49 +0530 Subject: [PATCH 273/475] cpufreq: interactive: Use generic get_cpu_idle_time() from cpufreq.c Now that a generic version of get_cpu_idle_time() is available, use that for the interactive governor. [toddpoynor@google.com: commit text changes] Change-Id: Ia38b57085aac99ec3d415fe44471d5dfde519c2c Signed-off-by: Viresh Kumar Signed-off-by: Jon Medhurst Signed-off-by: John Stultz --- drivers/cpufreq/cpufreq_interactive.c | 40 --------------------------- 1 file changed, 40 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 8297ac69863b..ff77b300df88 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -30,8 +30,6 @@ #include #include #include -#include -#include #define CREATE_TRACE_POINTS #include @@ -119,44 +117,6 @@ struct cpufreq_interactive_tunables *common_tunables; static struct attribute_group *get_sysfs_attr(void); -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, - bool io_is_busy) -{ - 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) { From 479449ecf2d12bc11762c4a2400aff04efaf182e Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 8 Dec 2014 10:08:35 -0800 Subject: [PATCH 274/475] cpufreq: interactive: hold reference on global cpufreq kobject if needed 2361be23666232dbb4851a527f466c4cbf5340fc changed cpufreq to add the global cpufreq kobject to sysfs on demand. To ensure this happens, cpufreq_interactive must hold a reference on this object on devices where it intends to use it (i.e., devices where have_governor_per_policy() returns false). Otherwise a parentless kobject will be passed to sysfs_create_group() which will subsequently BUG(). Change-Id: I7dd03956e1d3c6c3c0cc17c799882c235804ae09 Signed-off-by: Greg Hackmann --- drivers/cpufreq/cpufreq_interactive.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index ff77b300df88..786fd012e8b2 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -1154,8 +1154,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, spin_lock_init(&tunables->above_hispeed_delay_lock); policy->governor_data = tunables; - if (!have_governor_per_policy()) + if (!have_governor_per_policy()) { common_tunables = tunables; + WARN_ON(cpufreq_get_global_kobject()); + } rc = sysfs_create_group(get_governor_parent_kobj(policy), get_sysfs_attr()); @@ -1185,6 +1187,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, sysfs_remove_group(get_governor_parent_kobj(policy), get_sysfs_attr()); + + if (!have_governor_per_policy()) + cpufreq_put_global_kobject(); + kfree(tunables); common_tunables = NULL; } From 528ef7dd37cf38ed2632b3368fda2fb640a43c4d Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 7 Apr 2014 18:26:30 -0700 Subject: [PATCH 275/475] cpufreq: interactive: restructure CPUFREQ_GOV_LIMITS The cpufreq_interactive_timer gets cancelled and rescheduled whenever the cpufreq_policy is changed. When the cpufreq policy is changed at a rate faster than the sampling_rate of the interactive governor, then the governor misses to change the target frequency for long duration. The patch removes the need of cancelling the timers when policy->min is changed. Signed-off-by: Badhri Jagan Sridharan Change-Id: Ibd98d151e1c73b8bd969484583ff98ee9f1135ef --- drivers/cpufreq/cpufreq_interactive.c | 48 +++++++++++++++++++-------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 786fd012e8b2..0e3e45e9e084 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -44,8 +44,10 @@ struct cpufreq_interactive_cpuinfo { u64 cputime_speedadj_timestamp; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; + spinlock_t target_freq_lock; /*protects target freq */ unsigned int target_freq; unsigned int floor_freq; + unsigned int max_freq; u64 floor_validate_time; u64 hispeed_validate_time; struct rw_semaphore enable_sem; @@ -358,6 +360,7 @@ static void cpufreq_interactive_timer(unsigned long data) if (WARN_ON_ONCE(!delta_time)) goto rearm; + spin_lock_irqsave(&pcpu->target_freq_lock, flags); do_div(cputime_speedadj, delta_time); loadadjfreq = (unsigned int)cputime_speedadj * 100; cpu_load = loadadjfreq / pcpu->target_freq; @@ -383,6 +386,7 @@ static void cpufreq_interactive_timer(unsigned long data) trace_cpufreq_interactive_notyet( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); goto rearm; } @@ -390,8 +394,10 @@ static void cpufreq_interactive_timer(unsigned long data) if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_L, - &index)) + &index)) { + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); goto rearm; + } new_freq = pcpu->freq_table[index].frequency; @@ -405,6 +411,7 @@ static void cpufreq_interactive_timer(unsigned long data) trace_cpufreq_interactive_notyet( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); goto rearm; } } @@ -426,6 +433,7 @@ static void cpufreq_interactive_timer(unsigned long data) trace_cpufreq_interactive_already( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); goto rearm_if_notmax; } @@ -433,6 +441,7 @@ static void cpufreq_interactive_timer(unsigned long data) pcpu->policy->cur, new_freq); pcpu->target_freq = new_freq; + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); spin_lock_irqsave(&speedchange_cpumask_lock, flags); cpumask_set_cpu(data, &speedchange_cpumask); spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); @@ -576,16 +585,17 @@ static void cpufreq_interactive_boost(void) { int i; int anyboost = 0; - unsigned long flags; + unsigned long flags[2]; struct cpufreq_interactive_cpuinfo *pcpu; struct cpufreq_interactive_tunables *tunables; - spin_lock_irqsave(&speedchange_cpumask_lock, flags); + spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]); for_each_online_cpu(i) { pcpu = &per_cpu(cpuinfo, i); tunables = pcpu->policy->governor_data; + spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]); if (pcpu->target_freq < tunables->hispeed_freq) { pcpu->target_freq = tunables->hispeed_freq; cpumask_set_cpu(i, &speedchange_cpumask); @@ -601,9 +611,10 @@ static void cpufreq_interactive_boost(void) pcpu->floor_freq = tunables->hispeed_freq; pcpu->floor_validate_time = ktime_to_us(ktime_get()); + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]); } - spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags[0]); if (anyboost) wake_up_process(speedchange_task); @@ -1114,6 +1125,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, struct cpufreq_interactive_cpuinfo *pcpu; struct cpufreq_frequency_table *freq_table; struct cpufreq_interactive_tunables *tunables; + unsigned long flags; if (have_governor_per_policy()) tunables = policy->governor_data; @@ -1215,6 +1227,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, ktime_to_us(ktime_get()); pcpu->hispeed_validate_time = pcpu->floor_validate_time; + pcpu->max_freq = policy->max; down_write(&pcpu->enable_sem); del_timer_sync(&pcpu->cpu_timer); del_timer_sync(&pcpu->cpu_slack_timer); @@ -1250,29 +1263,37 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, for_each_cpu(j, policy->cpus) { pcpu = &per_cpu(cpuinfo, j); - /* hold write semaphore to avoid race */ - down_write(&pcpu->enable_sem); + down_read(&pcpu->enable_sem); if (pcpu->governor_enabled == 0) { - up_write(&pcpu->enable_sem); + up_read(&pcpu->enable_sem); continue; } - /* update target_freq firstly */ + spin_lock_irqsave(&pcpu->target_freq_lock, flags); if (policy->max < pcpu->target_freq) pcpu->target_freq = policy->max; else if (policy->min > pcpu->target_freq) pcpu->target_freq = policy->min; - /* Reschedule timer. + spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); + up_read(&pcpu->enable_sem); + + /* Reschedule timer only if policy->max is raised. * Delete the timers, else the timer callback may * return without re-arm the timer when failed * acquire the semaphore. This race may cause timer * stopped unexpectedly. */ - del_timer_sync(&pcpu->cpu_timer); - del_timer_sync(&pcpu->cpu_slack_timer); - cpufreq_interactive_timer_start(tunables, j); - up_write(&pcpu->enable_sem); + + if (policy->max > pcpu->max_freq) { + down_write(&pcpu->enable_sem); + del_timer_sync(&pcpu->cpu_timer); + del_timer_sync(&pcpu->cpu_slack_timer); + cpufreq_interactive_timer_start(tunables, j); + up_write(&pcpu->enable_sem); + } + + pcpu->max_freq = policy->max; } break; } @@ -1308,6 +1329,7 @@ static int __init cpufreq_interactive_init(void) init_timer(&pcpu->cpu_slack_timer); pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer; spin_lock_init(&pcpu->load_lock); + spin_lock_init(&pcpu->target_freq_lock); init_rwsem(&pcpu->enable_sem); } From 6c2c93b51b7972508ae997d1fe412f5bc2271b6b Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Thu, 3 Apr 2014 15:39:23 -0700 Subject: [PATCH 276/475] cpufreq: interactive: turn boost_pulse off on boost off Change-Id: I36fe217fa047d68ea90e78b12c7db4537ea8010b Signed-off-by: Ruchi Kandoi --- 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 0e3e45e9e084..3daca2137b33 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -908,6 +908,7 @@ static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables, trace_cpufreq_interactive_boost("on"); cpufreq_interactive_boost(); } else { + boostpulse_endtime = ktime_to_us(ktime_get()); trace_cpufreq_interactive_unboost("off"); } From 61a37b31dc5bc921b5409271c29600825e91b7c5 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Wed, 9 Apr 2014 16:47:59 -0700 Subject: [PATCH 277/475] cpufreq: interactive: remove compilation error from commit 49cc72365fb7ee87762a7ccc6a32ef68627216c5 Change-Id: I068b18281d03ac879ef64d8ff36ed43367293767 Signed-off-by: Ruchi Kandoi --- 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 3daca2137b33..6c4b6a895e0d 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -908,7 +908,7 @@ static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables, trace_cpufreq_interactive_boost("on"); cpufreq_interactive_boost(); } else { - boostpulse_endtime = ktime_to_us(ktime_get()); + tunables->boostpulse_endtime = ktime_to_us(ktime_get()); trace_cpufreq_interactive_unboost("off"); } From bc88e631b272563929476da0be3fef1dac10e70a Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Fri, 13 Jun 2014 16:24:15 -0700 Subject: [PATCH 278/475] cpufreq: interactive: prevents the frequency to directly raise above the hispeed_freq from a lower frequency. When the load was below go_hispeed_load, there is a possibility that choose_freq() would return a frequency which would be higher than the hispeed_freq. According to the policy we should first jump to the hispeed_freq, stay there for above_hispeed_delay and then be allowed to raise higher than that. Added a check to prevent the frequency to be directly raised to something higher than the hispeed_freq. Change-Id: Icda5d848dd9beadcc18835082ddf269732c75bd0 Signed-off-by: Ruchi Kandoi --- drivers/cpufreq/cpufreq_interactive.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 6c4b6a895e0d..f0e6fe220efb 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -377,6 +377,9 @@ static void cpufreq_interactive_timer(unsigned long data) } } else { new_freq = choose_freq(pcpu, loadadjfreq); + if (new_freq > tunables->hispeed_freq && + pcpu->target_freq < tunables->hispeed_freq) + new_freq = tunables->hispeed_freq; } if (pcpu->target_freq >= tunables->hispeed_freq && From 39b5c1bf837b16432490fb2b63d5345bb782fa79 Mon Sep 17 00:00:00 2001 From: Cylen Yao Date: Fri, 5 Sep 2014 18:27:38 -0700 Subject: [PATCH 279/475] cpufreq: interactive: make common_tunables static common_tunables should be static. Change-Id: I502ee3062bece5082fea7861eff2f6237e25cede 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 f0e6fe220efb..a0a7c3aef859 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -115,7 +115,7 @@ struct cpufreq_interactive_tunables { }; /* For cases where we have single governor instance for system */ -struct cpufreq_interactive_tunables *common_tunables; +static struct cpufreq_interactive_tunables *common_tunables; static struct attribute_group *get_sysfs_attr(void); From 0b4d5f59680e7c7101dec8c84514cd4eb0f7fb0a Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Sat, 29 Nov 2014 21:43:53 +0900 Subject: [PATCH 280/475] cpufreq: interactive: don't skip waking up speedchange_task if target_freq > policy->cur When __cpufreq_driver_target() in speedchange_task failed for some reason, the policy->cur could be lower than the target_freq. The governor misses to change the target_freq if the target_freq is equal to the next_freq at the next sample time. Added a check to prevent the CPU to stay at the speed that is lower than the target_freq for long duration. Change-Id: Ibfdcd193b8280390b8f8374a63218aa31267f310 Signed-off-by: Minsung Kim --- 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 a0a7c3aef859..5121976ade55 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -432,7 +432,8 @@ static void cpufreq_interactive_timer(unsigned long data) pcpu->floor_validate_time = now; } - if (pcpu->target_freq == new_freq) { + if (pcpu->target_freq == new_freq && + pcpu->target_freq <= pcpu->policy->cur) { trace_cpufreq_interactive_already( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); From 1e04bc2c80363882ff2774600dc4a5a0539c31f0 Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Tue, 2 Dec 2014 17:20:50 -0800 Subject: [PATCH 281/475] cpufreq: interactive: only boost tunable affected cpus It is not correct to boost all the cpus when tunable boost parameters are changed. It also does not need to boost the cpus which is already boosted. Signed-off-by: Lianwei Wang --- drivers/cpufreq/cpufreq_interactive.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 5121976ade55..24fe0ce88e77 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -105,6 +105,7 @@ struct cpufreq_interactive_tunables { int boostpulse_duration_val; /* End time of boost pulse in ktime converted to usecs */ u64 boostpulse_endtime; + bool boosted; /* * Max additional time to wait in idle, beyond timer_rate, at speeds * above minimum before wakeup to reduce speed, or -1 if unnecessary. @@ -344,7 +345,6 @@ static void cpufreq_interactive_timer(unsigned long data) unsigned int loadadjfreq; unsigned int index; unsigned long flags; - bool boosted; if (!down_read_trylock(&pcpu->enable_sem)) return; @@ -364,9 +364,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 = tunables->boost_val || now < tunables->boostpulse_endtime; + tunables->boosted = tunables->boost_val || now < tunables->boostpulse_endtime; - if (cpu_load >= tunables->go_hispeed_load || boosted) { + if (cpu_load >= tunables->go_hispeed_load || tunables->boosted) { if (pcpu->target_freq < tunables->hispeed_freq) { new_freq = tunables->hispeed_freq; } else { @@ -427,7 +427,7 @@ static void cpufreq_interactive_timer(unsigned long data) * (or the indefinite boost is turned off). */ - if (!boosted || new_freq > tunables->hispeed_freq) { + if (!tunables->boosted || new_freq > tunables->hispeed_freq) { pcpu->floor_freq = new_freq; pcpu->floor_validate_time = now; } @@ -585,19 +585,21 @@ static int cpufreq_interactive_speedchange_task(void *data) return 0; } -static void cpufreq_interactive_boost(void) +static void cpufreq_interactive_boost(struct cpufreq_interactive_tunables *tunables) { int i; int anyboost = 0; unsigned long flags[2]; struct cpufreq_interactive_cpuinfo *pcpu; - struct cpufreq_interactive_tunables *tunables; + + tunables->boosted = true; spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]); for_each_online_cpu(i) { pcpu = &per_cpu(cpuinfo, i); - tunables = pcpu->policy->governor_data; + if (tunables != pcpu->policy->governor_data) + continue; spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]); if (pcpu->target_freq < tunables->hispeed_freq) { @@ -910,7 +912,8 @@ static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables, if (tunables->boost_val) { trace_cpufreq_interactive_boost("on"); - cpufreq_interactive_boost(); + if (!tunables->boosted) + cpufreq_interactive_boost(tunables); } else { tunables->boostpulse_endtime = ktime_to_us(ktime_get()); trace_cpufreq_interactive_unboost("off"); @@ -932,7 +935,8 @@ static ssize_t store_boostpulse(struct cpufreq_interactive_tunables *tunables, tunables->boostpulse_endtime = ktime_to_us(ktime_get()) + tunables->boostpulse_duration_val; trace_cpufreq_interactive_boost("pulse"); - cpufreq_interactive_boost(); + if (!tunables->boosted) + cpufreq_interactive_boost(tunables); return count; } From 10fe4289714730ac025624ada51b72e020d66846 Mon Sep 17 00:00:00 2001 From: Junjie Wu Date: Fri, 6 Feb 2015 20:28:37 -0800 Subject: [PATCH 282/475] cpufreq: interactive: Put global cpufreq kobject on failure Fix failure recovery path in cpufreq_governor_interactive(). Call cpufreq_put_global_kobject() to release cpufreq global kobject upon governor init failure. Change-Id: I7a977070b7a3c75c90acccd2c117064ed1a10d0e Signed-off-by: Junjie Wu --- drivers/cpufreq/cpufreq_interactive.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 24fe0ce88e77..6b3facdf5ba2 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -1185,8 +1185,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (rc) { kfree(tunables); policy->governor_data = NULL; - if (!have_governor_per_policy()) + if (!have_governor_per_policy()) { common_tunables = NULL; + cpufreq_put_global_kobject(); + } return rc; } From 16e989d788451c5ed3fb0e0a4dd9ee84de23d616 Mon Sep 17 00:00:00 2001 From: Junjie Wu Date: Mon, 6 Apr 2015 11:16:45 -0700 Subject: [PATCH 283/475] cpufreq: interactive: Don't set floor_validate_time during boost Frequency selection algorithm guarantees its chosen frequency is not lower than hispeed_freq as long as boost is enabled. Setting floor_freq and floor_validate_time during boost could block CPU frequency from going below hispeed_freq even after boostpulse_duration expires, if min_sample_time is higher than boostpulse_duration. This conflicts with the intention of commit de091367ead15b6e95dd1d0743a18f0da5a07ee5 (cpufreq: interactive: specify duration of CPU speed boost pulse) to allow CPU to ramp down immediately after boost expires. It also makes boost behavior inconsistent since it depends on min_sample_time. Avoid setting floor_freq and floor_validate_time when boost starts. Change-Id: I12852998af46cfbfaf8661eb5e8d5301b6f631e7 Signed-off-by: Junjie Wu --- drivers/cpufreq/cpufreq_interactive.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 6b3facdf5ba2..49f206e48aa3 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -609,14 +609,6 @@ static void cpufreq_interactive_boost(struct cpufreq_interactive_tunables *tunab ktime_to_us(ktime_get()); anyboost = 1; } - - /* - * Set floor freq and (re)start timer for when last - * validated. - */ - - pcpu->floor_freq = tunables->hispeed_freq; - pcpu->floor_validate_time = ktime_to_us(ktime_get()); spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]); } From 4db1f0ac8761fc30d4e1230939d623a52517bdcc Mon Sep 17 00:00:00 2001 From: Junjie Wu Date: Fri, 15 Aug 2014 16:34:37 -0700 Subject: [PATCH 284/475] cpufreq: interactive: Round up timer_rate to match jiffy Timers are scheduled in unit of jiffies. Round up timer_rate so that it matches the actual sampling period. Change-Id: I88386a5a448e40333f9a9b9f0cf72af58cb54656 Signed-off-by: Junjie Wu --- drivers/cpufreq/cpufreq_interactive.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 49f206e48aa3..6bbcc905bb3e 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -855,12 +855,18 @@ static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables, const char *buf, size_t count) { int ret; - unsigned long val; + unsigned long val, val_round; ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - tunables->timer_rate = val; + + val_round = jiffies_to_usecs(usecs_to_jiffies(val)); + if (val != val_round) + pr_warn("timer_rate not aligned to jiffy. Rounded up to %lu\n", + val_round); + + tunables->timer_rate = val_round; return count; } From 54c3ec0a7bb945ca872788241e3820a92f3f508a Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 15 Oct 2014 12:44:18 -0700 Subject: [PATCH 285/475] cpufreq: interactive: Exercise hispeed settings at a policy level If a heavy task migrates between otherwise idle CPUs in a policy during every sample window, the above hispeed delay window for the CPUs would get restarted for every sample window. Due to the continuous restart of above hispeed delay window, none of the CPUs would ever pick a target frequency higher than hispeed frequency. This causes the policy's frequency to be stuck at hispeed freq even if the load justifies a higher frequency. To fix this, the above high speed delay window is restarted only when the policy frequency changes. This ensures that tasks migrating between CPUs in a policy are handled correctly. Also, the hispeed load/frequency heuristic is only necessary when the information is insufficient to determine if the load on the CPU needs at least hispeed frequency. When the policy frequency is already at or above hispeed frequency, if the CPU load% based on policy frequency is not above hispeed load, then the information is clearly sufficient to determine that the load on the CPU does not need hispeed frequency. Therefore, compute CPU load% (which is used only to compare against hispeed load) based on policy frequency instead of CPU target frequency. Change-Id: I8b5dfe6c50bee567a6719f0980e3f7757876ce4b Signed-off-by: Saravana Kannan Signed-off-by: Junjie Wu --- drivers/cpufreq/cpufreq_interactive.c | 40 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 6bbcc905bb3e..3f9d8f43fc48 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -49,7 +49,8 @@ struct cpufreq_interactive_cpuinfo { unsigned int floor_freq; unsigned int max_freq; u64 floor_validate_time; - u64 hispeed_validate_time; + u64 pol_hispeed_val_time; /* policy hispeed_validate_time */ + u64 loc_hispeed_val_time; /* per-cpu hispeed_validate_time */ struct rw_semaphore enable_sem; int governor_enabled; }; @@ -367,7 +368,7 @@ static void cpufreq_interactive_timer(unsigned long data) tunables->boosted = tunables->boost_val || now < tunables->boostpulse_endtime; if (cpu_load >= tunables->go_hispeed_load || tunables->boosted) { - if (pcpu->target_freq < tunables->hispeed_freq) { + if (pcpu->policy->cur < tunables->hispeed_freq) { new_freq = tunables->hispeed_freq; } else { new_freq = choose_freq(pcpu, loadadjfreq); @@ -378,14 +379,14 @@ static void cpufreq_interactive_timer(unsigned long data) } else { new_freq = choose_freq(pcpu, loadadjfreq); if (new_freq > tunables->hispeed_freq && - pcpu->target_freq < tunables->hispeed_freq) + pcpu->policy->cur < tunables->hispeed_freq) new_freq = tunables->hispeed_freq; } - if (pcpu->target_freq >= tunables->hispeed_freq && - new_freq > pcpu->target_freq && - now - pcpu->hispeed_validate_time < - freq_to_above_hispeed_delay(tunables, pcpu->target_freq)) { + if (pcpu->policy->cur >= tunables->hispeed_freq && + new_freq > pcpu->policy->cur && + now - pcpu->pol_hispeed_val_time < + freq_to_above_hispeed_delay(tunables, pcpu->policy->cur)) { trace_cpufreq_interactive_notyet( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); @@ -393,7 +394,7 @@ static void cpufreq_interactive_timer(unsigned long data) goto rearm; } - pcpu->hispeed_validate_time = now; + pcpu->loc_hispeed_val_time = now; if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_L, @@ -553,6 +554,8 @@ static int cpufreq_interactive_speedchange_task(void *data) for_each_cpu(cpu, &tmp_mask) { unsigned int j; unsigned int max_freq = 0; + struct cpufreq_interactive_cpuinfo *pjcpu; + u64 hvt = ~0ULL; pcpu = &per_cpu(cpuinfo, cpu); if (!down_read_trylock(&pcpu->enable_sem)) @@ -563,17 +566,25 @@ static int cpufreq_interactive_speedchange_task(void *data) } for_each_cpu(j, pcpu->policy->cpus) { - struct cpufreq_interactive_cpuinfo *pjcpu = - &per_cpu(cpuinfo, j); + pjcpu = &per_cpu(cpuinfo, j); - if (pjcpu->target_freq > max_freq) + if (pjcpu->target_freq > max_freq) { max_freq = pjcpu->target_freq; + hvt = pjcpu->loc_hispeed_val_time; + } else if (pjcpu->target_freq == max_freq) { + hvt = min(hvt, pjcpu->loc_hispeed_val_time); + } } - if (max_freq != pcpu->policy->cur) + if (max_freq != pcpu->policy->cur) { __cpufreq_driver_target(pcpu->policy, max_freq, CPUFREQ_RELATION_H); + for_each_cpu(j, pcpu->policy->cpus) { + pjcpu = &per_cpu(cpuinfo, j); + pjcpu->pol_hispeed_val_time = hvt; + } + } trace_cpufreq_interactive_setspeed(cpu, pcpu->target_freq, pcpu->policy->cur); @@ -605,7 +616,7 @@ static void cpufreq_interactive_boost(struct cpufreq_interactive_tunables *tunab if (pcpu->target_freq < tunables->hispeed_freq) { pcpu->target_freq = tunables->hispeed_freq; cpumask_set_cpu(i, &speedchange_cpumask); - pcpu->hispeed_validate_time = + pcpu->pol_hispeed_val_time = ktime_to_us(ktime_get()); anyboost = 1; } @@ -1234,8 +1245,9 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->floor_freq = pcpu->target_freq; pcpu->floor_validate_time = ktime_to_us(ktime_get()); - pcpu->hispeed_validate_time = + pcpu->pol_hispeed_val_time = pcpu->floor_validate_time; + pcpu->loc_hispeed_val_time = pcpu->floor_validate_time; pcpu->max_freq = policy->max; down_write(&pcpu->enable_sem); del_timer_sync(&pcpu->cpu_timer); From 0ad0834ba02fa736366ec8fc5bf050bd447290cf Mon Sep 17 00:00:00 2001 From: Junjie Wu Date: Tue, 24 Mar 2015 15:51:10 -0700 Subject: [PATCH 286/475] cpufreq: interactive: Implement cluster-based min_sample_time min_sample_time needs to be cluster-based to match above_hispeed_delay. If each CPU keeps making local decisions, it's possible min_sample_time is not correctly enforced at cluster level, which results in undesired frequency drops. Change-Id: Ia2ec2ad9b7a8d715d4408c924d6762b7e532e4b4 Reviewed-by: Saravana Kannan Signed-off-by: Junjie Wu --- drivers/cpufreq/cpufreq_interactive.c | 30 ++++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 3f9d8f43fc48..52c551d58477 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -48,7 +48,8 @@ struct cpufreq_interactive_cpuinfo { unsigned int target_freq; unsigned int floor_freq; unsigned int max_freq; - u64 floor_validate_time; + u64 pol_floor_val_time; /* policy floor_validate_time */ + u64 loc_floor_val_time; /* per-cpu floor_validate_time */ u64 pol_hispeed_val_time; /* policy hispeed_validate_time */ u64 loc_hispeed_val_time; /* per-cpu hispeed_validate_time */ struct rw_semaphore enable_sem; @@ -346,6 +347,7 @@ static void cpufreq_interactive_timer(unsigned long data) unsigned int loadadjfreq; unsigned int index; unsigned long flags; + u64 max_fvtime; if (!down_read_trylock(&pcpu->enable_sem)) return; @@ -409,9 +411,10 @@ static void cpufreq_interactive_timer(unsigned long data) * 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->floor_freq) { - if (now - pcpu->floor_validate_time < - tunables->min_sample_time) { + max_fvtime = max(pcpu->pol_floor_val_time, pcpu->loc_floor_val_time); + if (new_freq < pcpu->floor_freq && + pcpu->target_freq >= pcpu->policy->cur) { + if (now - max_fvtime < tunables->min_sample_time) { trace_cpufreq_interactive_notyet( data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); @@ -430,7 +433,9 @@ static void cpufreq_interactive_timer(unsigned long data) if (!tunables->boosted || new_freq > tunables->hispeed_freq) { pcpu->floor_freq = new_freq; - pcpu->floor_validate_time = now; + if (pcpu->target_freq >= pcpu->policy->cur || + new_freq >= pcpu->policy->cur) + pcpu->loc_floor_val_time = now; } if (pcpu->target_freq == new_freq && @@ -555,7 +560,7 @@ static int cpufreq_interactive_speedchange_task(void *data) unsigned int j; unsigned int max_freq = 0; struct cpufreq_interactive_cpuinfo *pjcpu; - u64 hvt = ~0ULL; + u64 hvt = ~0ULL, fvt = 0; pcpu = &per_cpu(cpuinfo, cpu); if (!down_read_trylock(&pcpu->enable_sem)) @@ -568,6 +573,7 @@ static int cpufreq_interactive_speedchange_task(void *data) for_each_cpu(j, pcpu->policy->cpus) { pjcpu = &per_cpu(cpuinfo, j); + fvt = max(fvt, pjcpu->loc_floor_val_time); if (pjcpu->target_freq > max_freq) { max_freq = pjcpu->target_freq; hvt = pjcpu->loc_hispeed_val_time; @@ -575,6 +581,10 @@ static int cpufreq_interactive_speedchange_task(void *data) hvt = min(hvt, pjcpu->loc_hispeed_val_time); } } + for_each_cpu(j, pcpu->policy->cpus) { + pjcpu = &per_cpu(cpuinfo, j); + pjcpu->pol_floor_val_time = fvt; + } if (max_freq != pcpu->policy->cur) { __cpufreq_driver_target(pcpu->policy, @@ -1243,11 +1253,11 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->target_freq = policy->cur; pcpu->freq_table = freq_table; pcpu->floor_freq = pcpu->target_freq; - pcpu->floor_validate_time = + pcpu->pol_floor_val_time = ktime_to_us(ktime_get()); - pcpu->pol_hispeed_val_time = - pcpu->floor_validate_time; - pcpu->loc_hispeed_val_time = pcpu->floor_validate_time; + pcpu->loc_floor_val_time = pcpu->pol_floor_val_time; + pcpu->pol_hispeed_val_time = pcpu->pol_floor_val_time; + pcpu->loc_hispeed_val_time = pcpu->pol_floor_val_time; pcpu->max_freq = policy->max; down_write(&pcpu->enable_sem); del_timer_sync(&pcpu->cpu_timer); From b1b0fd37276a3482ee663c3635e7c67ec1059a24 Mon Sep 17 00:00:00 2001 From: Rohit Gupta Date: Fri, 6 Mar 2015 18:46:04 -0800 Subject: [PATCH 287/475] cpufreq: interactive: Rearm governor timer at max freq Interactive governor doesn't rearm per-cpu timer if target_freq is equal to policy->max. However, this does not have clear performance benefits. Profiling doesn't show any difference in benchmarks, games or other workloads, if timers are always rearmed. At same time, there are a few issues caused by not rearming timer at policy->max. 1) min_sample_time enforcement is inconsistent For target frequency that is lower than policy->max, it will not drop until min_sample_time has passed since last frequency evaluation selected current frequency. However, for policy->max, it will always drop immediately as long as CPU has been run for longer than min_sample_time. This is because timer is not running and thus floor_freq and floor_validate_time is not updated. Example: assume min_sample_time is 59ms and timer_rate is 20ms. Frequency X < Y. Let's say CPU would pick the following frequencies before accounting for min_sample_time in each 20ms sampling window. Y, Y, Y, Y, X, X, X, X, X If Y is not policy->max, the final target_freq after considering min_sample_time will be Y, Y, Y, Y, *Y, *Y, X, X, X * marks the windows where frequency is prevented from dropping. If Y is policy->max, the final target_freq will be Y, Y, Y, Y, X, X, X, X, X 2) Rearm timer in IDLE_START does not work as intended IDLE_START/END is sent in arch_cpu_idle_enter/exit(). However, next wake up is decided in tick_nohz_idle_enter(), which traverses the timer list before idle notification is sent out. Therefore, rearming timer in idle notification won't take effect until CPU wakes up at least once. In rare scenarios when a CPU goes to idle and sleeps for a long time immediately after a heavy load stops, it may not wake up to drop its frequency vote for a long time, defeating the purpose of having a slack_timer. 3) Need to rearm timer for policy->max change commit 535a553fc1c4b4c3627c73214ade6326615a7463 (cpufreq: interactive: restructure CPUFREQ_GOV_LIMITS) mentions the problem of timer getting indefinitely pushed back due to frequency changes in policy->min/max. However, it still cancels and rearms timer if policy->max is increased, and same problem could still happen if policy->max is frequently changing after the fix. The best solution is to always rearm timer for each CPU even if it's running at policy->max. Rearming timers even if target_freq is policy->max solves these problems cleanly. It also simplifies the design and code of interactive governor. Change-Id: I973853d2375ea6f697fa4cee04a89efe6b8bf735 Reviewed-by: Saravana Kannan Signed-off-by: Junjie Wu Signed-off-by: Rohit Gupta --- drivers/cpufreq/cpufreq_interactive.c | 68 +-------------------------- 1 file changed, 2 insertions(+), 66 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 52c551d58477..a20ab2e635bf 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -47,7 +47,6 @@ struct cpufreq_interactive_cpuinfo { spinlock_t target_freq_lock; /*protects target freq */ unsigned int target_freq; unsigned int floor_freq; - unsigned int max_freq; u64 pol_floor_val_time; /* policy floor_validate_time */ u64 loc_floor_val_time; /* per-cpu floor_validate_time */ u64 pol_hispeed_val_time; /* policy hispeed_validate_time */ @@ -444,7 +443,7 @@ static void cpufreq_interactive_timer(unsigned long data) data, cpu_load, pcpu->target_freq, pcpu->policy->cur, new_freq); spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); - goto rearm_if_notmax; + goto rearm; } trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, @@ -457,14 +456,6 @@ static void cpufreq_interactive_timer(unsigned long data) spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); wake_up_process(speedchange_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)) cpufreq_interactive_timer_resched(pcpu); @@ -474,37 +465,6 @@ exit: return; } -static void cpufreq_interactive_idle_start(void) -{ - struct cpufreq_interactive_cpuinfo *pcpu = - &per_cpu(cpuinfo, smp_processor_id()); - int pending; - - if (!down_read_trylock(&pcpu->enable_sem)) - return; - if (!pcpu->governor_enabled) { - up_read(&pcpu->enable_sem); - return; - } - - pending = timer_pending(&pcpu->cpu_timer); - - if (pcpu->target_freq != pcpu->policy->min) { - /* - * 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) - cpufreq_interactive_timer_resched(pcpu); - } - - up_read(&pcpu->enable_sem); -} - static void cpufreq_interactive_idle_end(void) { struct cpufreq_interactive_cpuinfo *pcpu = @@ -1129,14 +1089,8 @@ 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: + if (val == IDLE_END) cpufreq_interactive_idle_end(); - break; - } return 0; } @@ -1258,7 +1212,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->loc_floor_val_time = pcpu->pol_floor_val_time; pcpu->pol_hispeed_val_time = pcpu->pol_floor_val_time; pcpu->loc_hispeed_val_time = pcpu->pol_floor_val_time; - pcpu->max_freq = policy->max; down_write(&pcpu->enable_sem); del_timer_sync(&pcpu->cpu_timer); del_timer_sync(&pcpu->cpu_slack_timer); @@ -1308,23 +1261,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, spin_unlock_irqrestore(&pcpu->target_freq_lock, flags); up_read(&pcpu->enable_sem); - - /* Reschedule timer only if policy->max is raised. - * Delete the timers, else the timer callback may - * return without re-arm the timer when failed - * acquire the semaphore. This race may cause timer - * stopped unexpectedly. - */ - - if (policy->max > pcpu->max_freq) { - down_write(&pcpu->enable_sem); - del_timer_sync(&pcpu->cpu_timer); - del_timer_sync(&pcpu->cpu_slack_timer); - cpufreq_interactive_timer_start(tunables, j); - up_write(&pcpu->enable_sem); - } - - pcpu->max_freq = policy->max; } break; } From e197a21971a5bfb9d335a9baa701c70b973fdab4 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 3 Nov 2015 20:53:29 +0530 Subject: [PATCH 288/475] cpufreq: interactive: replace strict_strtoul() with kstrtoul() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strict_strtoul() is obsolete. Use kstrtoul() instead. Otherwise we run into following build error: CC drivers/cpufreq/cpufreq_interactive.o drivers/cpufreq/cpufreq_interactive.c: In function ‘store_hispeed_freq’: drivers/cpufreq/cpufreq_interactive.c:784:2: error: implicit declaration of function ‘strict_strtoul’ [-Werror=implicit-function-declaration] cc1: some warnings being treated as errors make[2]: *** [drivers/cpufreq/cpufreq_interactive.o] Error 1 Change-Id: Ib91b9df3af5fe2a244861c2f598bd20ec8115e6c Signed-off-by: Amit Pundir --- drivers/cpufreq/cpufreq_interactive.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index a20ab2e635bf..f2be0d87d73d 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -781,7 +781,7 @@ static ssize_t store_hispeed_freq(struct cpufreq_interactive_tunables *tunables, int ret; long unsigned int val; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; tunables->hispeed_freq = val; @@ -800,7 +800,7 @@ static ssize_t store_go_hispeed_load(struct cpufreq_interactive_tunables int ret; unsigned long val; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; tunables->go_hispeed_load = val; @@ -819,7 +819,7 @@ static ssize_t store_min_sample_time(struct cpufreq_interactive_tunables int ret; unsigned long val; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; tunables->min_sample_time = val; @@ -838,7 +838,7 @@ static ssize_t store_timer_rate(struct cpufreq_interactive_tunables *tunables, int ret; unsigned long val, val_round; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; From bc68f6c4efbd4ddbb15817203f18b7941d9ffd52 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 20 Nov 2015 18:54:30 +0530 Subject: [PATCH 289/475] cpufreq: interactive: build fixes for 4.4 Bring back cpufreq_{get,put}_global_kobject() definitions removed by upstream commit 8eec1020f0c0 "cpufreq: create cpu/cpufreq at boot time" to fix build failures. Signed-off-by: Amit Pundir --- drivers/cpufreq/cpufreq_interactive.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index f2be0d87d73d..9259d4829765 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -116,6 +116,28 @@ struct cpufreq_interactive_tunables { bool io_is_busy; }; +/* + * HACK: FIXME: Bring back cpufreq_{get,put}_global_kobject() + * definition removed by upstream commit 8eec1020f0c0 "cpufreq: + * create cpu/cpufreq at boot time" to fix build failures. + */ +static int cpufreq_global_kobject_usage; + +int cpufreq_get_global_kobject(void) +{ + if (!cpufreq_global_kobject_usage++) + return kobject_add(cpufreq_global_kobject, + &cpu_subsys.dev_root->kobj, "%s", "cpufreq"); + + return 0; +} + +void cpufreq_put_global_kobject(void) +{ + if (!--cpufreq_global_kobject_usage) + kobject_del(cpufreq_global_kobject); +} + /* For cases where we have single governor instance for system */ static struct cpufreq_interactive_tunables *common_tunables; From 4808d2835106dac954682fe427387b115834b348 Mon Sep 17 00:00:00 2001 From: "rahul.khandelwal" Date: Fri, 17 Apr 2015 11:45:23 +0530 Subject: [PATCH 290/475] subsystem: CPU FREQUENCY DRIVERS- Set cpu_load calculation on current frequency In timer, cpu_load is calcuated on target_freq. cpu_load = loadadjfreq / pcpu->target_freq; But cpu is actually running on current freq i.e. pcpu->policy->cur. So cpu_load should be calculated on current frequency. cpu_load = loadadjfreq / pcpu->policy->cur; Change-Id: I89db6b68e9f82aa52077f6bf7d819dab74265790 Signed-off-by: rahul.khandelwal --- 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 9259d4829765..0be66df4a6e6 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -387,7 +387,7 @@ static void cpufreq_interactive_timer(unsigned long data) spin_lock_irqsave(&pcpu->target_freq_lock, flags); do_div(cputime_speedadj, delta_time); loadadjfreq = (unsigned int)cputime_speedadj * 100; - cpu_load = loadadjfreq / pcpu->target_freq; + cpu_load = loadadjfreq / pcpu->policy->cur; tunables->boosted = tunables->boost_val || now < tunables->boostpulse_endtime; if (cpu_load >= tunables->go_hispeed_load || tunables->boosted) { From 066a50cee53685baa1a58e7690da93faa0689b91 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Wed, 22 May 2013 14:23:10 -0700 Subject: [PATCH 291/475] 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 | 255 ++++++++ include/video/adf.h | 439 +++++++++++++ include/video/adf_client.h | 57 ++ include/video/adf_format.h | 26 + 19 files changed, 4550 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 e0606c01e8ac..35d239ebf912 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -29,6 +29,7 @@ source "drivers/video/fbdev/Kconfig" endmenu source "drivers/video/backlight/Kconfig" +source "drivers/video/adf/Kconfig" config VGASTATE tristate diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9ad3c17d6456..1a8c4ced39b2 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_VGASTATE) += vgastate.o obj-$(CONFIG_HDMI) += hdmi.o +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