From 9807a54cd74149988f5d20088bf7a7957c205bfb Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Mon, 7 Jan 2013 09:54:31 -0800 Subject: linux/openvswitch.h: Make OVSP_LOCAL 32-bit. OVS ports are now 32-bit, so OVSP_LOCAL should be too. (Internally, kernel module still keeps port numbers 16-bit, though.) Signed-off-by: Jarno Rajahalme Signed-off-by: Jesse Gross --- include/linux/openvswitch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h index d42e174bd0c..99e6414a40d 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -94,7 +94,7 @@ struct ovs_vport_stats { }; /* Fixed logical ports. */ -#define OVSP_LOCAL ((__u16)0) +#define OVSP_LOCAL ((__u32)0) /* Packet transfer. */ -- cgit v1.2.3-70-g09d2 From 345046673449b5c35840e5cc34a60059cbec9305 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 10 Jan 2013 19:00:53 +0000 Subject: slab: Move kmalloc related function defs Move these functions higher up in slab.h so that they are grouped with other generic kmalloc related definitions. Acked-by: Glauber Costa Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index 5d168d7e0a2..ccbb37685c6 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -147,6 +147,15 @@ void kmem_cache_free(struct kmem_cache *, void *); sizeof(struct __struct), __alignof__(struct __struct),\ (__flags), NULL) +/* + * Common kmalloc functions provided by all allocators + */ +void * __must_check __krealloc(const void *, size_t, gfp_t); +void * __must_check krealloc(const void *, size_t, gfp_t); +void kfree(const void *); +void kzfree(const void *); +size_t ksize(const void *); + /* * The largest kmalloc size supported by the slab allocators is * 32 megabyte (2^25) or the maximum allocatable page order if that is @@ -224,15 +233,6 @@ struct seq_file; int cache_show(struct kmem_cache *s, struct seq_file *m); void print_slabinfo_header(struct seq_file *m); -/* - * Common kmalloc functions provided by all allocators - */ -void * __must_check __krealloc(const void *, size_t, gfp_t); -void * __must_check krealloc(const void *, size_t, gfp_t); -void kfree(const void *); -void kzfree(const void *); -size_t ksize(const void *); - /* * Allocator specific definitions. These are mainly used to establish optimized * ways to convert kmalloc() calls to kmem_cache_alloc() invocations by -- cgit v1.2.3-70-g09d2 From ce6a50263d4ddeba1f0d08f16716a82770c03690 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 10 Jan 2013 19:14:19 +0000 Subject: slab: Common kmalloc slab index determination Extract the function to determine the index of the slab within the array of kmalloc caches as well as a function to determine maximum object size from the nr of the kmalloc slab. This is used here only to simplify slub bootstrap but will be used later also for SLAB. Acked-by: Glauber Costa Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 172 +++++++++++++++++++++++++++++++++-------------- include/linux/slub_def.h | 63 ----------------- 2 files changed, 122 insertions(+), 113 deletions(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index ccbb37685c6..c97fe92532d 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -94,29 +94,6 @@ #define ZERO_OR_NULL_PTR(x) ((unsigned long)(x) <= \ (unsigned long)ZERO_SIZE_PTR) -/* - * Common fields provided in kmem_cache by all slab allocators - * This struct is either used directly by the allocator (SLOB) - * or the allocator must include definitions for all fields - * provided in kmem_cache_common in their definition of kmem_cache. - * - * Once we can do anonymous structs (C11 standard) we could put a - * anonymous struct definition in these allocators so that the - * separate allocations in the kmem_cache structure of SLAB and - * SLUB is no longer needed. - */ -#ifdef CONFIG_SLOB -struct kmem_cache { - unsigned int object_size;/* The original size of the object */ - unsigned int size; /* The aligned/padded/added on size */ - unsigned int align; /* Alignment as calculated */ - unsigned long flags; /* Active flags on the slab */ - const char *name; /* Slab name for sysfs */ - int refcount; /* Use counter */ - void (*ctor)(void *); /* Called on object slot creation */ - struct list_head list; /* List of all slab caches on the system */ -}; -#endif struct mem_cgroup; /* @@ -156,6 +133,35 @@ void kfree(const void *); void kzfree(const void *); size_t ksize(const void *); +#ifdef CONFIG_SLOB +/* + * Common fields provided in kmem_cache by all slab allocators + * This struct is either used directly by the allocator (SLOB) + * or the allocator must include definitions for all fields + * provided in kmem_cache_common in their definition of kmem_cache. + * + * Once we can do anonymous structs (C11 standard) we could put a + * anonymous struct definition in these allocators so that the + * separate allocations in the kmem_cache structure of SLAB and + * SLUB is no longer needed. + */ +struct kmem_cache { + unsigned int object_size;/* The original size of the object */ + unsigned int size; /* The aligned/padded/added on size */ + unsigned int align; /* Alignment as calculated */ + unsigned long flags; /* Active flags on the slab */ + const char *name; /* Slab name for sysfs */ + int refcount; /* Use counter */ + void (*ctor)(void *); /* Called on object slot creation */ + struct list_head list; /* List of all slab caches on the system */ +}; + +#define KMALLOC_MAX_SIZE (1UL << 30) + +#include + +#else /* CONFIG_SLOB */ + /* * The largest kmalloc size supported by the slab allocators is * 32 megabyte (2^25) or the maximum allocatable page order if that is @@ -171,6 +177,99 @@ size_t ksize(const void *); #define KMALLOC_MAX_SIZE (1UL << KMALLOC_SHIFT_HIGH) #define KMALLOC_MAX_ORDER (KMALLOC_SHIFT_HIGH - PAGE_SHIFT) +/* + * Kmalloc subsystem. + */ +#if defined(ARCH_DMA_MINALIGN) && ARCH_DMA_MINALIGN > 8 +#define KMALLOC_MIN_SIZE ARCH_DMA_MINALIGN +#else +#ifdef CONFIG_SLAB +#define KMALLOC_MIN_SIZE 32 +#else +#define KMALLOC_MIN_SIZE 8 +#endif +#endif + +#define KMALLOC_SHIFT_LOW ilog2(KMALLOC_MIN_SIZE) + +/* + * Figure out which kmalloc slab an allocation of a certain size + * belongs to. + * 0 = zero alloc + * 1 = 65 .. 96 bytes + * 2 = 120 .. 192 bytes + * n = 2^(n-1) .. 2^n -1 + */ +static __always_inline int kmalloc_index(size_t size) +{ + if (!size) + return 0; + + if (size <= KMALLOC_MIN_SIZE) + return KMALLOC_SHIFT_LOW; + + if (KMALLOC_MIN_SIZE <= 32 && size > 64 && size <= 96) + return 1; + if (KMALLOC_MIN_SIZE <= 64 && size > 128 && size <= 192) + return 2; + if (size <= 8) return 3; + if (size <= 16) return 4; + if (size <= 32) return 5; + if (size <= 64) return 6; + if (size <= 128) return 7; + if (size <= 256) return 8; + if (size <= 512) return 9; + if (size <= 1024) return 10; + if (size <= 2 * 1024) return 11; + if (size <= 4 * 1024) return 12; + if (size <= 8 * 1024) return 13; + if (size <= 16 * 1024) return 14; + if (size <= 32 * 1024) return 15; + if (size <= 64 * 1024) return 16; + if (size <= 128 * 1024) return 17; + if (size <= 256 * 1024) return 18; + if (size <= 512 * 1024) return 19; + if (size <= 1024 * 1024) return 20; + if (size <= 2 * 1024 * 1024) return 21; + if (size <= 4 * 1024 * 1024) return 22; + if (size <= 8 * 1024 * 1024) return 23; + if (size <= 16 * 1024 * 1024) return 24; + if (size <= 32 * 1024 * 1024) return 25; + if (size <= 64 * 1024 * 1024) return 26; + BUG(); + + /* Will never be reached. Needed because the compiler may complain */ + return -1; +} + +#ifdef CONFIG_SLAB +#include +#elif defined(CONFIG_SLUB) +#include +#else +#error "Unknown slab allocator" +#endif + +/* + * Determine size used for the nth kmalloc cache. + * return size or 0 if a kmalloc cache for that + * size does not exist + */ +static __always_inline int kmalloc_size(int n) +{ + if (n > 2) + return 1 << n; + + if (n == 1 && KMALLOC_MIN_SIZE <= 32) + return 96; + + if (n == 2 && KMALLOC_MIN_SIZE <= 64) + return 192; + + return 0; +} +#endif /* !CONFIG_SLOB */ + /* * Some archs want to perform DMA into kmalloc caches and need a guaranteed * alignment larger than the alignment of a 64-bit integer. @@ -233,33 +332,6 @@ struct seq_file; int cache_show(struct kmem_cache *s, struct seq_file *m); void print_slabinfo_header(struct seq_file *m); -/* - * Allocator specific definitions. These are mainly used to establish optimized - * ways to convert kmalloc() calls to kmem_cache_alloc() invocations by - * selecting the appropriate general cache at compile time. - * - * Allocators must define at least: - * - * kmem_cache_alloc() - * __kmalloc() - * kmalloc() - * - * Those wishing to support NUMA must also define: - * - * kmem_cache_alloc_node() - * kmalloc_node() - * - * See each allocator definition file for additional comments and - * implementation notes. - */ -#ifdef CONFIG_SLUB -#include -#elif defined(CONFIG_SLOB) -#include -#else -#include -#endif - /** * kmalloc_array - allocate memory for an array. * @n: number of elements. diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 9db4825cd39..99c3e05ff1f 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -115,17 +115,6 @@ struct kmem_cache { struct kmem_cache_node *node[MAX_NUMNODES]; }; -/* - * Kmalloc subsystem. - */ -#if defined(ARCH_DMA_MINALIGN) && ARCH_DMA_MINALIGN > 8 -#define KMALLOC_MIN_SIZE ARCH_DMA_MINALIGN -#else -#define KMALLOC_MIN_SIZE 8 -#endif - -#define KMALLOC_SHIFT_LOW ilog2(KMALLOC_MIN_SIZE) - /* * Maximum kmalloc object size handled by SLUB. Larger object allocations * are passed through to the page allocator. The page allocator "fastpath" @@ -152,58 +141,6 @@ struct kmem_cache { */ extern struct kmem_cache *kmalloc_caches[SLUB_PAGE_SHIFT]; -/* - * Sorry that the following has to be that ugly but some versions of GCC - * have trouble with constant propagation and loops. - */ -static __always_inline int kmalloc_index(size_t size) -{ - if (!size) - return 0; - - if (size <= KMALLOC_MIN_SIZE) - return KMALLOC_SHIFT_LOW; - - if (KMALLOC_MIN_SIZE <= 32 && size > 64 && size <= 96) - return 1; - if (KMALLOC_MIN_SIZE <= 64 && size > 128 && size <= 192) - return 2; - if (size <= 8) return 3; - if (size <= 16) return 4; - if (size <= 32) return 5; - if (size <= 64) return 6; - if (size <= 128) return 7; - if (size <= 256) return 8; - if (size <= 512) return 9; - if (size <= 1024) return 10; - if (size <= 2 * 1024) return 11; - if (size <= 4 * 1024) return 12; -/* - * The following is only needed to support architectures with a larger page - * size than 4k. We need to support 2 * PAGE_SIZE here. So for a 64k page - * size we would have to go up to 128k. - */ - if (size <= 8 * 1024) return 13; - if (size <= 16 * 1024) return 14; - if (size <= 32 * 1024) return 15; - if (size <= 64 * 1024) return 16; - if (size <= 128 * 1024) return 17; - if (size <= 256 * 1024) return 18; - if (size <= 512 * 1024) return 19; - if (size <= 1024 * 1024) return 20; - if (size <= 2 * 1024 * 1024) return 21; - BUG(); - return -1; /* Will never be reached */ - -/* - * What we really wanted to do and cannot do because of compiler issues is: - * int i; - * for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) - * if (size <= (1 << i)) - * return i; - */ -} - /* * Find the slab cache for a given combination of allocation flags and size. * -- cgit v1.2.3-70-g09d2 From e33660165c901d18e7d3df2290db070d3e4b46df Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 10 Jan 2013 19:14:18 +0000 Subject: slab: Use common kmalloc_index/kmalloc_size functions Make slab use the common functions. We can get rid of a lot of old ugly stuff as a results. Among them the sizes array and the weird include/linux/kmalloc_sizes file and some pretty bad #include statements in slab_def.h. The one thing that is different in slab is that the 32 byte cache will also be created for arches that have page sizes larger than 4K. There are numerous smaller allocations that SLOB and SLUB can handle better because of their support for smaller allocation sizes so lets keep the 32 byte slab also for arches with > 4K pages. Reviewed-by: Glauber Costa Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/kmalloc_sizes.h | 45 ----------- include/linux/slab_def.h | 47 +++--------- mm/slab.c | 169 +++++++++++++++++++----------------------- 3 files changed, 88 insertions(+), 173 deletions(-) delete mode 100644 include/linux/kmalloc_sizes.h (limited to 'include') diff --git a/include/linux/kmalloc_sizes.h b/include/linux/kmalloc_sizes.h deleted file mode 100644 index e576b848ce1..00000000000 --- a/include/linux/kmalloc_sizes.h +++ /dev/null @@ -1,45 +0,0 @@ -#if (PAGE_SIZE == 4096) - CACHE(32) -#endif - CACHE(64) -#if L1_CACHE_BYTES < 64 - CACHE(96) -#endif - CACHE(128) -#if L1_CACHE_BYTES < 128 - CACHE(192) -#endif - CACHE(256) - CACHE(512) - CACHE(1024) - CACHE(2048) - CACHE(4096) - CACHE(8192) - CACHE(16384) - CACHE(32768) - CACHE(65536) - CACHE(131072) -#if KMALLOC_MAX_SIZE >= 262144 - CACHE(262144) -#endif -#if KMALLOC_MAX_SIZE >= 524288 - CACHE(524288) -#endif -#if KMALLOC_MAX_SIZE >= 1048576 - CACHE(1048576) -#endif -#if KMALLOC_MAX_SIZE >= 2097152 - CACHE(2097152) -#endif -#if KMALLOC_MAX_SIZE >= 4194304 - CACHE(4194304) -#endif -#if KMALLOC_MAX_SIZE >= 8388608 - CACHE(8388608) -#endif -#if KMALLOC_MAX_SIZE >= 16777216 - CACHE(16777216) -#endif -#if KMALLOC_MAX_SIZE >= 33554432 - CACHE(33554432) -#endif diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index 8bb6e0eaf3c..e0f30ef9525 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -11,8 +11,6 @@ */ #include -#include /* kmalloc_sizes.h needs PAGE_SIZE */ -#include /* kmalloc_sizes.h needs L1_CACHE_BYTES */ #include /* @@ -104,15 +102,8 @@ struct kmem_cache { */ }; -/* Size description struct for general caches. */ -struct cache_sizes { - size_t cs_size; - struct kmem_cache *cs_cachep; -#ifdef CONFIG_ZONE_DMA - struct kmem_cache *cs_dmacachep; -#endif -}; -extern struct cache_sizes malloc_sizes[]; +extern struct kmem_cache *kmalloc_caches[PAGE_SHIFT + MAX_ORDER]; +extern struct kmem_cache *kmalloc_dma_caches[PAGE_SHIFT + MAX_ORDER]; void *kmem_cache_alloc(struct kmem_cache *, gfp_t); void *__kmalloc(size_t size, gfp_t flags); @@ -133,26 +124,19 @@ static __always_inline void *kmalloc(size_t size, gfp_t flags) void *ret; if (__builtin_constant_p(size)) { - int i = 0; + int i; if (!size) return ZERO_SIZE_PTR; -#define CACHE(x) \ - if (size <= x) \ - goto found; \ - else \ - i++; -#include -#undef CACHE - return NULL; -found: + i = kmalloc_index(size); + #ifdef CONFIG_ZONE_DMA if (flags & GFP_DMA) - cachep = malloc_sizes[i].cs_dmacachep; + cachep = kmalloc_dma_caches[i]; else #endif - cachep = malloc_sizes[i].cs_cachep; + cachep = kmalloc_caches[i]; ret = kmem_cache_alloc_trace(cachep, flags, size); @@ -186,26 +170,19 @@ static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node) struct kmem_cache *cachep; if (__builtin_constant_p(size)) { - int i = 0; + int i; if (!size) return ZERO_SIZE_PTR; -#define CACHE(x) \ - if (size <= x) \ - goto found; \ - else \ - i++; -#include -#undef CACHE - return NULL; -found: + i = kmalloc_index(size); + #ifdef CONFIG_ZONE_DMA if (flags & GFP_DMA) - cachep = malloc_sizes[i].cs_dmacachep; + cachep = kmalloc_dma_caches[i]; else #endif - cachep = malloc_sizes[i].cs_cachep; + cachep = kmalloc_caches[i]; return kmem_cache_alloc_node_trace(cachep, flags, node, size); } diff --git a/mm/slab.c b/mm/slab.c index e7667a3584b..2a7132ec4ff 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -318,34 +318,18 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int len, static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp); static void cache_reap(struct work_struct *unused); -/* - * This function must be completely optimized away if a constant is passed to - * it. Mostly the same as what is in linux/slab.h except it returns an index. - */ -static __always_inline int index_of(const size_t size) -{ - extern void __bad_size(void); - - if (__builtin_constant_p(size)) { - int i = 0; +struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1]; +EXPORT_SYMBOL(kmalloc_caches); -#define CACHE(x) \ - if (size <=x) \ - return i; \ - else \ - i++; -#include -#undef CACHE - __bad_size(); - } else - __bad_size(); - return 0; -} +#ifdef CONFIG_ZONE_DMA +struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1]; +EXPORT_SYMBOL(kmalloc_dma_caches); +#endif static int slab_early_init = 1; -#define INDEX_AC index_of(sizeof(struct arraycache_init)) -#define INDEX_L3 index_of(sizeof(struct kmem_list3)) +#define INDEX_AC kmalloc_index(sizeof(struct arraycache_init)) +#define INDEX_L3 kmalloc_index(sizeof(struct kmem_list3)) static void kmem_list3_init(struct kmem_list3 *parent) { @@ -524,30 +508,6 @@ static inline unsigned int obj_to_index(const struct kmem_cache *cache, return reciprocal_divide(offset, cache->reciprocal_buffer_size); } -/* - * These are the default caches for kmalloc. Custom caches can have other sizes. - */ -struct cache_sizes malloc_sizes[] = { -#define CACHE(x) { .cs_size = (x) }, -#include - CACHE(ULONG_MAX) -#undef CACHE -}; -EXPORT_SYMBOL(malloc_sizes); - -/* Must match cache_sizes above. Out of line to keep cache footprint low. */ -struct cache_names { - char *name; - char *name_dma; -}; - -static struct cache_names __initdata cache_names[] = { -#define CACHE(x) { .name = "size-" #x, .name_dma = "size-" #x "(DMA)" }, -#include - {NULL,} -#undef CACHE -}; - static struct arraycache_init initarray_generic = { {0, BOOT_CPUCACHE_ENTRIES, 1, 0} }; @@ -625,19 +585,23 @@ static void slab_set_debugobj_lock_classes(struct kmem_cache *cachep) static void init_node_lock_keys(int q) { - struct cache_sizes *s = malloc_sizes; + int i; if (slab_state < UP) return; - for (s = malloc_sizes; s->cs_size != ULONG_MAX; s++) { + for (i = 1; i < PAGE_SHIFT + MAX_ORDER; i++) { struct kmem_list3 *l3; + struct kmem_cache *cache = kmalloc_caches[i]; + + if (!cache) + continue; - l3 = s->cs_cachep->nodelists[q]; - if (!l3 || OFF_SLAB(s->cs_cachep)) + l3 = cache->nodelists[q]; + if (!l3 || OFF_SLAB(cache)) continue; - slab_set_lock_classes(s->cs_cachep, &on_slab_l3_key, + slab_set_lock_classes(cache, &on_slab_l3_key, &on_slab_alc_key, q); } } @@ -705,20 +669,19 @@ static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep) static inline struct kmem_cache *__find_general_cachep(size_t size, gfp_t gfpflags) { - struct cache_sizes *csizep = malloc_sizes; + int i; #if DEBUG /* This happens if someone tries to call * kmem_cache_create(), or __kmalloc(), before * the generic caches are initialized. */ - BUG_ON(malloc_sizes[INDEX_AC].cs_cachep == NULL); + BUG_ON(kmalloc_caches[INDEX_AC] == NULL); #endif if (!size) return ZERO_SIZE_PTR; - while (size > csizep->cs_size) - csizep++; + i = kmalloc_index(size); /* * Really subtle: The last entry with cs->cs_size==ULONG_MAX @@ -727,9 +690,9 @@ static inline struct kmem_cache *__find_general_cachep(size_t size, */ #ifdef CONFIG_ZONE_DMA if (unlikely(gfpflags & GFP_DMA)) - return csizep->cs_dmacachep; + return kmalloc_dma_caches[i]; #endif - return csizep->cs_cachep; + return kmalloc_caches[i]; } static struct kmem_cache *kmem_find_general_cachep(size_t size, gfp_t gfpflags) @@ -1602,8 +1565,6 @@ static void setup_nodelists_pointer(struct kmem_cache *cachep) */ void __init kmem_cache_init(void) { - struct cache_sizes *sizes; - struct cache_names *names; int i; kmem_cache = &kmem_cache_boot; @@ -1657,8 +1618,6 @@ void __init kmem_cache_init(void) list_add(&kmem_cache->list, &slab_caches); /* 2+3) create the kmalloc caches */ - sizes = malloc_sizes; - names = cache_names; /* * Initialize the caches that provide memory for the array cache and the @@ -1666,35 +1625,39 @@ void __init kmem_cache_init(void) * bug. */ - sizes[INDEX_AC].cs_cachep = create_kmalloc_cache(names[INDEX_AC].name, - sizes[INDEX_AC].cs_size, ARCH_KMALLOC_FLAGS); + kmalloc_caches[INDEX_AC] = create_kmalloc_cache("kmalloc-ac", + kmalloc_size(INDEX_AC), ARCH_KMALLOC_FLAGS); if (INDEX_AC != INDEX_L3) - sizes[INDEX_L3].cs_cachep = - create_kmalloc_cache(names[INDEX_L3].name, - sizes[INDEX_L3].cs_size, ARCH_KMALLOC_FLAGS); + kmalloc_caches[INDEX_L3] = + create_kmalloc_cache("kmalloc-l3", + kmalloc_size(INDEX_L3), ARCH_KMALLOC_FLAGS); slab_early_init = 0; - while (sizes->cs_size != ULONG_MAX) { - /* - * For performance, all the general caches are L1 aligned. - * This should be particularly beneficial on SMP boxes, as it - * eliminates "false sharing". - * Note for systems short on memory removing the alignment will - * allow tighter packing of the smaller caches. - */ - if (!sizes->cs_cachep) - sizes->cs_cachep = create_kmalloc_cache(names->name, - sizes->cs_size, ARCH_KMALLOC_FLAGS); + for (i = 1; i < PAGE_SHIFT + MAX_ORDER; i++) { + size_t cs_size = kmalloc_size(i); + + if (cs_size < KMALLOC_MIN_SIZE) + continue; + + if (!kmalloc_caches[i]) { + /* + * For performance, all the general caches are L1 aligned. + * This should be particularly beneficial on SMP boxes, as it + * eliminates "false sharing". + * Note for systems short on memory removing the alignment will + * allow tighter packing of the smaller caches. + */ + kmalloc_caches[i] = create_kmalloc_cache("kmalloc", + cs_size, ARCH_KMALLOC_FLAGS); + } #ifdef CONFIG_ZONE_DMA - sizes->cs_dmacachep = create_kmalloc_cache( - names->name_dma, sizes->cs_size, + kmalloc_dma_caches[i] = create_kmalloc_cache( + "kmalloc-dma", cs_size, SLAB_CACHE_DMA|ARCH_KMALLOC_FLAGS); #endif - sizes++; - names++; } /* 4) Replace the bootstrap head arrays */ { @@ -1713,17 +1676,16 @@ void __init kmem_cache_init(void) ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT); - BUG_ON(cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep) + BUG_ON(cpu_cache_get(kmalloc_caches[INDEX_AC]) != &initarray_generic.cache); - memcpy(ptr, cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep), + memcpy(ptr, cpu_cache_get(kmalloc_caches[INDEX_AC]), sizeof(struct arraycache_init)); /* * Do not assume that spinlocks can be initialized via memcpy: */ spin_lock_init(&ptr->lock); - malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()] = - ptr; + kmalloc_caches[INDEX_AC]->array[smp_processor_id()] = ptr; } /* 5) Replace the bootstrap kmem_list3's */ { @@ -1732,17 +1694,39 @@ void __init kmem_cache_init(void) for_each_online_node(nid) { init_list(kmem_cache, &initkmem_list3[CACHE_CACHE + nid], nid); - init_list(malloc_sizes[INDEX_AC].cs_cachep, + init_list(kmalloc_caches[INDEX_AC], &initkmem_list3[SIZE_AC + nid], nid); if (INDEX_AC != INDEX_L3) { - init_list(malloc_sizes[INDEX_L3].cs_cachep, + init_list(kmalloc_caches[INDEX_L3], &initkmem_list3[SIZE_L3 + nid], nid); } } } slab_state = UP; + + /* Create the proper names */ + for (i = 1; i < PAGE_SHIFT + MAX_ORDER; i++) { + char *s; + struct kmem_cache *c = kmalloc_caches[i]; + + if (!c) + continue; + + s = kasprintf(GFP_NOWAIT, "kmalloc-%d", kmalloc_size(i)); + + BUG_ON(!s); + c->name = s; + +#ifdef CONFIG_ZONE_DMA + c = kmalloc_dma_caches[i]; + BUG_ON(!c); + s = kasprintf(GFP_NOWAIT, "dma-kmalloc-%d", kmalloc_size(i)); + BUG_ON(!s); + c->name = s; +#endif + } } void __init kmem_cache_init_late(void) @@ -2428,10 +2412,9 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags) size += BYTES_PER_WORD; } #if FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC) - if (size >= malloc_sizes[INDEX_L3 + 1].cs_size - && cachep->object_size > cache_line_size() - && ALIGN(size, cachep->align) < PAGE_SIZE) { - cachep->obj_offset += PAGE_SIZE - ALIGN(size, cachep->align); + if (size >= kmalloc_size(INDEX_L3 + 1) + && cachep->object_size > cache_line_size() && ALIGN(size, align) < PAGE_SIZE) { + cachep->obj_offset += PAGE_SIZE - ALIGN(size, align); size = PAGE_SIZE; } #endif -- cgit v1.2.3-70-g09d2 From 6744f087ba2a49f6d6935d9daa0b20a0f03567b5 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 10 Jan 2013 19:12:17 +0000 Subject: slab: Common name for the per node structures Rename the structure used for the per node structures in slab to have a name that expresses that fact. Acked-by: Glauber Costa Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab_def.h | 2 +- mm/slab.c | 87 ++++++++++++++++++++++++------------------------ 2 files changed, 44 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index e0f30ef9525..8b5b2f6b36d 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -95,7 +95,7 @@ struct kmem_cache { * pointer for each node since "nodelists" uses the remainder of * available pointers. */ - struct kmem_list3 **nodelists; + struct kmem_cache_node **nodelists; struct array_cache *array[NR_CPUS + MAX_NUMNODES]; /* * Do not add fields after array[] diff --git a/mm/slab.c b/mm/slab.c index 2a7132ec4ff..7c0da4c8697 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -288,7 +288,7 @@ struct arraycache_init { /* * The slab lists for all objects. */ -struct kmem_list3 { +struct kmem_cache_node { struct list_head slabs_partial; /* partial list first, better asm code */ struct list_head slabs_full; struct list_head slabs_free; @@ -306,13 +306,13 @@ struct kmem_list3 { * Need this for bootstrapping a per node allocator. */ #define NUM_INIT_LISTS (3 * MAX_NUMNODES) -static struct kmem_list3 __initdata initkmem_list3[NUM_INIT_LISTS]; +static struct kmem_cache_node __initdata initkmem_list3[NUM_INIT_LISTS]; #define CACHE_CACHE 0 #define SIZE_AC MAX_NUMNODES #define SIZE_L3 (2 * MAX_NUMNODES) static int drain_freelist(struct kmem_cache *cache, - struct kmem_list3 *l3, int tofree); + struct kmem_cache_node *l3, int tofree); static void free_block(struct kmem_cache *cachep, void **objpp, int len, int node); static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp); @@ -329,9 +329,9 @@ EXPORT_SYMBOL(kmalloc_dma_caches); static int slab_early_init = 1; #define INDEX_AC kmalloc_index(sizeof(struct arraycache_init)) -#define INDEX_L3 kmalloc_index(sizeof(struct kmem_list3)) +#define INDEX_L3 kmalloc_index(sizeof(struct kmem_cache_node)) -static void kmem_list3_init(struct kmem_list3 *parent) +static void kmem_list3_init(struct kmem_cache_node *parent) { INIT_LIST_HEAD(&parent->slabs_full); INIT_LIST_HEAD(&parent->slabs_partial); @@ -546,7 +546,7 @@ static void slab_set_lock_classes(struct kmem_cache *cachep, int q) { struct array_cache **alc; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; int r; l3 = cachep->nodelists[q]; @@ -591,7 +591,7 @@ static void init_node_lock_keys(int q) return; for (i = 1; i < PAGE_SHIFT + MAX_ORDER; i++) { - struct kmem_list3 *l3; + struct kmem_cache_node *l3; struct kmem_cache *cache = kmalloc_caches[i]; if (!cache) @@ -608,9 +608,8 @@ static void init_node_lock_keys(int q) static void on_slab_lock_classes_node(struct kmem_cache *cachep, int q) { - struct kmem_list3 *l3; - l3 = cachep->nodelists[q]; - if (!l3) + + if (!cachep->nodelists[q]) return; slab_set_lock_classes(cachep, &on_slab_l3_key, @@ -901,7 +900,7 @@ static inline bool is_slab_pfmemalloc(struct slab *slabp) static void recheck_pfmemalloc_active(struct kmem_cache *cachep, struct array_cache *ac) { - struct kmem_list3 *l3 = cachep->nodelists[numa_mem_id()]; + struct kmem_cache_node *l3 = cachep->nodelists[numa_mem_id()]; struct slab *slabp; unsigned long flags; @@ -934,7 +933,7 @@ static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac, /* Ensure the caller is allowed to use objects from PFMEMALLOC slab */ if (unlikely(is_obj_pfmemalloc(objp))) { - struct kmem_list3 *l3; + struct kmem_cache_node *l3; if (gfp_pfmemalloc_allowed(flags)) { clear_obj_pfmemalloc(&objp); @@ -1106,7 +1105,7 @@ static void free_alien_cache(struct array_cache **ac_ptr) static void __drain_alien_cache(struct kmem_cache *cachep, struct array_cache *ac, int node) { - struct kmem_list3 *rl3 = cachep->nodelists[node]; + struct kmem_cache_node *rl3 = cachep->nodelists[node]; if (ac->avail) { spin_lock(&rl3->list_lock); @@ -1127,7 +1126,7 @@ static void __drain_alien_cache(struct kmem_cache *cachep, /* * Called from cache_reap() to regularly drain alien caches round robin. */ -static void reap_alien(struct kmem_cache *cachep, struct kmem_list3 *l3) +static void reap_alien(struct kmem_cache *cachep, struct kmem_cache_node *l3) { int node = __this_cpu_read(slab_reap_node); @@ -1162,7 +1161,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp) { struct slab *slabp = virt_to_slab(objp); int nodeid = slabp->nodeid; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; struct array_cache *alien = NULL; int node; @@ -1207,8 +1206,8 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp) static int init_cache_nodelists_node(int node) { struct kmem_cache *cachep; - struct kmem_list3 *l3; - const int memsize = sizeof(struct kmem_list3); + struct kmem_cache_node *l3; + const int memsize = sizeof(struct kmem_cache_node); list_for_each_entry(cachep, &slab_caches, list) { /* @@ -1244,7 +1243,7 @@ static int init_cache_nodelists_node(int node) static void __cpuinit cpuup_canceled(long cpu) { struct kmem_cache *cachep; - struct kmem_list3 *l3 = NULL; + struct kmem_cache_node *l3 = NULL; int node = cpu_to_mem(cpu); const struct cpumask *mask = cpumask_of_node(node); @@ -1309,7 +1308,7 @@ free_array_cache: static int __cpuinit cpuup_prepare(long cpu) { struct kmem_cache *cachep; - struct kmem_list3 *l3 = NULL; + struct kmem_cache_node *l3 = NULL; int node = cpu_to_mem(cpu); int err; @@ -1463,7 +1462,7 @@ static int __meminit drain_cache_nodelists_node(int node) int ret = 0; list_for_each_entry(cachep, &slab_caches, list) { - struct kmem_list3 *l3; + struct kmem_cache_node *l3; l3 = cachep->nodelists[node]; if (!l3) @@ -1516,15 +1515,15 @@ out: /* * swap the static kmem_list3 with kmalloced memory */ -static void __init init_list(struct kmem_cache *cachep, struct kmem_list3 *list, +static void __init init_list(struct kmem_cache *cachep, struct kmem_cache_node *list, int nodeid) { - struct kmem_list3 *ptr; + struct kmem_cache_node *ptr; - ptr = kmalloc_node(sizeof(struct kmem_list3), GFP_NOWAIT, nodeid); + ptr = kmalloc_node(sizeof(struct kmem_cache_node), GFP_NOWAIT, nodeid); BUG_ON(!ptr); - memcpy(ptr, list, sizeof(struct kmem_list3)); + memcpy(ptr, list, sizeof(struct kmem_cache_node)); /* * Do not assume that spinlocks can be initialized via memcpy: */ @@ -1556,7 +1555,7 @@ static void __init set_up_list3s(struct kmem_cache *cachep, int index) */ static void setup_nodelists_pointer(struct kmem_cache *cachep) { - cachep->nodelists = (struct kmem_list3 **)&cachep->array[nr_cpu_ids]; + cachep->nodelists = (struct kmem_cache_node **)&cachep->array[nr_cpu_ids]; } /* @@ -1613,7 +1612,7 @@ void __init kmem_cache_init(void) */ create_boot_cache(kmem_cache, "kmem_cache", offsetof(struct kmem_cache, array[nr_cpu_ids]) + - nr_node_ids * sizeof(struct kmem_list3 *), + nr_node_ids * sizeof(struct kmem_cache_node *), SLAB_HWCACHE_ALIGN); list_add(&kmem_cache->list, &slab_caches); @@ -1787,7 +1786,7 @@ __initcall(cpucache_init); static noinline void slab_out_of_memory(struct kmem_cache *cachep, gfp_t gfpflags, int nodeid) { - struct kmem_list3 *l3; + struct kmem_cache_node *l3; struct slab *slabp; unsigned long flags; int node; @@ -2279,7 +2278,7 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) int node; for_each_online_node(node) { cachep->nodelists[node] = - kmalloc_node(sizeof(struct kmem_list3), + kmalloc_node(sizeof(struct kmem_cache_node), gfp, node); BUG_ON(!cachep->nodelists[node]); kmem_list3_init(cachep->nodelists[node]); @@ -2547,7 +2546,7 @@ static void check_spinlock_acquired_node(struct kmem_cache *cachep, int node) #define check_spinlock_acquired_node(x, y) do { } while(0) #endif -static void drain_array(struct kmem_cache *cachep, struct kmem_list3 *l3, +static void drain_array(struct kmem_cache *cachep, struct kmem_cache_node *l3, struct array_cache *ac, int force, int node); @@ -2567,7 +2566,7 @@ static void do_drain(void *arg) static void drain_cpu_caches(struct kmem_cache *cachep) { - struct kmem_list3 *l3; + struct kmem_cache_node *l3; int node; on_each_cpu(do_drain, cachep, 1); @@ -2592,7 +2591,7 @@ static void drain_cpu_caches(struct kmem_cache *cachep) * Returns the actual number of slabs released. */ static int drain_freelist(struct kmem_cache *cache, - struct kmem_list3 *l3, int tofree) + struct kmem_cache_node *l3, int tofree) { struct list_head *p; int nr_freed; @@ -2630,7 +2629,7 @@ out: static int __cache_shrink(struct kmem_cache *cachep) { int ret = 0, i = 0; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; drain_cpu_caches(cachep); @@ -2672,7 +2671,7 @@ EXPORT_SYMBOL(kmem_cache_shrink); int __kmem_cache_shutdown(struct kmem_cache *cachep) { int i; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; int rc = __cache_shrink(cachep); if (rc) @@ -2869,7 +2868,7 @@ static int cache_grow(struct kmem_cache *cachep, struct slab *slabp; size_t offset; gfp_t local_flags; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; /* * Be lazy and only check for valid flags here, keeping it out of the @@ -3059,7 +3058,7 @@ static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags, bool force_refill) { int batchcount; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; struct array_cache *ac; int node; @@ -3391,7 +3390,7 @@ static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, { struct list_head *entry; struct slab *slabp; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; void *obj; int x; @@ -3586,7 +3585,7 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects, int node) { int i; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; for (i = 0; i < nr_objects; i++) { void *objp; @@ -3632,7 +3631,7 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects, static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac) { int batchcount; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; int node = numa_mem_id(); batchcount = ac->batchcount; @@ -3924,7 +3923,7 @@ EXPORT_SYMBOL(kfree); static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp) { int node; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; struct array_cache *new_shared; struct array_cache **new_alien = NULL; @@ -3969,7 +3968,7 @@ static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp) free_alien_cache(new_alien); continue; } - l3 = kmalloc_node(sizeof(struct kmem_list3), gfp, node); + l3 = kmalloc_node(sizeof(struct kmem_cache_node), gfp, node); if (!l3) { free_alien_cache(new_alien); kfree(new_shared); @@ -4165,7 +4164,7 @@ skip_setup: * necessary. Note that the l3 listlock also protects the array_cache * if drain_array() is used on the shared array. */ -static void drain_array(struct kmem_cache *cachep, struct kmem_list3 *l3, +static void drain_array(struct kmem_cache *cachep, struct kmem_cache_node *l3, struct array_cache *ac, int force, int node) { int tofree; @@ -4204,7 +4203,7 @@ static void drain_array(struct kmem_cache *cachep, struct kmem_list3 *l3, static void cache_reap(struct work_struct *w) { struct kmem_cache *searchp; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; int node = numa_mem_id(); struct delayed_work *work = to_delayed_work(w); @@ -4268,7 +4267,7 @@ void get_slabinfo(struct kmem_cache *cachep, struct slabinfo *sinfo) const char *name; char *error = NULL; int node; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; active_objs = 0; num_slabs = 0; @@ -4482,7 +4481,7 @@ static int leaks_show(struct seq_file *m, void *p) { struct kmem_cache *cachep = list_entry(p, struct kmem_cache, list); struct slab *slabp; - struct kmem_list3 *l3; + struct kmem_cache_node *l3; const char *name; unsigned long *n = m->private; int node; -- cgit v1.2.3-70-g09d2 From 6a67368c36e2c0c2578ba62f6264ab739af08cce Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 10 Jan 2013 19:14:19 +0000 Subject: slab: Rename nodelists to node Have a common naming between both slab caches for future changes. Acked-by: Glauber Costa Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab_def.h | 2 +- mm/slab.c | 135 +++++++++++++++++++++++------------------------ 2 files changed, 68 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index 8b5b2f6b36d..4ff50e8d1a2 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -95,7 +95,7 @@ struct kmem_cache { * pointer for each node since "nodelists" uses the remainder of * available pointers. */ - struct kmem_cache_node **nodelists; + struct kmem_cache_node **node; struct array_cache *array[NR_CPUS + MAX_NUMNODES]; /* * Do not add fields after array[] diff --git a/mm/slab.c b/mm/slab.c index 7c0da4c8697..3416f4c544b 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -347,7 +347,7 @@ static void kmem_list3_init(struct kmem_cache_node *parent) #define MAKE_LIST(cachep, listp, slab, nodeid) \ do { \ INIT_LIST_HEAD(listp); \ - list_splice(&(cachep->nodelists[nodeid]->slab), listp); \ + list_splice(&(cachep->node[nodeid]->slab), listp); \ } while (0) #define MAKE_ALL_LISTS(cachep, ptr, nodeid) \ @@ -549,7 +549,7 @@ static void slab_set_lock_classes(struct kmem_cache *cachep, struct kmem_cache_node *l3; int r; - l3 = cachep->nodelists[q]; + l3 = cachep->node[q]; if (!l3) return; @@ -597,7 +597,7 @@ static void init_node_lock_keys(int q) if (!cache) continue; - l3 = cache->nodelists[q]; + l3 = cache->node[q]; if (!l3 || OFF_SLAB(cache)) continue; @@ -608,8 +608,7 @@ static void init_node_lock_keys(int q) static void on_slab_lock_classes_node(struct kmem_cache *cachep, int q) { - - if (!cachep->nodelists[q]) + if (!cachep->node[q]) return; slab_set_lock_classes(cachep, &on_slab_l3_key, @@ -900,7 +899,7 @@ static inline bool is_slab_pfmemalloc(struct slab *slabp) static void recheck_pfmemalloc_active(struct kmem_cache *cachep, struct array_cache *ac) { - struct kmem_cache_node *l3 = cachep->nodelists[numa_mem_id()]; + struct kmem_cache_node *l3 = cachep->node[numa_mem_id()]; struct slab *slabp; unsigned long flags; @@ -955,7 +954,7 @@ static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac, * If there are empty slabs on the slabs_free list and we are * being forced to refill the cache, mark this one !pfmemalloc. */ - l3 = cachep->nodelists[numa_mem_id()]; + l3 = cachep->node[numa_mem_id()]; if (!list_empty(&l3->slabs_free) && force_refill) { struct slab *slabp = virt_to_slab(objp); ClearPageSlabPfmemalloc(virt_to_head_page(slabp->s_mem)); @@ -1105,7 +1104,7 @@ static void free_alien_cache(struct array_cache **ac_ptr) static void __drain_alien_cache(struct kmem_cache *cachep, struct array_cache *ac, int node) { - struct kmem_cache_node *rl3 = cachep->nodelists[node]; + struct kmem_cache_node *rl3 = cachep->node[node]; if (ac->avail) { spin_lock(&rl3->list_lock); @@ -1174,7 +1173,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp) if (likely(slabp->nodeid == node)) return 0; - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; STATS_INC_NODEFREES(cachep); if (l3->alien && l3->alien[nodeid]) { alien = l3->alien[nodeid]; @@ -1186,24 +1185,24 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp) ac_put_obj(cachep, alien, objp); spin_unlock(&alien->lock); } else { - spin_lock(&(cachep->nodelists[nodeid])->list_lock); + spin_lock(&(cachep->node[nodeid])->list_lock); free_block(cachep, &objp, 1, nodeid); - spin_unlock(&(cachep->nodelists[nodeid])->list_lock); + spin_unlock(&(cachep->node[nodeid])->list_lock); } return 1; } #endif /* - * Allocates and initializes nodelists for a node on each slab cache, used for + * Allocates and initializes node for a node on each slab cache, used for * either memory or cpu hotplug. If memory is being hot-added, the kmem_list3 * will be allocated off-node since memory is not yet online for the new node. - * When hotplugging memory or a cpu, existing nodelists are not replaced if + * When hotplugging memory or a cpu, existing node are not replaced if * already in use. * * Must hold slab_mutex. */ -static int init_cache_nodelists_node(int node) +static int init_cache_node_node(int node) { struct kmem_cache *cachep; struct kmem_cache_node *l3; @@ -1215,7 +1214,7 @@ static int init_cache_nodelists_node(int node) * begin anything. Make sure some other cpu on this * node has not already allocated this */ - if (!cachep->nodelists[node]) { + if (!cachep->node[node]) { l3 = kmalloc_node(memsize, GFP_KERNEL, node); if (!l3) return -ENOMEM; @@ -1228,14 +1227,14 @@ static int init_cache_nodelists_node(int node) * go. slab_mutex is sufficient * protection here. */ - cachep->nodelists[node] = l3; + cachep->node[node] = l3; } - spin_lock_irq(&cachep->nodelists[node]->list_lock); - cachep->nodelists[node]->free_limit = + spin_lock_irq(&cachep->node[node]->list_lock); + cachep->node[node]->free_limit = (1 + nr_cpus_node(node)) * cachep->batchcount + cachep->num; - spin_unlock_irq(&cachep->nodelists[node]->list_lock); + spin_unlock_irq(&cachep->node[node]->list_lock); } return 0; } @@ -1255,7 +1254,7 @@ static void __cpuinit cpuup_canceled(long cpu) /* cpu is dead; no one can alloc from it. */ nc = cachep->array[cpu]; cachep->array[cpu] = NULL; - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; if (!l3) goto free_array_cache; @@ -1298,7 +1297,7 @@ free_array_cache: * shrink each nodelist to its limit. */ list_for_each_entry(cachep, &slab_caches, list) { - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; if (!l3) continue; drain_freelist(cachep, l3, l3->free_objects); @@ -1318,7 +1317,7 @@ static int __cpuinit cpuup_prepare(long cpu) * kmalloc_node allows us to add the slab to the right * kmem_list3 and not this cpu's kmem_list3 */ - err = init_cache_nodelists_node(node); + err = init_cache_node_node(node); if (err < 0) goto bad; @@ -1353,7 +1352,7 @@ static int __cpuinit cpuup_prepare(long cpu) } } cachep->array[cpu] = nc; - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; BUG_ON(!l3); spin_lock_irq(&l3->list_lock); @@ -1456,7 +1455,7 @@ static struct notifier_block __cpuinitdata cpucache_notifier = { * * Must hold slab_mutex. */ -static int __meminit drain_cache_nodelists_node(int node) +static int __meminit drain_cache_node_node(int node) { struct kmem_cache *cachep; int ret = 0; @@ -1464,7 +1463,7 @@ static int __meminit drain_cache_nodelists_node(int node) list_for_each_entry(cachep, &slab_caches, list) { struct kmem_cache_node *l3; - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; if (!l3) continue; @@ -1493,12 +1492,12 @@ static int __meminit slab_memory_callback(struct notifier_block *self, switch (action) { case MEM_GOING_ONLINE: mutex_lock(&slab_mutex); - ret = init_cache_nodelists_node(nid); + ret = init_cache_node_node(nid); mutex_unlock(&slab_mutex); break; case MEM_GOING_OFFLINE: mutex_lock(&slab_mutex); - ret = drain_cache_nodelists_node(nid); + ret = drain_cache_node_node(nid); mutex_unlock(&slab_mutex); break; case MEM_ONLINE: @@ -1530,7 +1529,7 @@ static void __init init_list(struct kmem_cache *cachep, struct kmem_cache_node * spin_lock_init(&ptr->list_lock); MAKE_ALL_LISTS(cachep, ptr, nodeid); - cachep->nodelists[nodeid] = ptr; + cachep->node[nodeid] = ptr; } /* @@ -1542,8 +1541,8 @@ static void __init set_up_list3s(struct kmem_cache *cachep, int index) int node; for_each_online_node(node) { - cachep->nodelists[node] = &initkmem_list3[index + node]; - cachep->nodelists[node]->next_reap = jiffies + + cachep->node[node] = &initkmem_list3[index + node]; + cachep->node[node]->next_reap = jiffies + REAPTIMEOUT_LIST3 + ((unsigned long)cachep) % REAPTIMEOUT_LIST3; } @@ -1551,11 +1550,11 @@ static void __init set_up_list3s(struct kmem_cache *cachep, int index) /* * The memory after the last cpu cache pointer is used for the - * the nodelists pointer. + * the node pointer. */ -static void setup_nodelists_pointer(struct kmem_cache *cachep) +static void setup_node_pointer(struct kmem_cache *cachep) { - cachep->nodelists = (struct kmem_cache_node **)&cachep->array[nr_cpu_ids]; + cachep->node = (struct kmem_cache_node **)&cachep->array[nr_cpu_ids]; } /* @@ -1567,7 +1566,7 @@ void __init kmem_cache_init(void) int i; kmem_cache = &kmem_cache_boot; - setup_nodelists_pointer(kmem_cache); + setup_node_pointer(kmem_cache); if (num_possible_nodes() == 1) use_alien_caches = 0; @@ -1756,7 +1755,7 @@ void __init kmem_cache_init_late(void) #ifdef CONFIG_NUMA /* * Register a memory hotplug callback that initializes and frees - * nodelists. + * node. */ hotplug_memory_notifier(slab_memory_callback, SLAB_CALLBACK_PRI); #endif @@ -1801,7 +1800,7 @@ slab_out_of_memory(struct kmem_cache *cachep, gfp_t gfpflags, int nodeid) unsigned long active_objs = 0, num_objs = 0, free_objects = 0; unsigned long active_slabs = 0, num_slabs = 0; - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; if (!l3) continue; @@ -2277,15 +2276,15 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) } else { int node; for_each_online_node(node) { - cachep->nodelists[node] = + cachep->node[node] = kmalloc_node(sizeof(struct kmem_cache_node), gfp, node); - BUG_ON(!cachep->nodelists[node]); - kmem_list3_init(cachep->nodelists[node]); + BUG_ON(!cachep->node[node]); + kmem_list3_init(cachep->node[node]); } } } - cachep->nodelists[numa_mem_id()]->next_reap = + cachep->node[numa_mem_id()]->next_reap = jiffies + REAPTIMEOUT_LIST3 + ((unsigned long)cachep) % REAPTIMEOUT_LIST3; @@ -2388,7 +2387,7 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags) else gfp = GFP_NOWAIT; - setup_nodelists_pointer(cachep); + setup_node_pointer(cachep); #if DEBUG /* @@ -2527,7 +2526,7 @@ static void check_spinlock_acquired(struct kmem_cache *cachep) { #ifdef CONFIG_SMP check_irq_off(); - assert_spin_locked(&cachep->nodelists[numa_mem_id()]->list_lock); + assert_spin_locked(&cachep->node[numa_mem_id()]->list_lock); #endif } @@ -2535,7 +2534,7 @@ static void check_spinlock_acquired_node(struct kmem_cache *cachep, int node) { #ifdef CONFIG_SMP check_irq_off(); - assert_spin_locked(&cachep->nodelists[node]->list_lock); + assert_spin_locked(&cachep->node[node]->list_lock); #endif } @@ -2558,9 +2557,9 @@ static void do_drain(void *arg) check_irq_off(); ac = cpu_cache_get(cachep); - spin_lock(&cachep->nodelists[node]->list_lock); + spin_lock(&cachep->node[node]->list_lock); free_block(cachep, ac->entry, ac->avail, node); - spin_unlock(&cachep->nodelists[node]->list_lock); + spin_unlock(&cachep->node[node]->list_lock); ac->avail = 0; } @@ -2572,13 +2571,13 @@ static void drain_cpu_caches(struct kmem_cache *cachep) on_each_cpu(do_drain, cachep, 1); check_irq_on(); for_each_online_node(node) { - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; if (l3 && l3->alien) drain_alien_cache(cachep, l3->alien); } for_each_online_node(node) { - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; if (l3) drain_array(cachep, l3, l3->shared, 1, node); } @@ -2635,7 +2634,7 @@ static int __cache_shrink(struct kmem_cache *cachep) check_irq_on(); for_each_online_node(i) { - l3 = cachep->nodelists[i]; + l3 = cachep->node[i]; if (!l3) continue; @@ -2682,7 +2681,7 @@ int __kmem_cache_shutdown(struct kmem_cache *cachep) /* NUMA: free the list3 structures */ for_each_online_node(i) { - l3 = cachep->nodelists[i]; + l3 = cachep->node[i]; if (l3) { kfree(l3->shared); free_alien_cache(l3->alien); @@ -2879,7 +2878,7 @@ static int cache_grow(struct kmem_cache *cachep, /* Take the l3 list lock to change the colour_next on this node */ check_irq_off(); - l3 = cachep->nodelists[nodeid]; + l3 = cachep->node[nodeid]; spin_lock(&l3->list_lock); /* Get colour for the slab, and cal the next value. */ @@ -3077,7 +3076,7 @@ retry: */ batchcount = BATCHREFILL_LIMIT; } - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; BUG_ON(ac->avail > 0 || !l3); spin_lock(&l3->list_lock); @@ -3299,7 +3298,7 @@ static void *alternate_node_alloc(struct kmem_cache *cachep, gfp_t flags) /* * Fallback function if there was no memory available and no objects on a * certain node and fall back is permitted. First we scan all the - * available nodelists for available objects. If that fails then we + * available node for available objects. If that fails then we * perform an allocation without specifying a node. This allows the page * allocator to do its reclaim / fallback magic. We then insert the * slab into the proper nodelist and then allocate from it. @@ -3333,8 +3332,8 @@ retry: nid = zone_to_nid(zone); if (cpuset_zone_allowed_hardwall(zone, flags) && - cache->nodelists[nid] && - cache->nodelists[nid]->free_objects) { + cache->node[nid] && + cache->node[nid]->free_objects) { obj = ____cache_alloc_node(cache, flags | GFP_THISNODE, nid); if (obj) @@ -3394,7 +3393,7 @@ static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, void *obj; int x; - l3 = cachep->nodelists[nodeid]; + l3 = cachep->node[nodeid]; BUG_ON(!l3); retry: @@ -3479,7 +3478,7 @@ slab_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid, if (nodeid == NUMA_NO_NODE) nodeid = slab_node; - if (unlikely(!cachep->nodelists[nodeid])) { + if (unlikely(!cachep->node[nodeid])) { /* Node not bootstrapped yet */ ptr = fallback_alloc(cachep, flags); goto out; @@ -3595,7 +3594,7 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects, objp = objpp[i]; slabp = virt_to_slab(objp); - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; list_del(&slabp->list); check_spinlock_acquired_node(cachep, node); check_slabp(cachep, slabp); @@ -3639,7 +3638,7 @@ static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac) BUG_ON(!batchcount || batchcount > ac->avail); #endif check_irq_off(); - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; spin_lock(&l3->list_lock); if (l3->shared) { struct array_cache *shared_array = l3->shared; @@ -3946,7 +3945,7 @@ static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp) } } - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; if (l3) { struct array_cache *shared = l3->shared; @@ -3982,7 +3981,7 @@ static int alloc_kmemlist(struct kmem_cache *cachep, gfp_t gfp) l3->alien = new_alien; l3->free_limit = (1 + nr_cpus_node(node)) * cachep->batchcount + cachep->num; - cachep->nodelists[node] = l3; + cachep->node[node] = l3; } return 0; @@ -3991,13 +3990,13 @@ fail: /* Cache is not active yet. Roll back what we did */ node--; while (node >= 0) { - if (cachep->nodelists[node]) { - l3 = cachep->nodelists[node]; + if (cachep->node[node]) { + l3 = cachep->node[node]; kfree(l3->shared); free_alien_cache(l3->alien); kfree(l3); - cachep->nodelists[node] = NULL; + cachep->node[node] = NULL; } node--; } @@ -4057,9 +4056,9 @@ static int __do_tune_cpucache(struct kmem_cache *cachep, int limit, struct array_cache *ccold = new->new[i]; if (!ccold) continue; - spin_lock_irq(&cachep->nodelists[cpu_to_mem(i)]->list_lock); + spin_lock_irq(&cachep->node[cpu_to_mem(i)]->list_lock); free_block(cachep, ccold->entry, ccold->avail, cpu_to_mem(i)); - spin_unlock_irq(&cachep->nodelists[cpu_to_mem(i)]->list_lock); + spin_unlock_irq(&cachep->node[cpu_to_mem(i)]->list_lock); kfree(ccold); } kfree(new); @@ -4219,7 +4218,7 @@ static void cache_reap(struct work_struct *w) * have established with reasonable certainty that * we can do some work if the lock was obtained. */ - l3 = searchp->nodelists[node]; + l3 = searchp->node[node]; reap_alien(searchp, l3); @@ -4272,7 +4271,7 @@ void get_slabinfo(struct kmem_cache *cachep, struct slabinfo *sinfo) active_objs = 0; num_slabs = 0; for_each_online_node(node) { - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; if (!l3) continue; @@ -4497,7 +4496,7 @@ static int leaks_show(struct seq_file *m, void *p) n[1] = 0; for_each_online_node(node) { - l3 = cachep->nodelists[node]; + l3 = cachep->node[node]; if (!l3) continue; -- cgit v1.2.3-70-g09d2 From 95a05b428cc675694321c8f762591984f3fd2b1e Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 10 Jan 2013 19:14:19 +0000 Subject: slab: Common constants for kmalloc boundaries Standardize the constants that describe the smallest and largest object kept in the kmalloc arrays for SLAB and SLUB. Differentiate between the maximum size for which a slab cache is used (KMALLOC_MAX_CACHE_SIZE) and the maximum allocatable size (KMALLOC_MAX_SIZE, KMALLOC_MAX_ORDER). Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 34 ++++++++++++++++++++++++---------- include/linux/slub_def.h | 19 +++---------------- mm/slub.c | 22 +++++++++++----------- 3 files changed, 38 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index c97fe92532d..c0178054005 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -163,7 +163,12 @@ struct kmem_cache { #else /* CONFIG_SLOB */ /* - * The largest kmalloc size supported by the slab allocators is + * Kmalloc array related definitions + */ + +#ifdef CONFIG_SLAB +/* + * The largest kmalloc size supported by the SLAB allocators is * 32 megabyte (2^25) or the maximum allocatable page order if that is * less than 32 MB. * @@ -173,9 +178,24 @@ struct kmem_cache { */ #define KMALLOC_SHIFT_HIGH ((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? \ (MAX_ORDER + PAGE_SHIFT - 1) : 25) +#define KMALLOC_SHIFT_MAX KMALLOC_SHIFT_HIGH +#define KMALLOC_SHIFT_LOW 5 +#else +/* + * SLUB allocates up to order 2 pages directly and otherwise + * passes the request to the page allocator. + */ +#define KMALLOC_SHIFT_HIGH (PAGE_SHIFT + 1) +#define KMALLOC_SHIFT_MAX (MAX_ORDER + PAGE_SHIFT) +#define KMALLOC_SHIFT_LOW 3 +#endif -#define KMALLOC_MAX_SIZE (1UL << KMALLOC_SHIFT_HIGH) -#define KMALLOC_MAX_ORDER (KMALLOC_SHIFT_HIGH - PAGE_SHIFT) +/* Maximum allocatable size */ +#define KMALLOC_MAX_SIZE (1UL << KMALLOC_SHIFT_MAX) +/* Maximum size for which we actually use a slab cache */ +#define KMALLOC_MAX_CACHE_SIZE (1UL << KMALLOC_SHIFT_HIGH) +/* Maximum order allocatable via the slab allocagtor */ +#define KMALLOC_MAX_ORDER (KMALLOC_SHIFT_MAX - PAGE_SHIFT) /* * Kmalloc subsystem. @@ -183,15 +203,9 @@ struct kmem_cache { #if defined(ARCH_DMA_MINALIGN) && ARCH_DMA_MINALIGN > 8 #define KMALLOC_MIN_SIZE ARCH_DMA_MINALIGN #else -#ifdef CONFIG_SLAB -#define KMALLOC_MIN_SIZE 32 -#else -#define KMALLOC_MIN_SIZE 8 -#endif +#define KMALLOC_MIN_SIZE (1 << KMALLOC_SHIFT_LOW) #endif -#define KMALLOC_SHIFT_LOW ilog2(KMALLOC_MIN_SIZE) - /* * Figure out which kmalloc slab an allocation of a certain size * belongs to. diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 99c3e05ff1f..032028ef9a3 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -115,19 +115,6 @@ struct kmem_cache { struct kmem_cache_node *node[MAX_NUMNODES]; }; -/* - * Maximum kmalloc object size handled by SLUB. Larger object allocations - * are passed through to the page allocator. The page allocator "fastpath" - * is relatively slow so we need this value sufficiently high so that - * performance critical objects are allocated through the SLUB fastpath. - * - * This should be dropped to PAGE_SIZE / 2 once the page allocator - * "fastpath" becomes competitive with the slab allocator fastpaths. - */ -#define SLUB_MAX_SIZE (2 * PAGE_SIZE) - -#define SLUB_PAGE_SHIFT (PAGE_SHIFT + 2) - #ifdef CONFIG_ZONE_DMA #define SLUB_DMA __GFP_DMA #else @@ -139,7 +126,7 @@ struct kmem_cache { * We keep the general caches in an array of slab caches that are used for * 2^x bytes of allocations. */ -extern struct kmem_cache *kmalloc_caches[SLUB_PAGE_SHIFT]; +extern struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1]; /* * Find the slab cache for a given combination of allocation flags and size. @@ -211,7 +198,7 @@ static __always_inline void *kmalloc_large(size_t size, gfp_t flags) static __always_inline void *kmalloc(size_t size, gfp_t flags) { if (__builtin_constant_p(size)) { - if (size > SLUB_MAX_SIZE) + if (size > KMALLOC_MAX_CACHE_SIZE) return kmalloc_large(size, flags); if (!(flags & SLUB_DMA)) { @@ -247,7 +234,7 @@ kmem_cache_alloc_node_trace(struct kmem_cache *s, static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node) { if (__builtin_constant_p(size) && - size <= SLUB_MAX_SIZE && !(flags & SLUB_DMA)) { + size <= KMALLOC_MAX_CACHE_SIZE && !(flags & SLUB_DMA)) { struct kmem_cache *s = kmalloc_slab(size); if (!s) diff --git a/mm/slub.c b/mm/slub.c index ba2ca53f6c3..d0f72ee0631 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2775,7 +2775,7 @@ init_kmem_cache_node(struct kmem_cache_node *n) static inline int alloc_kmem_cache_cpus(struct kmem_cache *s) { BUILD_BUG_ON(PERCPU_DYNAMIC_EARLY_SIZE < - SLUB_PAGE_SHIFT * sizeof(struct kmem_cache_cpu)); + KMALLOC_SHIFT_HIGH * sizeof(struct kmem_cache_cpu)); /* * Must align to double word boundary for the double cmpxchg @@ -3174,11 +3174,11 @@ int __kmem_cache_shutdown(struct kmem_cache *s) * Kmalloc subsystem *******************************************************************/ -struct kmem_cache *kmalloc_caches[SLUB_PAGE_SHIFT]; +struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1]; EXPORT_SYMBOL(kmalloc_caches); #ifdef CONFIG_ZONE_DMA -static struct kmem_cache *kmalloc_dma_caches[SLUB_PAGE_SHIFT]; +static struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1]; #endif static int __init setup_slub_min_order(char *str) @@ -3280,7 +3280,7 @@ void *__kmalloc(size_t size, gfp_t flags) struct kmem_cache *s; void *ret; - if (unlikely(size > SLUB_MAX_SIZE)) + if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) return kmalloc_large(size, flags); s = get_slab(size, flags); @@ -3316,7 +3316,7 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node) struct kmem_cache *s; void *ret; - if (unlikely(size > SLUB_MAX_SIZE)) { + if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) { ret = kmalloc_large_node(size, flags, node); trace_kmalloc_node(_RET_IP_, ret, @@ -3721,7 +3721,7 @@ void __init kmem_cache_init(void) caches++; } - for (i = KMALLOC_SHIFT_LOW; i < SLUB_PAGE_SHIFT; i++) { + for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) { kmalloc_caches[i] = create_kmalloc_cache("kmalloc", 1 << i, 0); caches++; } @@ -3739,7 +3739,7 @@ void __init kmem_cache_init(void) BUG_ON(!kmalloc_caches[2]->name); } - for (i = KMALLOC_SHIFT_LOW; i < SLUB_PAGE_SHIFT; i++) { + for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) { char *s = kasprintf(GFP_NOWAIT, "kmalloc-%d", 1 << i); BUG_ON(!s); @@ -3751,7 +3751,7 @@ void __init kmem_cache_init(void) #endif #ifdef CONFIG_ZONE_DMA - for (i = 0; i < SLUB_PAGE_SHIFT; i++) { + for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) { struct kmem_cache *s = kmalloc_caches[i]; if (s && s->size) { @@ -3930,7 +3930,7 @@ void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, unsigned long caller) struct kmem_cache *s; void *ret; - if (unlikely(size > SLUB_MAX_SIZE)) + if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) return kmalloc_large(size, gfpflags); s = get_slab(size, gfpflags); @@ -3953,7 +3953,7 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t gfpflags, struct kmem_cache *s; void *ret; - if (unlikely(size > SLUB_MAX_SIZE)) { + if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) { ret = kmalloc_large_node(size, gfpflags, node); trace_kmalloc_node(caller, ret, @@ -4312,7 +4312,7 @@ static void resiliency_test(void) { u8 *p; - BUILD_BUG_ON(KMALLOC_MIN_SIZE > 16 || SLUB_PAGE_SHIFT < 10); + BUILD_BUG_ON(KMALLOC_MIN_SIZE > 16 || KMALLOC_SHIFT_HIGH < 10); printk(KERN_ERR "SLUB resiliency testing\n"); printk(KERN_ERR "-----------------------\n"); -- cgit v1.2.3-70-g09d2 From 9425c58e5445277699ff3c2a87bac1cfebc1b48d Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 10 Jan 2013 19:12:17 +0000 Subject: slab: Common definition for the array of kmalloc caches Have a common definition fo the kmalloc cache arrays in SLAB and SLUB Acked-by: Glauber Costa Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 5 +++++ include/linux/slab_def.h | 3 --- include/linux/slub_def.h | 6 ------ mm/slab.c | 8 -------- mm/slab_common.c | 8 ++++++++ mm/slub.c | 7 ------- 6 files changed, 13 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index c0178054005..f2327a898a8 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -206,6 +206,11 @@ struct kmem_cache { #define KMALLOC_MIN_SIZE (1 << KMALLOC_SHIFT_LOW) #endif +extern struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1]; +#ifdef CONFIG_ZONE_DMA +extern struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1]; +#endif + /* * Figure out which kmalloc slab an allocation of a certain size * belongs to. diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h index 4ff50e8d1a2..113ec080313 100644 --- a/include/linux/slab_def.h +++ b/include/linux/slab_def.h @@ -102,9 +102,6 @@ struct kmem_cache { */ }; -extern struct kmem_cache *kmalloc_caches[PAGE_SHIFT + MAX_ORDER]; -extern struct kmem_cache *kmalloc_dma_caches[PAGE_SHIFT + MAX_ORDER]; - void *kmem_cache_alloc(struct kmem_cache *, gfp_t); void *__kmalloc(size_t size, gfp_t flags); diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 032028ef9a3..3701896f7f8 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -122,12 +122,6 @@ struct kmem_cache { #define SLUB_DMA (__force gfp_t)0 #endif -/* - * We keep the general caches in an array of slab caches that are used for - * 2^x bytes of allocations. - */ -extern struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1]; - /* * Find the slab cache for a given combination of allocation flags and size. * diff --git a/mm/slab.c b/mm/slab.c index 3416f4c544b..357f0bdc5e4 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -318,14 +318,6 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int len, static int enable_cpucache(struct kmem_cache *cachep, gfp_t gfp); static void cache_reap(struct work_struct *unused); -struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1]; -EXPORT_SYMBOL(kmalloc_caches); - -#ifdef CONFIG_ZONE_DMA -struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1]; -EXPORT_SYMBOL(kmalloc_dma_caches); -#endif - static int slab_early_init = 1; #define INDEX_AC kmalloc_index(sizeof(struct arraycache_init)) diff --git a/mm/slab_common.c b/mm/slab_common.c index 53adfbf2f3b..0437b8189b8 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -319,6 +319,14 @@ struct kmem_cache *__init create_kmalloc_cache(const char *name, size_t size, return s; } +struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1]; +EXPORT_SYMBOL(kmalloc_caches); + +#ifdef CONFIG_ZONE_DMA +struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1]; +EXPORT_SYMBOL(kmalloc_dma_caches); +#endif + #endif /* !CONFIG_SLOB */ diff --git a/mm/slub.c b/mm/slub.c index d0f72ee0631..527cbfb5c49 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -3174,13 +3174,6 @@ int __kmem_cache_shutdown(struct kmem_cache *s) * Kmalloc subsystem *******************************************************************/ -struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1]; -EXPORT_SYMBOL(kmalloc_caches); - -#ifdef CONFIG_ZONE_DMA -static struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1]; -#endif - static int __init setup_slub_min_order(char *str) { get_option(&str, &slub_min_order); -- cgit v1.2.3-70-g09d2 From 2c59dd6544212faa5ce761920d2251f4152f408d Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 10 Jan 2013 19:14:19 +0000 Subject: slab: Common Kmalloc cache determination Extract the optimized lookup functions from slub and put them into slab_common.c. Then make slab use these functions as well. Joonsoo notes that this fixes some issues with constant folding which also reduces the code size for slub. https://lkml.org/lkml/2012/10/20/82 Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slub_def.h | 41 +++++------------- mm/slab.c | 40 ++---------------- mm/slab.h | 3 ++ mm/slab_common.c | 105 ++++++++++++++++++++++++++++++++++++++++++++- mm/slub.c | 108 +++-------------------------------------------- 5 files changed, 124 insertions(+), 173 deletions(-) (limited to 'include') diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 3701896f7f8..16341e5316d 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -115,29 +115,6 @@ struct kmem_cache { struct kmem_cache_node *node[MAX_NUMNODES]; }; -#ifdef CONFIG_ZONE_DMA -#define SLUB_DMA __GFP_DMA -#else -/* Disable DMA functionality */ -#define SLUB_DMA (__force gfp_t)0 -#endif - -/* - * Find the slab cache for a given combination of allocation flags and size. - * - * This ought to end up with a global pointer to the right cache - * in kmalloc_caches. - */ -static __always_inline struct kmem_cache *kmalloc_slab(size_t size) -{ - int index = kmalloc_index(size); - - if (index == 0) - return NULL; - - return kmalloc_caches[index]; -} - void *kmem_cache_alloc(struct kmem_cache *, gfp_t); void *__kmalloc(size_t size, gfp_t flags); @@ -195,13 +172,14 @@ static __always_inline void *kmalloc(size_t size, gfp_t flags) if (size > KMALLOC_MAX_CACHE_SIZE) return kmalloc_large(size, flags); - if (!(flags & SLUB_DMA)) { - struct kmem_cache *s = kmalloc_slab(size); + if (!(flags & GFP_DMA)) { + int index = kmalloc_index(size); - if (!s) + if (!index) return ZERO_SIZE_PTR; - return kmem_cache_alloc_trace(s, flags, size); + return kmem_cache_alloc_trace(kmalloc_caches[index], + flags, size); } } return __kmalloc(size, flags); @@ -228,13 +206,14 @@ kmem_cache_alloc_node_trace(struct kmem_cache *s, static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node) { if (__builtin_constant_p(size) && - size <= KMALLOC_MAX_CACHE_SIZE && !(flags & SLUB_DMA)) { - struct kmem_cache *s = kmalloc_slab(size); + size <= KMALLOC_MAX_CACHE_SIZE && !(flags & GFP_DMA)) { + int index = kmalloc_index(size); - if (!s) + if (!index) return ZERO_SIZE_PTR; - return kmem_cache_alloc_node_trace(s, flags, node, size); + return kmem_cache_alloc_node_trace(kmalloc_caches[index], + flags, node, size); } return __kmalloc_node(size, flags, node); } diff --git a/mm/slab.c b/mm/slab.c index 08ba44f81a2..62629b11df3 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -656,40 +656,6 @@ static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep) return cachep->array[smp_processor_id()]; } -static inline struct kmem_cache *__find_general_cachep(size_t size, - gfp_t gfpflags) -{ - int i; - -#if DEBUG - /* This happens if someone tries to call - * kmem_cache_create(), or __kmalloc(), before - * the generic caches are initialized. - */ - BUG_ON(kmalloc_caches[INDEX_AC] == NULL); -#endif - if (!size) - return ZERO_SIZE_PTR; - - i = kmalloc_index(size); - - /* - * Really subtle: The last entry with cs->cs_size==ULONG_MAX - * has cs_{dma,}cachep==NULL. Thus no special case - * for large kmalloc calls required. - */ -#ifdef CONFIG_ZONE_DMA - if (unlikely(gfpflags & GFP_DMA)) - return kmalloc_dma_caches[i]; -#endif - return kmalloc_caches[i]; -} - -static struct kmem_cache *kmem_find_general_cachep(size_t size, gfp_t gfpflags) -{ - return __find_general_cachep(size, gfpflags); -} - static size_t slab_mgmt_size(size_t nr_objs, size_t align) { return ALIGN(sizeof(struct slab)+nr_objs*sizeof(kmem_bufctl_t), align); @@ -2426,7 +2392,7 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags) cachep->reciprocal_buffer_size = reciprocal_value(size); if (flags & CFLGS_OFF_SLAB) { - cachep->slabp_cache = kmem_find_general_cachep(slab_size, 0u); + cachep->slabp_cache = kmalloc_slab(slab_size, 0u); /* * This is a possibility for one of the malloc_sizes caches. * But since we go off slab only for object size greater than @@ -3729,7 +3695,7 @@ __do_kmalloc_node(size_t size, gfp_t flags, int node, unsigned long caller) { struct kmem_cache *cachep; - cachep = kmem_find_general_cachep(size, flags); + cachep = kmalloc_slab(size, flags); if (unlikely(ZERO_OR_NULL_PTR(cachep))) return cachep; return kmem_cache_alloc_node_trace(cachep, flags, node, size); @@ -3774,7 +3740,7 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, * Then kmalloc uses the uninlined functions instead of the inline * functions. */ - cachep = __find_general_cachep(size, flags); + cachep = kmalloc_slab(size, flags); if (unlikely(ZERO_OR_NULL_PTR(cachep))) return cachep; ret = slab_alloc(cachep, flags, caller); diff --git a/mm/slab.h b/mm/slab.h index 44c0bd6dc19..c01bc8921ac 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -38,6 +38,9 @@ unsigned long calculate_alignment(unsigned long flags, #ifndef CONFIG_SLOB /* Kmalloc array related functions */ void create_kmalloc_caches(unsigned long); + +/* Find the kmalloc slab corresponding for a certain size */ +struct kmem_cache *kmalloc_slab(size_t, gfp_t); #endif diff --git a/mm/slab_common.c b/mm/slab_common.c index 2b0ebb6d071..6d73f0b7f21 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -327,6 +327,68 @@ struct kmem_cache *kmalloc_dma_caches[KMALLOC_SHIFT_HIGH + 1]; EXPORT_SYMBOL(kmalloc_dma_caches); #endif +/* + * Conversion table for small slabs sizes / 8 to the index in the + * kmalloc array. This is necessary for slabs < 192 since we have non power + * of two cache sizes there. The size of larger slabs can be determined using + * fls. + */ +static s8 size_index[24] = { + 3, /* 8 */ + 4, /* 16 */ + 5, /* 24 */ + 5, /* 32 */ + 6, /* 40 */ + 6, /* 48 */ + 6, /* 56 */ + 6, /* 64 */ + 1, /* 72 */ + 1, /* 80 */ + 1, /* 88 */ + 1, /* 96 */ + 7, /* 104 */ + 7, /* 112 */ + 7, /* 120 */ + 7, /* 128 */ + 2, /* 136 */ + 2, /* 144 */ + 2, /* 152 */ + 2, /* 160 */ + 2, /* 168 */ + 2, /* 176 */ + 2, /* 184 */ + 2 /* 192 */ +}; + +static inline int size_index_elem(size_t bytes) +{ + return (bytes - 1) / 8; +} + +/* + * Find the kmem_cache structure that serves a given size of + * allocation + */ +struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags) +{ + int index; + + if (size <= 192) { + if (!size) + return ZERO_SIZE_PTR; + + index = size_index[size_index_elem(size)]; + } else + index = fls(size - 1); + +#ifdef CONFIG_ZONE_DMA + if (unlikely((flags & SLAB_CACHE_DMA))) + return kmalloc_dma_caches[index]; + +#endif + return kmalloc_caches[index]; +} + /* * Create the kmalloc array. Some of the regular kmalloc arrays * may already have been created because they were needed to @@ -336,6 +398,47 @@ void __init create_kmalloc_caches(unsigned long flags) { int i; + /* + * Patch up the size_index table if we have strange large alignment + * requirements for the kmalloc array. This is only the case for + * MIPS it seems. The standard arches will not generate any code here. + * + * Largest permitted alignment is 256 bytes due to the way we + * handle the index determination for the smaller caches. + * + * Make sure that nothing crazy happens if someone starts tinkering + * around with ARCH_KMALLOC_MINALIGN + */ + BUILD_BUG_ON(KMALLOC_MIN_SIZE > 256 || + (KMALLOC_MIN_SIZE & (KMALLOC_MIN_SIZE - 1))); + + for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) { + int elem = size_index_elem(i); + + if (elem >= ARRAY_SIZE(size_index)) + break; + size_index[elem] = KMALLOC_SHIFT_LOW; + } + + if (KMALLOC_MIN_SIZE >= 64) { + /* + * The 96 byte size cache is not used if the alignment + * is 64 byte. + */ + for (i = 64 + 8; i <= 96; i += 8) + size_index[size_index_elem(i)] = 7; + + } + + if (KMALLOC_MIN_SIZE >= 128) { + /* + * The 192 byte sized cache is not used if the alignment + * is 128 byte. Redirect kmalloc to use the 256 byte cache + * instead. + */ + for (i = 128 + 8; i <= 192; i += 8) + size_index[size_index_elem(i)] = 8; + } /* Caches that are not of the two-to-the-power-of size */ if (KMALLOC_MIN_SIZE <= 32 && !kmalloc_caches[1]) kmalloc_caches[1] = create_kmalloc_cache(NULL, 96, flags); @@ -379,8 +482,6 @@ void __init create_kmalloc_caches(unsigned long flags) } #endif } - - #endif /* !CONFIG_SLOB */ diff --git a/mm/slub.c b/mm/slub.c index e813c2d30fe..6184b0821f7 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2982,7 +2982,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order) s->allocflags |= __GFP_COMP; if (s->flags & SLAB_CACHE_DMA) - s->allocflags |= SLUB_DMA; + s->allocflags |= GFP_DMA; if (s->flags & SLAB_RECLAIM_ACCOUNT) s->allocflags |= __GFP_RECLAIMABLE; @@ -3210,64 +3210,6 @@ static int __init setup_slub_nomerge(char *str) __setup("slub_nomerge", setup_slub_nomerge); -/* - * Conversion table for small slabs sizes / 8 to the index in the - * kmalloc array. This is necessary for slabs < 192 since we have non power - * of two cache sizes there. The size of larger slabs can be determined using - * fls. - */ -static s8 size_index[24] = { - 3, /* 8 */ - 4, /* 16 */ - 5, /* 24 */ - 5, /* 32 */ - 6, /* 40 */ - 6, /* 48 */ - 6, /* 56 */ - 6, /* 64 */ - 1, /* 72 */ - 1, /* 80 */ - 1, /* 88 */ - 1, /* 96 */ - 7, /* 104 */ - 7, /* 112 */ - 7, /* 120 */ - 7, /* 128 */ - 2, /* 136 */ - 2, /* 144 */ - 2, /* 152 */ - 2, /* 160 */ - 2, /* 168 */ - 2, /* 176 */ - 2, /* 184 */ - 2 /* 192 */ -}; - -static inline int size_index_elem(size_t bytes) -{ - return (bytes - 1) / 8; -} - -static struct kmem_cache *get_slab(size_t size, gfp_t flags) -{ - int index; - - if (size <= 192) { - if (!size) - return ZERO_SIZE_PTR; - - index = size_index[size_index_elem(size)]; - } else - index = fls(size - 1); - -#ifdef CONFIG_ZONE_DMA - if (unlikely((flags & SLUB_DMA))) - return kmalloc_dma_caches[index]; - -#endif - return kmalloc_caches[index]; -} - void *__kmalloc(size_t size, gfp_t flags) { struct kmem_cache *s; @@ -3276,7 +3218,7 @@ void *__kmalloc(size_t size, gfp_t flags) if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) return kmalloc_large(size, flags); - s = get_slab(size, flags); + s = kmalloc_slab(size, flags); if (unlikely(ZERO_OR_NULL_PTR(s))) return s; @@ -3319,7 +3261,7 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node) return ret; } - s = get_slab(size, flags); + s = kmalloc_slab(size, flags); if (unlikely(ZERO_OR_NULL_PTR(s))) return s; @@ -3632,7 +3574,6 @@ void __init kmem_cache_init(void) { static __initdata struct kmem_cache boot_kmem_cache, boot_kmem_cache_node; - int i; if (debug_guardpage_minorder()) slub_max_order = 0; @@ -3663,45 +3604,6 @@ void __init kmem_cache_init(void) kmem_cache_node = bootstrap(&boot_kmem_cache_node); /* Now we can use the kmem_cache to allocate kmalloc slabs */ - - /* - * Patch up the size_index table if we have strange large alignment - * requirements for the kmalloc array. This is only the case for - * MIPS it seems. The standard arches will not generate any code here. - * - * Largest permitted alignment is 256 bytes due to the way we - * handle the index determination for the smaller caches. - * - * Make sure that nothing crazy happens if someone starts tinkering - * around with ARCH_KMALLOC_MINALIGN - */ - BUILD_BUG_ON(KMALLOC_MIN_SIZE > 256 || - (KMALLOC_MIN_SIZE & (KMALLOC_MIN_SIZE - 1))); - - for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) { - int elem = size_index_elem(i); - if (elem >= ARRAY_SIZE(size_index)) - break; - size_index[elem] = KMALLOC_SHIFT_LOW; - } - - if (KMALLOC_MIN_SIZE == 64) { - /* - * The 96 byte size cache is not used if the alignment - * is 64 byte. - */ - for (i = 64 + 8; i <= 96; i += 8) - size_index[size_index_elem(i)] = 7; - } else if (KMALLOC_MIN_SIZE == 128) { - /* - * The 192 byte sized cache is not used if the alignment - * is 128 byte. Redirect kmalloc to use the 256 byte cache - * instead. - */ - for (i = 128 + 8; i <= 192; i += 8) - size_index[size_index_elem(i)] = 8; - } - create_kmalloc_caches(0); #ifdef CONFIG_SMP @@ -3877,7 +3779,7 @@ void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, unsigned long caller) if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) return kmalloc_large(size, gfpflags); - s = get_slab(size, gfpflags); + s = kmalloc_slab(size, gfpflags); if (unlikely(ZERO_OR_NULL_PTR(s))) return s; @@ -3907,7 +3809,7 @@ void *__kmalloc_node_track_caller(size_t size, gfp_t gfpflags, return ret; } - s = get_slab(size, gfpflags); + s = kmalloc_slab(size, gfpflags); if (unlikely(ZERO_OR_NULL_PTR(s))) return s; -- cgit v1.2.3-70-g09d2 From ca34956b804b7554fc4e88826773380d9d5122a8 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 10 Jan 2013 19:14:19 +0000 Subject: slab: Common definition for kmem_cache_node Put the definitions for the kmem_cache_node structures together so that we have one structure. That will allow us to create more common fields in the future which could yield more opportunities to share code. Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slub_def.h | 11 ----------- mm/slab.c | 17 ----------------- mm/slab.h | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h index 16341e5316d..027276fa871 100644 --- a/include/linux/slub_def.h +++ b/include/linux/slub_def.h @@ -53,17 +53,6 @@ struct kmem_cache_cpu { #endif }; -struct kmem_cache_node { - spinlock_t list_lock; /* Protect partial list and nr_partial */ - unsigned long nr_partial; - struct list_head partial; -#ifdef CONFIG_SLUB_DEBUG - atomic_long_t nr_slabs; - atomic_long_t total_objects; - struct list_head full; -#endif -}; - /* * Word size structure that can be atomically updated or read and that * contains both the order and the number of objects that a slab of the diff --git a/mm/slab.c b/mm/slab.c index c162b2eb493..17f85961454 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -285,23 +285,6 @@ struct arraycache_init { void *entries[BOOT_CPUCACHE_ENTRIES]; }; -/* - * The slab lists for all objects. - */ -struct kmem_cache_node { - struct list_head slabs_partial; /* partial list first, better asm code */ - struct list_head slabs_full; - struct list_head slabs_free; - unsigned long free_objects; - unsigned int free_limit; - unsigned int colour_next; /* Per-node cache coloring */ - spinlock_t list_lock; - struct array_cache *shared; /* shared per node */ - struct array_cache **alien; /* on other nodes */ - unsigned long next_reap; /* updated without locking */ - int free_touched; /* updated without locking */ -}; - /* * Need this for bootstrapping a per node allocator. */ diff --git a/mm/slab.h b/mm/slab.h index f0a552ff7b9..f96b49e4704 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -239,3 +239,35 @@ static inline struct kmem_cache *cache_from_obj(struct kmem_cache *s, void *x) return s; } #endif + + +/* + * The slab lists for all objects. + */ +struct kmem_cache_node { + spinlock_t list_lock; + +#ifdef CONFIG_SLAB + struct list_head slabs_partial; /* partial list first, better asm code */ + struct list_head slabs_full; + struct list_head slabs_free; + unsigned long free_objects; + unsigned int free_limit; + unsigned int colour_next; /* Per-node cache coloring */ + struct array_cache *shared; /* shared per node */ + struct array_cache **alien; /* on other nodes */ + unsigned long next_reap; /* updated without locking */ + int free_touched; /* updated without locking */ +#endif + +#ifdef CONFIG_SLUB + unsigned long nr_partial; + struct list_head partial; +#ifdef CONFIG_SLUB_DEBUG + atomic_long_t nr_slabs; + atomic_long_t total_objects; + struct list_head full; +#endif +#endif + +}; -- cgit v1.2.3-70-g09d2 From c601fd6956e92b0eb268d4af754073c76155b99d Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Tue, 5 Feb 2013 16:36:47 +0000 Subject: slab: Handle ARCH_DMA_MINALIGN correctly James Hogan hit boot problems in next-20130204 on Meta: META213-Thread0 DSP [LogF] kobject (4fc03980): tried to init an initialized object, something is seriously wrong. META213-Thread0 DSP [LogF] META213-Thread0 DSP [LogF] Call trace: META213-Thread0 DSP [LogF] [<4000888c>] _show_stack+0x68/0x7c META213-Thread0 DSP [LogF] [<400088b4>] _dump_stack+0x14/0x28 META213-Thread0 DSP [LogF] [<40103794>] _kobject_init+0x58/0x9c META213-Thread0 DSP [LogF] [<40103810>] _kobject_create+0x38/0x64 META213-Thread0 DSP [LogF] [<40103eac>] _kobject_create_and_add+0x14/0x8c META213-Thread0 DSP [LogF] [<40190ac4>] _mnt_init+0xd8/0x220 META213-Thread0 DSP [LogF] [<40190508>] _vfs_caches_init+0xb0/0x160 META213-Thread0 DSP [LogF] [<401851f4>] _start_kernel+0x274/0x340 META213-Thread0 DSP [LogF] [<40188424>] _metag_start_kernel+0x58/0x6c META213-Thread0 DSP [LogF] [<40000044>] __start+0x44/0x48 META213-Thread0 DSP [LogF] META213-Thread0 DSP [LogF] devtmpfs: initialized META213-Thread0 DSP [LogF] L2 Cache: Not present META213-Thread0 DSP [LogF] BUG: failure at fs/sysfs/dir.c:736/sysfs_read_ns_type()! META213-Thread0 DSP [LogF] Kernel panic - not syncing: BUG! META213-Thread0 DSP [Thread Exit] Thread has exited - return code = 4294967295 And bisected the problem to commit 95a05b4 ("slab: Common constants for kmalloc boundaries"). As it turns out, a fixed KMALLOC_SHIFT_LOW does not work for arches with higher alignment requirements. Determine KMALLOC_SHIFT_LOW from ARCH_DMA_MINALIGN instead. Reported-and-tested-by: James Hogan Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/slab.h b/include/linux/slab.h index f2327a898a8..0c621752caa 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -133,6 +133,19 @@ void kfree(const void *); void kzfree(const void *); size_t ksize(const void *); +/* + * Some archs want to perform DMA into kmalloc caches and need a guaranteed + * alignment larger than the alignment of a 64-bit integer. + * Setting ARCH_KMALLOC_MINALIGN in arch headers allows that. + */ +#if defined(ARCH_DMA_MINALIGN) && ARCH_DMA_MINALIGN > 8 +#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN +#define KMALLOC_MIN_SIZE ARCH_DMA_MINALIGN +#define KMALLOC_SHIFT_LOW ilog2(ARCH_DMA_MINALIGN) +#else +#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long) +#endif + #ifdef CONFIG_SLOB /* * Common fields provided in kmem_cache by all slab allocators @@ -179,7 +192,9 @@ struct kmem_cache { #define KMALLOC_SHIFT_HIGH ((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? \ (MAX_ORDER + PAGE_SHIFT - 1) : 25) #define KMALLOC_SHIFT_MAX KMALLOC_SHIFT_HIGH +#ifndef KMALLOC_SHIFT_LOW #define KMALLOC_SHIFT_LOW 5 +#endif #else /* * SLUB allocates up to order 2 pages directly and otherwise @@ -187,8 +202,10 @@ struct kmem_cache { */ #define KMALLOC_SHIFT_HIGH (PAGE_SHIFT + 1) #define KMALLOC_SHIFT_MAX (MAX_ORDER + PAGE_SHIFT) +#ifndef KMALLOC_SHIFT_LOW #define KMALLOC_SHIFT_LOW 3 #endif +#endif /* Maximum allocatable size */ #define KMALLOC_MAX_SIZE (1UL << KMALLOC_SHIFT_MAX) @@ -200,9 +217,7 @@ struct kmem_cache { /* * Kmalloc subsystem. */ -#if defined(ARCH_DMA_MINALIGN) && ARCH_DMA_MINALIGN > 8 -#define KMALLOC_MIN_SIZE ARCH_DMA_MINALIGN -#else +#ifndef KMALLOC_MIN_SIZE #define KMALLOC_MIN_SIZE (1 << KMALLOC_SHIFT_LOW) #endif @@ -289,17 +304,6 @@ static __always_inline int kmalloc_size(int n) } #endif /* !CONFIG_SLOB */ -/* - * Some archs want to perform DMA into kmalloc caches and need a guaranteed - * alignment larger than the alignment of a 64-bit integer. - * Setting ARCH_KMALLOC_MINALIGN in arch headers allows that. - */ -#ifdef ARCH_DMA_MINALIGN -#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN -#else -#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long) -#endif - /* * Setting ARCH_SLAB_MINALIGN in arch headers allows a different alignment. * Intended for arches that get misalignment faults even for 64 bit integer -- cgit v1.2.3-70-g09d2 From 4490108b4a5ada14c7be712260829faecc814ae5 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 15 Feb 2013 17:29:22 -0800 Subject: openvswitch: Allow OVS_USERSPACE_ATTR_USERDATA to be variable length. Until now, the optional OVS_USERSPACE_ATTR_USERDATA attribute had to be exactly 64 bits long, if it was present. However, 64 bits is not enough space to associate as much information with a flow as would be convenient for some userspace features now under development. This commit generalizes the attribute, allowing it to be any length. This generalization is backward-compatible: if userspace only uses 64-bit attributes, then it will not see any change in behavior. CC: Romain Lenglet Signed-off-by: Ben Pfaff Signed-off-by: Jesse Gross --- include/linux/openvswitch.h | 11 ++++++----- net/openvswitch/datapath.c | 11 ++++++----- net/openvswitch/datapath.h | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h index 99e6414a40d..67d6c7b0358 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -127,7 +127,8 @@ enum ovs_packet_cmd { * for %OVS_PACKET_CMD_EXECUTE. It has nested %OVS_ACTION_ATTR_* attributes. * @OVS_PACKET_ATTR_USERDATA: Present for an %OVS_PACKET_CMD_ACTION * notification if the %OVS_ACTION_ATTR_USERSPACE action specified an - * %OVS_USERSPACE_ATTR_USERDATA attribute. + * %OVS_USERSPACE_ATTR_USERDATA attribute, with the same length and content + * specified there. * * These attributes follow the &struct ovs_header within the Generic Netlink * payload for %OVS_PACKET_* commands. @@ -137,7 +138,7 @@ enum ovs_packet_attr { OVS_PACKET_ATTR_PACKET, /* Packet data. */ OVS_PACKET_ATTR_KEY, /* Nested OVS_KEY_ATTR_* attributes. */ OVS_PACKET_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */ - OVS_PACKET_ATTR_USERDATA, /* u64 OVS_ACTION_ATTR_USERSPACE arg. */ + OVS_PACKET_ATTR_USERDATA, /* OVS_ACTION_ATTR_USERSPACE arg. */ __OVS_PACKET_ATTR_MAX }; @@ -389,13 +390,13 @@ enum ovs_sample_attr { * enum ovs_userspace_attr - Attributes for %OVS_ACTION_ATTR_USERSPACE action. * @OVS_USERSPACE_ATTR_PID: u32 Netlink PID to which the %OVS_PACKET_CMD_ACTION * message should be sent. Required. - * @OVS_USERSPACE_ATTR_USERDATA: If present, its u64 argument is copied to the - * %OVS_PACKET_CMD_ACTION message as %OVS_PACKET_ATTR_USERDATA, + * @OVS_USERSPACE_ATTR_USERDATA: If present, its variable-length argument is + * copied to the %OVS_PACKET_CMD_ACTION message as %OVS_PACKET_ATTR_USERDATA. */ enum ovs_userspace_attr { OVS_USERSPACE_ATTR_UNSPEC, OVS_USERSPACE_ATTR_PID, /* u32 Netlink PID to receive upcalls. */ - OVS_USERSPACE_ATTR_USERDATA, /* u64 optional user-specified cookie. */ + OVS_USERSPACE_ATTR_USERDATA, /* Optional user-specified cookie. */ __OVS_USERSPACE_ATTR_MAX }; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index f9d2438e643..96cd5b243d5 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -370,8 +370,8 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex, len = sizeof(struct ovs_header); len += nla_total_size(skb->len); len += nla_total_size(FLOW_BUFSIZE); - if (upcall_info->cmd == OVS_PACKET_CMD_ACTION) - len += nla_total_size(8); + if (upcall_info->userdata) + len += NLA_ALIGN(upcall_info->userdata->nla_len); user_skb = genlmsg_new(len, GFP_ATOMIC); if (!user_skb) { @@ -388,8 +388,9 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex, nla_nest_end(user_skb, nla); if (upcall_info->userdata) - nla_put_u64(user_skb, OVS_PACKET_ATTR_USERDATA, - nla_get_u64(upcall_info->userdata)); + __nla_put(user_skb, OVS_PACKET_ATTR_USERDATA, + nla_len(upcall_info->userdata), + nla_data(upcall_info->userdata)); nla = __nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, skb->len); @@ -544,7 +545,7 @@ static int validate_userspace(const struct nlattr *attr) { static const struct nla_policy userspace_policy[OVS_USERSPACE_ATTR_MAX + 1] = { [OVS_USERSPACE_ATTR_PID] = {.type = NLA_U32 }, - [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_U64 }, + [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_UNSPEC }, }; struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1]; int error; diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 031dfbf37c9..9125ad5c5ae 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -119,7 +119,7 @@ struct ovs_skb_cb { * struct dp_upcall - metadata to include with a packet to send to userspace * @cmd: One of %OVS_PACKET_CMD_*. * @key: Becomes %OVS_PACKET_ATTR_KEY. Must be nonnull. - * @userdata: If nonnull, its u64 value is extracted and passed to userspace as + * @userdata: If nonnull, its variable-length value is passed to userspace as * %OVS_PACKET_ATTR_USERDATA. * @pid: Netlink PID to which packet should be sent. If @pid is 0 then no * packet is sent and the packet is accounted in the datapath's @n_lost -- cgit v1.2.3-70-g09d2 From 27cef8b47cfb27fa2955a8577637794f1f275db2 Mon Sep 17 00:00:00 2001 From: Heiko Stübner Date: Sat, 23 Feb 2013 12:06:44 -0800 Subject: Input: auo-pixcir-ts - handle reset gpio directly Devicetree based platforms don't handle device callbacks very well and until now no board has come along that needs more extended hwinit than pulling the rst gpio high. Therefore pull the reset handling directly into the driver and remove the callbacks from the driver. If extended device setup is needed at some later point, power-sequences would probably be the solution of choice. Signed-off-by: Heiko Stuebner Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/auo-pixcir-ts.c | 26 ++++++++++++++++++++------ include/linux/input/auo-pixcir-ts.h | 4 +--- 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index 813413eebab..6317a9c7884 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -511,8 +511,21 @@ static int auo_pixcir_probe(struct i2c_client *client, goto err_gpio_dir; } - if (pdata->init_hw) - pdata->init_hw(client); + ret = gpio_request(pdata->gpio_rst, "auo_pixcir_ts_rst"); + if (ret) { + dev_err(&client->dev, "request of gpio %d failed, %d\n", + pdata->gpio_rst, ret); + goto err_gpio_dir; + } + + ret = gpio_direction_output(pdata->gpio_rst, 1); + if (ret) { + dev_err(&client->dev, "setting direction of gpio %d failed %d\n", + pdata->gpio_rst, ret); + goto err_gpio_rst; + } + + msleep(200); ts->client = client; ts->touch_ind_mode = 0; @@ -597,8 +610,9 @@ err_input_register: err_fw_vers: input_free_device(input_dev); err_input_alloc: - if (pdata->exit_hw) - pdata->exit_hw(client); + gpio_set_value(pdata->gpio_rst, 0); +err_gpio_rst: + gpio_free(pdata->gpio_rst); err_gpio_dir: gpio_free(pdata->gpio_int); err_gpio_int: @@ -616,8 +630,8 @@ static int auo_pixcir_remove(struct i2c_client *client) input_unregister_device(ts->input); - if (pdata->exit_hw) - pdata->exit_hw(client); + gpio_set_value(pdata->gpio_rst, 0); + gpio_free(pdata->gpio_rst); gpio_free(pdata->gpio_int); diff --git a/include/linux/input/auo-pixcir-ts.h b/include/linux/input/auo-pixcir-ts.h index 75d4be71771..5049f21928e 100644 --- a/include/linux/input/auo-pixcir-ts.h +++ b/include/linux/input/auo-pixcir-ts.h @@ -43,12 +43,10 @@ */ struct auo_pixcir_ts_platdata { int gpio_int; + int gpio_rst; int int_setting; - void (*init_hw)(struct i2c_client *); - void (*exit_hw)(struct i2c_client *); - unsigned int x_max; unsigned int y_max; }; -- cgit v1.2.3-70-g09d2 From e90a6df80dc45ab53d2f4f4db297434e48c0208e Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 25 Feb 2013 11:31:43 +0100 Subject: HID: Extend the interface with report requests Some drivers send reports directly to underlying device, creating an unwanted dependency on the underlying transport layer. This patch adds hid_hw_request() to the interface, thereby removing usbhid from the lion share of the drivers. Signed-off-by: Henrik Rydberg Signed-off-by: Benjamin Tissoires Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 13 +++++++++++++ include/linux/hid.h | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 8e0c4bf94eb..366fd09d257 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1243,6 +1243,18 @@ static int usbhid_power(struct hid_device *hid, int lvl) return r; } +static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + usbhid_submit_report(hid, rep, USB_DIR_IN); + break; + case HID_REQ_SET_REPORT: + usbhid_submit_report(hid, rep, USB_DIR_OUT); + break; + } +} + static struct hid_ll_driver usb_hid_driver = { .parse = usbhid_parse, .start = usbhid_start, @@ -1251,6 +1263,7 @@ static struct hid_ll_driver usb_hid_driver = { .close = usbhid_close, .power = usbhid_power, .hidinput_input_event = usb_hidinput_input_event, + .request = usbhid_request, }; static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/include/linux/hid.h b/include/linux/hid.h index e14b465b114..261c713d484 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -662,6 +662,7 @@ struct hid_driver { * @hidinput_input_event: event input event (e.g. ff or leds) * @parse: this method is called only once to parse the device data, * shouldn't allocate anything to not leak memory + * @request: send report request to device (e.g. feature report) */ struct hid_ll_driver { int (*start)(struct hid_device *hdev); @@ -676,6 +677,10 @@ struct hid_ll_driver { unsigned int code, int value); int (*parse)(struct hid_device *hdev); + + void (*request)(struct hid_device *hdev, + struct hid_report *report, int reqtype); + }; #define PM_HINT_FULLON 1<<5 @@ -883,6 +888,21 @@ static inline int hid_hw_power(struct hid_device *hdev, int level) return hdev->ll_driver->power ? hdev->ll_driver->power(hdev, level) : 0; } + +/** + * hid_hw_request - send report request to device + * + * @hdev: hid device + * @report: report to send + * @reqtype: hid request type + */ +static inline void hid_hw_request(struct hid_device *hdev, + struct hid_report *report, int reqtype) +{ + if (hdev->ll_driver->request) + hdev->ll_driver->request(hdev, report, reqtype); +} + int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int interrupt); -- cgit v1.2.3-70-g09d2 From 3373443befa73ee60e4275e7699b26058b01455a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Mon, 25 Feb 2013 11:31:44 +0100 Subject: HID: Extend the interface with wait io request Some drivers need to wait for an io from the underlying device, creating an unwanted dependency on the underlying transport layer. This patch adds wait() to the interface, thereby removing usbhid from the lion share of the drivers. Signed-off-by: Henrik Rydberg Signed-off-by: Benjamin Tissoires Reviewed-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 1 + include/linux/hid.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 366fd09d257..99d95d3368b 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1264,6 +1264,7 @@ static struct hid_ll_driver usb_hid_driver = { .power = usbhid_power, .hidinput_input_event = usb_hidinput_input_event, .request = usbhid_request, + .wait = usbhid_wait_io, }; static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/include/linux/hid.h b/include/linux/hid.h index 261c713d484..7071eb3d36c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -663,6 +663,7 @@ struct hid_driver { * @parse: this method is called only once to parse the device data, * shouldn't allocate anything to not leak memory * @request: send report request to device (e.g. feature report) + * @wait: wait for buffered io to complete (send/recv reports) */ struct hid_ll_driver { int (*start)(struct hid_device *hdev); @@ -681,6 +682,8 @@ struct hid_ll_driver { void (*request)(struct hid_device *hdev, struct hid_report *report, int reqtype); + int (*wait)(struct hid_device *hdev); + }; #define PM_HINT_FULLON 1<<5 @@ -903,6 +906,17 @@ static inline void hid_hw_request(struct hid_device *hdev, hdev->ll_driver->request(hdev, report, reqtype); } +/** + * hid_hw_wait - wait for buffered io to complete + * + * @hdev: hid device + */ +static inline void hid_hw_wait(struct hid_device *hdev) +{ + if (hdev->ll_driver->wait) + hdev->ll_driver->wait(hdev); +} + int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int interrupt); -- cgit v1.2.3-70-g09d2 From 8af294b472067e9034fe288d912455cc0961d1b9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 22 Feb 2013 17:48:15 +0000 Subject: ASoC: dapm: Fix handling of loops Currently if a path loops back on itself we correctly skip over it to avoid going into an infinite loop but this causes us to ignore the need to power up the path as we don't count the loop for the purposes of counting inputs and outputs. This means that internal loopbacks within a device that have powered devices on them won't be powered up. Fix this by treating any path that is currently in the process of being recursed as having a single input or output so that it is counted for the purposes of power decisions. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 1 + sound/soc/soc-dapm.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e1ef63d4a5c..44a30b10868 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -488,6 +488,7 @@ struct snd_soc_dapm_path { /* status */ u32 connect:1; /* source and sink widgets are connected */ u32 walked:1; /* path has been walked */ + u32 walking:1; /* path is in the process of being walked */ u32 weak:1; /* path ignored for power management */ int (*connected)(struct snd_soc_dapm_widget *source, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 258acadb9e7..f3255517de7 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -821,6 +821,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, (widget->id == snd_soc_dapm_line && !list_empty(&widget->sources))) { widget->outputs = snd_soc_dapm_suspend_check(widget); + path->walking = 0; return widget->outputs; } } @@ -831,6 +832,9 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, if (path->weak) continue; + if (path->walking) + return 1; + if (path->walked) continue; @@ -838,6 +842,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, if (path->sink && path->connect) { path->walked = 1; + path->walking = 1; /* do we need to add this widget to the list ? */ if (list) { @@ -847,11 +852,14 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, dev_err(widget->dapm->dev, "ASoC: could not add widget %s\n", widget->name); + path->walking = 0; return con; } } con += is_connected_output_ep(path->sink, list); + + path->walking = 0; } } @@ -931,6 +939,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, if (path->weak) continue; + if (path->walking) + return 1; + if (path->walked) continue; @@ -938,6 +949,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, if (path->source && path->connect) { path->walked = 1; + path->walking = 1; /* do we need to add this widget to the list ? */ if (list) { @@ -947,11 +959,14 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, dev_err(widget->dapm->dev, "ASoC: could not add widget %s\n", widget->name); + path->walking = 0; return con; } } con += is_connected_input_ep(path->source, list); + + path->walking = 0; } } -- cgit v1.2.3-70-g09d2 From d6b0c58048d2c8c6f4955c37f670125b2792cd14 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 23 Feb 2013 13:11:14 -0800 Subject: devres: allow adding custom actions to the stack Sometimes drivers need to execute one-off actions in their error handling or device teardown paths. An example would be toggling a GPIO line to reset the controlled device into predefined state. To allow performing such actions when using managed resources let's allow adding them to stack/group of devres resources. Acked-by: Tejun Heo Acked-by: Greg Kroah-Hartman Signed-off-by: Dmitry Torokhov --- drivers/base/devres.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 4 +++ 2 files changed, 78 insertions(+) (limited to 'include') diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 8731979d668..724957a13d4 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -670,6 +670,80 @@ int devres_release_group(struct device *dev, void *id) } EXPORT_SYMBOL_GPL(devres_release_group); +/* + * Custom devres actions allow inserting a simple function call + * into the teadown sequence. + */ + +struct action_devres { + void *data; + void (*action)(void *); +}; + +static int devm_action_match(struct device *dev, void *res, void *p) +{ + struct action_devres *devres = res; + struct action_devres *target = p; + + return devres->action == target->action && + devres->data == target->data; +} + +static void devm_action_release(struct device *dev, void *res) +{ + struct action_devres *devres = res; + + devres->action(devres->data); +} + +/** + * devm_add_action() - add a custom action to list of managed resources + * @dev: Device that owns the action + * @action: Function that should be called + * @data: Pointer to data passed to @action implementation + * + * This adds a custom action to the list of managed resources so that + * it gets executed as part of standard resource unwinding. + */ +int devm_add_action(struct device *dev, void (*action)(void *), void *data) +{ + struct action_devres *devres; + + devres = devres_alloc(devm_action_release, + sizeof(struct action_devres), GFP_KERNEL); + if (!devres) + return -ENOMEM; + + devres->data = data; + devres->action = action; + + devres_add(dev, devres); + return 0; +} +EXPORT_SYMBOL_GPL(devm_add_action); + +/** + * devm_remove_action() - removes previously added custom action + * @dev: Device that owns the action + * @action: Function implementing the action + * @data: Pointer to data passed to @action implementation + * + * Removes instance of @action previously added by devm_add_action(). + * Both action and data should match one of the existing entries. + */ +void devm_remove_action(struct device *dev, void (*action)(void *), void *data) +{ + struct action_devres devres = { + .data = data, + .action = action, + }; + + WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match, + &devres)); + +} +EXPORT_SYMBOL_GPL(devm_remove_action); + /* * Managed kzalloc/kfree */ diff --git a/include/linux/device.h b/include/linux/device.h index 86ef6ab553b..854b247bf5f 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -567,6 +567,10 @@ extern void devm_kfree(struct device *dev, void *p); void __iomem *devm_request_and_ioremap(struct device *dev, struct resource *res); +/* allows to add/remove a custom action to devres stack */ +int devm_add_action(struct device *dev, void (*action)(void *), void *data); +void devm_remove_action(struct device *dev, void (*action)(void *), void *data); + struct device_dma_parameters { /* * a low level driver may set these to teach IOMMU code about -- cgit v1.2.3-70-g09d2 From c849a6143bec520aff2a6646518b0d041402428b Mon Sep 17 00:00:00 2001 From: Andrew de los Reyes Date: Mon, 18 Feb 2013 09:20:21 -0800 Subject: HID: Separate struct hid_device's driver_lock into two locks. This patch separates struct hid_device's driver_lock into two. The goal is to allow hid device drivers to receive input during their probe() or remove() function calls. This is necessary because some drivers need to communicate with the device to determine parameters needed during probe (e.g., size of a multi-touch surface), and if possible, may perfer to communicate with a device on host-initiated disconnect (e.g., to put it into a low-power state). Historically, three functions used driver_lock: - hid_device_probe: blocks to acquire lock - hid_device_remove: blocks to acquire lock - hid_input_report: if locked returns -EBUSY, else acquires lock This patch adds another lock (driver_input_lock) which is used to block input from occurring. The lock behavior is now: - hid_device_probe: blocks to acq. driver_lock, then driver_input_lock - hid_device_remove: blocks to acq. driver_lock, then driver_input_lock - hid_input_report: if driver_input_lock locked returns -EBUSY, else acquires driver_input_lock This patch also adds two helper functions to be called during probe() or remove(): hid_device_io_start() and hid_device_io_stop(). These functions lock and unlock, respectively, driver_input_lock; they also make a note of whether they did so that hid-core knows if a driver has changed the lock state. This patch results in no behavior change for existing devices and drivers. However, during a probe() or remove() function call in a driver, that driver may now selectively call hid_device_io_start() to let input events come through, then optionally call hid_device_io_stop() to stop them. Signed-off-by: Andrew de los Reyes Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 24 +++++++++++++++++++++--- include/linux/hid.h | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ff75cabf739..680068c0c46 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1267,7 +1267,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i if (!hid) return -ENODEV; - if (down_trylock(&hid->driver_lock)) + if (down_trylock(&hid->driver_input_lock)) return -EBUSY; if (!hid->driver) { @@ -1324,7 +1324,7 @@ nomem: ret = hid_report_raw_event(hid, type, data, size, interrupt); unlock: - up(&hid->driver_lock); + up(&hid->driver_input_lock); return ret; } EXPORT_SYMBOL_GPL(hid_input_report); @@ -1845,6 +1845,11 @@ static int hid_device_probe(struct device *dev) if (down_interruptible(&hdev->driver_lock)) return -EINTR; + if (down_interruptible(&hdev->driver_input_lock)) { + ret = -EINTR; + goto unlock_driver_lock; + } + hdev->io_started = false; if (!hdev->driver) { id = hid_match_device(hdev, hdrv); @@ -1867,6 +1872,9 @@ static int hid_device_probe(struct device *dev) } } unlock: + if (!hdev->io_started) + up(&hdev->driver_input_lock); +unlock_driver_lock: up(&hdev->driver_lock); return ret; } @@ -1875,9 +1883,15 @@ static int hid_device_remove(struct device *dev) { struct hid_device *hdev = container_of(dev, struct hid_device, dev); struct hid_driver *hdrv; + int ret = 0; if (down_interruptible(&hdev->driver_lock)) return -EINTR; + if (down_interruptible(&hdev->driver_input_lock)) { + ret = -EINTR; + goto unlock_driver_lock; + } + hdev->io_started = false; hdrv = hdev->driver; if (hdrv) { @@ -1889,8 +1903,11 @@ static int hid_device_remove(struct device *dev) hdev->driver = NULL; } + if (!hdev->io_started) + up(&hdev->driver_input_lock); +unlock_driver_lock: up(&hdev->driver_lock); - return 0; + return ret; } static ssize_t modalias_show(struct device *dev, struct device_attribute *a, @@ -2329,6 +2346,7 @@ struct hid_device *hid_allocate_device(void) init_waitqueue_head(&hdev->debug_wait); INIT_LIST_HEAD(&hdev->debug_list); sema_init(&hdev->driver_lock, 1); + sema_init(&hdev->driver_input_lock, 1); return hdev; } diff --git a/include/linux/hid.h b/include/linux/hid.h index e14b465b114..895b85639de 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -456,7 +456,8 @@ struct hid_device { /* device report descriptor */ unsigned country; /* HID country */ struct hid_report_enum report_enum[HID_REPORT_TYPES]; - struct semaphore driver_lock; /* protects the current driver */ + struct semaphore driver_lock; /* protects the current driver, except during input */ + struct semaphore driver_input_lock; /* protects the current driver */ struct device dev; /* device */ struct hid_driver *driver; struct hid_ll_driver *ll_driver; @@ -477,6 +478,7 @@ struct hid_device { /* device report descriptor */ unsigned int status; /* see STAT flags above */ unsigned claimed; /* Claimed by hidinput, hiddev? */ unsigned quirks; /* Various quirks the device can pull on us */ + bool io_started; /* Protected by driver_lock. If IO has started */ struct list_head inputs; /* The list of inputs */ void *hiddev; /* The hiddev structure */ @@ -599,6 +601,10 @@ struct hid_usage_id { * @resume: invoked on resume if device was not reset (NULL means nop) * @reset_resume: invoked on resume if device was reset (NULL means nop) * + * probe should return -errno on error, or 0 on success. During probe, + * input will not be passed to raw_event unless hid_device_io_start is + * called. + * * raw_event and event should return 0 on no action performed, 1 when no * further processing should be done and negative on error * @@ -737,6 +743,44 @@ const struct hid_device_id *hid_match_id(struct hid_device *hdev, const struct hid_device_id *id); s32 hid_snto32(__u32 value, unsigned n); +/** + * hid_device_io_start - enable HID input during probe, remove + * + * @hid - the device + * + * This should only be called during probe or remove and only be + * called by the thread calling probe or remove. It will allow + * incoming packets to be delivered to the driver. + */ +static inline void hid_device_io_start(struct hid_device *hid) { + if (hid->io_started) { + dev_warn(&hid->dev, "io already started"); + return; + } + hid->io_started = true; + up(&hid->driver_input_lock); +} + +/** + * hid_device_io_stop - disable HID input during probe, remove + * + * @hid - the device + * + * Should only be called after hid_device_io_start. It will prevent + * incoming packets from going to the driver for the duration of + * probe, remove. If called during probe, packets will still go to the + * driver after probe is complete. This function should only be called + * by the thread calling probe or remove. + */ +static inline void hid_device_io_stop(struct hid_device *hid) { + if (!hid->io_started) { + dev_warn(&hid->dev, "io already stopped"); + return; + } + hid->io_started = false; + down(&hid->driver_input_lock); +} + /** * hid_map_usage - map usage input bits * -- cgit v1.2.3-70-g09d2 From fe7d4ccd1d7748bc9919c1bdee1e8286776f75ff Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Feb 2013 19:05:48 +0000 Subject: regmap: async: Add tracepoints for async I/O Trace when we start and complete async writes, and when we start and finish blocking for their completion. This is useful for performance analysis of the resulting I/O patterns. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 8 ++++++++ include/trace/events/regmap.h | 48 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) (limited to 'include') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 3d2367501fd..7c6d3be137b 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -999,6 +999,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, if (!async) return -ENOMEM; + trace_regmap_async_write_start(map->dev, reg, val_len); + async->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL | GFP_DMA); if (!async->work_buf) { @@ -1640,6 +1642,8 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) struct regmap *map = async->map; bool wake; + trace_regmap_async_io_complete(map->dev); + spin_lock(&map->async_lock); list_del(&async->list); @@ -1686,6 +1690,8 @@ int regmap_async_complete(struct regmap *map) if (!map->bus->async_write) return 0; + trace_regmap_async_complete_start(map->dev); + wait_event(map->async_waitq, regmap_async_is_done(map)); spin_lock_irqsave(&map->async_lock, flags); @@ -1693,6 +1699,8 @@ int regmap_async_complete(struct regmap *map) map->async_ret = 0; spin_unlock_irqrestore(&map->async_lock, flags); + trace_regmap_async_complete_done(map->dev); + return ret; } EXPORT_SYMBOL_GPL(regmap_async_complete); diff --git a/include/trace/events/regmap.h b/include/trace/events/regmap.h index 41a7dbd570e..a43a2f67bd8 100644 --- a/include/trace/events/regmap.h +++ b/include/trace/events/regmap.h @@ -175,6 +175,54 @@ DEFINE_EVENT(regmap_bool, regmap_cache_bypass, ); +DECLARE_EVENT_CLASS(regmap_async, + + TP_PROTO(struct device *dev), + + TP_ARGS(dev), + + TP_STRUCT__entry( + __string( name, dev_name(dev) ) + ), + + TP_fast_assign( + __assign_str(name, dev_name(dev)); + ), + + TP_printk("%s", __get_str(name)) +); + +DEFINE_EVENT(regmap_block, regmap_async_write_start, + + TP_PROTO(struct device *dev, unsigned int reg, int count), + + TP_ARGS(dev, reg, count) +); + +DEFINE_EVENT(regmap_async, regmap_async_io_complete, + + TP_PROTO(struct device *dev), + + TP_ARGS(dev) + +); + +DEFINE_EVENT(regmap_async, regmap_async_complete_start, + + TP_PROTO(struct device *dev), + + TP_ARGS(dev) + +); + +DEFINE_EVENT(regmap_async, regmap_async_complete_done, + + TP_PROTO(struct device *dev), + + TP_ARGS(dev) + +); + #endif /* _TRACE_REGMAP_H */ /* This part must be outside protection */ -- cgit v1.2.3-70-g09d2 From c8801a8e715d7793e1e7bcd2f6fe132234741753 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Mar 2012 16:35:48 +0000 Subject: regulator: core: Mark all get and enable calls as __must_check It's generally important that devices have power when they expect it so drivers really ought to be checking for errors on the power up paths. Signed-off-by: Mark Brown --- include/linux/regulator/consumer.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 7bc732ce6e5..145022a8308 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -141,18 +141,18 @@ void regulator_put(struct regulator *regulator); void devm_regulator_put(struct regulator *regulator); /* regulator output control and status */ -int regulator_enable(struct regulator *regulator); +int __must_check regulator_enable(struct regulator *regulator); int regulator_disable(struct regulator *regulator); int regulator_force_disable(struct regulator *regulator); int regulator_is_enabled(struct regulator *regulator); int regulator_disable_deferred(struct regulator *regulator, int ms); -int regulator_bulk_get(struct device *dev, int num_consumers, - struct regulator_bulk_data *consumers); -int devm_regulator_bulk_get(struct device *dev, int num_consumers, - struct regulator_bulk_data *consumers); -int regulator_bulk_enable(int num_consumers, - struct regulator_bulk_data *consumers); +int __must_check regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers); +int __must_check devm_regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers); +int __must_check regulator_bulk_enable(int num_consumers, + struct regulator_bulk_data *consumers); int regulator_bulk_disable(int num_consumers, struct regulator_bulk_data *consumers); int regulator_bulk_force_disable(int num_consumers, -- cgit v1.2.3-70-g09d2 From f19b00da8ed37db4e3891fe534fcf3a605a0e562 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Mon, 18 Feb 2013 06:50:39 +0000 Subject: regulator: core: support shared enable GPIO concept A Regulator can be enabled by external GPIO pin. This is configurable in the regulator_config. At this moment, the GPIO can be owned by only one regulator device. In some devices, multiple regulators are enabled by shared one GPIO pin. This patch extends this limitation, enabling shared enable GPIO of regulators. New list for enable GPIO: 'regulator_ena_gpio_list' This manages enable GPIO list. New structure for supporting shared enable GPIO: 'regulator_enable_gpio' The enable count is used for balancing GPIO control count. This count is incremented when GPIO is enabled. On the other hand, it's decremented when GPIO is disabled. Reference count: 'request_count' The reference count, 'request_count' is incremented/decremented on requesting/freeing the GPIO. This count makes sure only free the GPIO when it has no users. How it works If the GPIO is already used, skip requesting new GPIO usage. The GPIO is new one, request GPIO function and add it to the list of enable GPIO. This list is used for balancing enable GPIO count and pin control. Updating a GPIO and invert code moved 'ena_gpio' and 'ena_gpio_invert' of the regulator_config were moved to new function, regulator_ena_gpio_request(). Use regulator_enable_pin structure rather than regulator_dev. Signed-off-by: Milo(Woogyom) Kim Reviewed-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/core.c | 86 +++++++++++++++++++++++++++++++++++----- include/linux/regulator/driver.h | 2 + 2 files changed, 78 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index da9782bd27d..71d6adc4eea 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -51,6 +51,7 @@ static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_list); static LIST_HEAD(regulator_map_list); +static LIST_HEAD(regulator_ena_gpio_list); static bool has_full_constraints; static bool board_wants_dummy_regulator; @@ -68,6 +69,19 @@ struct regulator_map { struct regulator_dev *regulator; }; +/* + * struct regulator_enable_gpio + * + * Management for shared enable GPIO pin + */ +struct regulator_enable_gpio { + struct list_head list; + int gpio; + u32 enable_count; /* a number of enabled shared GPIO */ + u32 request_count; /* a number of requested shared GPIO */ + unsigned int ena_gpio_invert:1; +}; + /* * struct regulator * @@ -1456,6 +1470,65 @@ void devm_regulator_put(struct regulator *regulator) } EXPORT_SYMBOL_GPL(devm_regulator_put); +/* Manage enable GPIO list. Same GPIO pin can be shared among regulators */ +static int regulator_ena_gpio_request(struct regulator_dev *rdev, + const struct regulator_config *config) +{ + struct regulator_enable_gpio *pin; + int ret; + + list_for_each_entry(pin, ®ulator_ena_gpio_list, list) { + if (pin->gpio == config->ena_gpio) { + rdev_dbg(rdev, "GPIO %d is already used\n", + config->ena_gpio); + goto update_ena_gpio_to_rdev; + } + } + + ret = gpio_request_one(config->ena_gpio, + GPIOF_DIR_OUT | config->ena_gpio_flags, + rdev_get_name(rdev)); + if (ret) + return ret; + + pin = kzalloc(sizeof(struct regulator_enable_gpio), GFP_KERNEL); + if (pin == NULL) { + gpio_free(config->ena_gpio); + return -ENOMEM; + } + + pin->gpio = config->ena_gpio; + pin->ena_gpio_invert = config->ena_gpio_invert; + list_add(&pin->list, ®ulator_ena_gpio_list); + +update_ena_gpio_to_rdev: + pin->request_count++; + rdev->ena_pin = pin; + return 0; +} + +static void regulator_ena_gpio_free(struct regulator_dev *rdev) +{ + struct regulator_enable_gpio *pin, *n; + + if (!rdev->ena_pin) + return; + + /* Free the GPIO only in case of no use */ + list_for_each_entry_safe(pin, n, ®ulator_ena_gpio_list, list) { + if (pin->gpio == rdev->ena_pin->gpio) { + if (pin->request_count <= 1) { + pin->request_count = 0; + gpio_free(pin->gpio); + list_del(&pin->list); + kfree(pin); + } else { + pin->request_count--; + } + } + } +} + static int _regulator_do_enable(struct regulator_dev *rdev) { int ret, delay; @@ -3435,18 +3508,13 @@ regulator_register(const struct regulator_desc *regulator_desc, dev_set_drvdata(&rdev->dev, rdev); if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) { - ret = gpio_request_one(config->ena_gpio, - GPIOF_DIR_OUT | config->ena_gpio_flags, - rdev_get_name(rdev)); + ret = regulator_ena_gpio_request(rdev, config); if (ret != 0) { rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", config->ena_gpio, ret); goto wash; } - rdev->ena_gpio = config->ena_gpio; - rdev->ena_gpio_invert = config->ena_gpio_invert; - if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH) rdev->ena_gpio_state = 1; @@ -3522,8 +3590,7 @@ unset_supplies: scrub: if (rdev->supply) _regulator_put(rdev->supply); - if (rdev->ena_gpio) - gpio_free(rdev->ena_gpio); + regulator_ena_gpio_free(rdev); kfree(rdev->constraints); wash: device_unregister(&rdev->dev); @@ -3558,8 +3625,7 @@ void regulator_unregister(struct regulator_dev *rdev) unset_regulator_supplies(rdev); list_del(&rdev->list); kfree(rdev->constraints); - if (rdev->ena_gpio) - gpio_free(rdev->ena_gpio); + regulator_ena_gpio_free(rdev); device_unregister(&rdev->dev); mutex_unlock(®ulator_list_mutex); } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 23070fd8387..a467d11dd67 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -22,6 +22,7 @@ struct regmap; struct regulator_dev; struct regulator_init_data; +struct regulator_enable_gpio; enum regulator_status { REGULATOR_STATUS_OFF, @@ -300,6 +301,7 @@ struct regulator_dev { struct dentry *debugfs; + struct regulator_enable_gpio *ena_pin; int ena_gpio; unsigned int ena_gpio_invert:1; unsigned int ena_gpio_state:1; -- cgit v1.2.3-70-g09d2 From 7b74d149247c8972da1cec3e4c70b67049aaeb69 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Mon, 18 Feb 2013 06:50:55 +0000 Subject: regulator: core: use regulator_ena_pin member The regulator_dev has regulator_enable_gpio structure. 'ena_gpio' and 'ena_gpio_invert' were moved to in regulator_enable_gpio. regulator_dev ---> regulator_enable_gpio .ena_gpio .gpio .ena_gpio_invert .ena_gpio_invert Pointer, 'ena_pin' is used for checking valid enable GPIO pin. Signed-off-by: Milo(Woogyom) Kim Reviewed-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/core.c | 6 +++--- include/linux/regulator/driver.h | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 57d434d3145..6c8c82406cd 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1945,7 +1945,7 @@ EXPORT_SYMBOL_GPL(regulator_disable_regmap); static int _regulator_is_enabled(struct regulator_dev *rdev) { /* A GPIO control always takes precedence */ - if (rdev->ena_gpio) + if (rdev->ena_pin) return rdev->ena_gpio_state; /* If we don't know then assume that the regulator is always on */ @@ -3344,7 +3344,7 @@ static int add_regulator_attributes(struct regulator_dev *rdev) if (status < 0) return status; } - if (rdev->ena_gpio || ops->is_enabled) { + if (rdev->ena_pin || ops->is_enabled) { status = device_create_file(dev, &dev_attr_state); if (status < 0) return status; @@ -3556,7 +3556,7 @@ regulator_register(const struct regulator_desc *regulator_desc, if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH) rdev->ena_gpio_state = 1; - if (rdev->ena_gpio_invert) + if (config->ena_gpio_invert) rdev->ena_gpio_state = !rdev->ena_gpio_state; } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index a467d11dd67..7b7aeec04f8 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -302,8 +302,6 @@ struct regulator_dev { struct dentry *debugfs; struct regulator_enable_gpio *ena_pin; - int ena_gpio; - unsigned int ena_gpio_invert:1; unsigned int ena_gpio_state:1; }; -- cgit v1.2.3-70-g09d2 From 8f5f5e0f459d37273f841e3f8da38b4e242c8e94 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Feb 2013 17:07:35 -0700 Subject: ASoC: tegra_wm8903: assume CONFIG_OF, remove platform data Tegra only supports, and always enables, device tree. Remove all runtime checks for DT support from the driver. This allows removal of the hard-coded Harmony ASoC mapping table, since Harmony only boots with DT now. All board-specific configuration now comes from device tree, so there is no need to have a platform_data structure. Rework the driver to parse the device tree directly into struct tegra_wm8903. Also some slight re-ordering of probe() so that the code more closely resembles other drivers for easier comparison. Inparticular, the GPIO DT parsing and initial programming are moved together for each GPIO. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- include/sound/tegra_wm8903.h | 26 ------ sound/soc/tegra/tegra_wm8903.c | 179 ++++++++++++++++------------------------- 2 files changed, 70 insertions(+), 135 deletions(-) delete mode 100644 include/sound/tegra_wm8903.h (limited to 'include') diff --git a/include/sound/tegra_wm8903.h b/include/sound/tegra_wm8903.h deleted file mode 100644 index 57b202ee97c..00000000000 --- a/include/sound/tegra_wm8903.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011 NVIDIA, 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 __SOUND_TEGRA_WM38903_H -#define __SOUND_TEGRA_WM38903_H - -struct tegra_wm8903_platform_data { - int gpio_spkr_en; - int gpio_hp_det; - int gpio_hp_mute; - int gpio_int_mic_en; - int gpio_ext_mic_en; -}; - -#endif diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index bbd79bf5630..4ac73730d79 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -39,7 +39,6 @@ #include #include #include -#include #include "../codecs/wm8903.h" @@ -48,7 +47,11 @@ #define DRV_NAME "tegra-snd-wm8903" struct tegra_wm8903 { - struct tegra_wm8903_platform_data pdata; + int gpio_spkr_en; + int gpio_hp_det; + int gpio_hp_mute; + int gpio_int_mic_en; + int gpio_ext_mic_en; struct tegra_asoc_utils_data util_data; }; @@ -129,12 +132,11 @@ static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); - struct tegra_wm8903_platform_data *pdata = &machine->pdata; - if (!gpio_is_valid(pdata->gpio_spkr_en)) + if (!gpio_is_valid(machine->gpio_spkr_en)) return 0; - gpio_set_value_cansleep(pdata->gpio_spkr_en, + gpio_set_value_cansleep(machine->gpio_spkr_en, SND_SOC_DAPM_EVENT_ON(event)); return 0; @@ -146,12 +148,11 @@ static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); - struct tegra_wm8903_platform_data *pdata = &machine->pdata; - if (!gpio_is_valid(pdata->gpio_hp_mute)) + if (!gpio_is_valid(machine->gpio_hp_mute)) return 0; - gpio_set_value_cansleep(pdata->gpio_hp_mute, + gpio_set_value_cansleep(machine->gpio_hp_mute, !SND_SOC_DAPM_EVENT_ON(event)); return 0; @@ -163,17 +164,6 @@ static const struct snd_soc_dapm_widget tegra_wm8903_dapm_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), }; -static const struct snd_soc_dapm_route harmony_audio_map[] = { - {"Headphone Jack", NULL, "HPOUTR"}, - {"Headphone Jack", NULL, "HPOUTL"}, - {"Int Spk", NULL, "ROP"}, - {"Int Spk", NULL, "RON"}, - {"Int Spk", NULL, "LOP"}, - {"Int Spk", NULL, "LON"}, - {"Mic Jack", NULL, "MICBIAS"}, - {"IN1L", NULL, "Mic Jack"}, -}; - static const struct snd_kcontrol_new tegra_wm8903_controls[] = { SOC_DAPM_PIN_SWITCH("Int Spk"), }; @@ -185,10 +175,9 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_card *card = codec->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); - struct tegra_wm8903_platform_data *pdata = &machine->pdata; - if (gpio_is_valid(pdata->gpio_hp_det)) { - tegra_wm8903_hp_jack_gpio.gpio = pdata->gpio_hp_det; + if (gpio_is_valid(machine->gpio_hp_det)) { + tegra_wm8903_hp_jack_gpio.gpio = machine->gpio_hp_det; snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, &tegra_wm8903_hp_jack); snd_soc_jack_add_pins(&tegra_wm8903_hp_jack, @@ -226,9 +215,6 @@ static int tegra_wm8903_remove(struct snd_soc_card *card) static struct snd_soc_dai_link tegra_wm8903_dai = { .name = "WM8903", .stream_name = "WM8903 PCM", - .codec_name = "wm8903.0-001a", - .platform_name = "tegra20-i2s.0", - .cpu_dai_name = "tegra20-i2s.0", .codec_dai_name = "wm8903-hifi", .init = tegra_wm8903_init, .ops = &tegra_wm8903_ops, @@ -257,96 +243,25 @@ static int tegra_wm8903_driver_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct snd_soc_card *card = &snd_soc_tegra_wm8903; struct tegra_wm8903 *machine; - struct tegra_wm8903_platform_data *pdata; int ret; - if (!pdev->dev.platform_data && !pdev->dev.of_node) { - dev_err(&pdev->dev, "No platform data supplied\n"); - return -EINVAL; - } - machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8903), GFP_KERNEL); if (!machine) { dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } - pdata = &machine->pdata; card->dev = &pdev->dev; platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, machine); - if (pdev->dev.platform_data) { - memcpy(pdata, card->dev->platform_data, sizeof(*pdata)); - } else if (np) { - pdata->gpio_spkr_en = of_get_named_gpio(np, - "nvidia,spkr-en-gpios", 0); - if (pdata->gpio_spkr_en == -EPROBE_DEFER) - return -EPROBE_DEFER; - - pdata->gpio_hp_mute = of_get_named_gpio(np, - "nvidia,hp-mute-gpios", 0); - if (pdata->gpio_hp_mute == -EPROBE_DEFER) - return -EPROBE_DEFER; - - pdata->gpio_hp_det = of_get_named_gpio(np, - "nvidia,hp-det-gpios", 0); - if (pdata->gpio_hp_det == -EPROBE_DEFER) - return -EPROBE_DEFER; - - pdata->gpio_int_mic_en = of_get_named_gpio(np, - "nvidia,int-mic-en-gpios", 0); - if (pdata->gpio_int_mic_en == -EPROBE_DEFER) - return -EPROBE_DEFER; - - pdata->gpio_ext_mic_en = of_get_named_gpio(np, - "nvidia,ext-mic-en-gpios", 0); - if (pdata->gpio_ext_mic_en == -EPROBE_DEFER) - return -EPROBE_DEFER; - } - - if (np) { - ret = snd_soc_of_parse_card_name(card, "nvidia,model"); - if (ret) - goto err; - - ret = snd_soc_of_parse_audio_routing(card, - "nvidia,audio-routing"); - if (ret) - goto err; - - tegra_wm8903_dai.codec_name = NULL; - tegra_wm8903_dai.codec_of_node = of_parse_phandle(np, - "nvidia,audio-codec", 0); - if (!tegra_wm8903_dai.codec_of_node) { - dev_err(&pdev->dev, - "Property 'nvidia,audio-codec' missing or invalid\n"); - ret = -EINVAL; - goto err; - } - - tegra_wm8903_dai.cpu_dai_name = NULL; - tegra_wm8903_dai.cpu_of_node = of_parse_phandle(np, - "nvidia,i2s-controller", 0); - if (!tegra_wm8903_dai.cpu_of_node) { - dev_err(&pdev->dev, - "Property 'nvidia,i2s-controller' missing or invalid\n"); - ret = -EINVAL; - goto err; - } - - tegra_wm8903_dai.platform_name = NULL; - tegra_wm8903_dai.platform_of_node = - tegra_wm8903_dai.cpu_of_node; - } else { - card->dapm_routes = harmony_audio_map; - card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map); - } - - if (gpio_is_valid(pdata->gpio_spkr_en)) { - ret = devm_gpio_request_one(&pdev->dev, pdata->gpio_spkr_en, + machine->gpio_spkr_en = of_get_named_gpio(np, "nvidia,spkr-en-gpios", + 0); + if (machine->gpio_spkr_en == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(machine->gpio_spkr_en)) { + ret = devm_gpio_request_one(&pdev->dev, machine->gpio_spkr_en, GPIOF_OUT_INIT_LOW, "spkr_en"); if (ret) { dev_err(card->dev, "cannot get spkr_en gpio\n"); @@ -354,8 +269,12 @@ static int tegra_wm8903_driver_probe(struct platform_device *pdev) } } - if (gpio_is_valid(pdata->gpio_hp_mute)) { - ret = devm_gpio_request_one(&pdev->dev, pdata->gpio_hp_mute, + machine->gpio_hp_mute = of_get_named_gpio(np, "nvidia,hp-mute-gpios", + 0); + if (machine->gpio_hp_mute == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(machine->gpio_hp_mute)) { + ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_mute, GPIOF_OUT_INIT_HIGH, "hp_mute"); if (ret) { dev_err(card->dev, "cannot get hp_mute gpio\n"); @@ -363,9 +282,18 @@ static int tegra_wm8903_driver_probe(struct platform_device *pdev) } } - if (gpio_is_valid(pdata->gpio_int_mic_en)) { + machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); + if (machine->gpio_hp_det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + machine->gpio_int_mic_en = of_get_named_gpio(np, + "nvidia,int-mic-en-gpios", 0); + if (machine->gpio_int_mic_en == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(machine->gpio_int_mic_en)) { /* Disable int mic; enable signal is active-high */ - ret = devm_gpio_request_one(&pdev->dev, pdata->gpio_int_mic_en, + ret = devm_gpio_request_one(&pdev->dev, + machine->gpio_int_mic_en, GPIOF_OUT_INIT_LOW, "int_mic_en"); if (ret) { dev_err(card->dev, "cannot get int_mic_en gpio\n"); @@ -373,9 +301,14 @@ static int tegra_wm8903_driver_probe(struct platform_device *pdev) } } - if (gpio_is_valid(pdata->gpio_ext_mic_en)) { + machine->gpio_ext_mic_en = of_get_named_gpio(np, + "nvidia,ext-mic-en-gpios", 0); + if (machine->gpio_ext_mic_en == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(machine->gpio_ext_mic_en)) { /* Enable ext mic; enable signal is active-low */ - ret = devm_gpio_request_one(&pdev->dev, pdata->gpio_ext_mic_en, + ret = devm_gpio_request_one(&pdev->dev, + machine->gpio_ext_mic_en, GPIOF_OUT_INIT_LOW, "ext_mic_en"); if (ret) { dev_err(card->dev, "cannot get ext_mic_en gpio\n"); @@ -383,6 +316,34 @@ static int tegra_wm8903_driver_probe(struct platform_device *pdev) } } + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto err; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto err; + + tegra_wm8903_dai.codec_of_node = of_parse_phandle(np, + "nvidia,audio-codec", 0); + if (!tegra_wm8903_dai.codec_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_wm8903_dai.cpu_of_node = of_parse_phandle(np, + "nvidia,i2s-controller", 0); + if (!tegra_wm8903_dai.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_wm8903_dai.platform_of_node = tegra_wm8903_dai.cpu_of_node; + ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); if (ret) goto err; -- cgit v1.2.3-70-g09d2 From 07fe6e00f6cca6fef85a14a1dc3ed4f2e35d3f0b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 21 Jan 2013 15:03:44 -0500 Subject: get rid of duplicate logics in __SC_....[1-6] definitions All those guys have the same form - "take a list of type/name pairs, apply some macro to each of them". Abstract that part away, convert all __SC_FOO##x(__VA_ARGS__) to __MAP(x,__SC_FOO,__VA_ARGS__). Signed-off-by: Al Viro --- include/linux/compat.h | 18 ++++------- include/linux/syscalls.h | 82 +++++++++++++++++++----------------------------- 2 files changed, 38 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/include/linux/compat.h b/include/linux/compat.h index 76a87fb57ac..8c1dfc8d830 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -27,12 +27,6 @@ #define __SC_DELOUSE(t,v) ((t)(unsigned long)(v)) #endif -#define __SC_CCAST1(t1, a1) __SC_DELOUSE(t1,a1) -#define __SC_CCAST2(t2, a2, ...) __SC_DELOUSE(t2,a2), __SC_CCAST1(__VA_ARGS__) -#define __SC_CCAST3(t3, a3, ...) __SC_DELOUSE(t3,a3), __SC_CCAST2(__VA_ARGS__) -#define __SC_CCAST4(t4, a4, ...) __SC_DELOUSE(t4,a4), __SC_CCAST3(__VA_ARGS__) -#define __SC_CCAST5(t5, a5, ...) __SC_DELOUSE(t5,a5), __SC_CCAST4(__VA_ARGS__) -#define __SC_CCAST6(t6, a6, ...) __SC_DELOUSE(t6,a6), __SC_CCAST5(__VA_ARGS__) #define COMPAT_SYSCALL_DEFINE1(name, ...) \ COMPAT_SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) #define COMPAT_SYSCALL_DEFINE2(name, ...) \ @@ -49,19 +43,19 @@ #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ - asmlinkage long compat_sys##name(__SC_DECL##x(__VA_ARGS__)); \ - static inline long C_SYSC##name(__SC_DECL##x(__VA_ARGS__)); \ - asmlinkage long compat_SyS##name(__SC_LONG##x(__VA_ARGS__)) \ + asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ + static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ + asmlinkage long compat_SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))\ { \ - return (long) C_SYSC##name(__SC_CCAST##x(__VA_ARGS__)); \ + return C_SYSC##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \ } \ SYSCALL_ALIAS(compat_sys##name, compat_SyS##name); \ - static inline long C_SYSC##name(__SC_DECL##x(__VA_ARGS__)) + static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) #else /* CONFIG_HAVE_SYSCALL_WRAPPERS */ #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ - asmlinkage long compat_sys##name(__SC_DECL##x(__VA_ARGS__)) + asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) #endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */ diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 313a8e0a655..f9411f0c1c8 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -78,49 +78,31 @@ struct sigaltstack; #include #include -#define __SC_DECL1(t1, a1) t1 a1 -#define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__) -#define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__) -#define __SC_DECL4(t4, a4, ...) t4 a4, __SC_DECL3(__VA_ARGS__) -#define __SC_DECL5(t5, a5, ...) t5 a5, __SC_DECL4(__VA_ARGS__) -#define __SC_DECL6(t6, a6, ...) t6 a6, __SC_DECL5(__VA_ARGS__) - -#define __SC_LONG1(t1, a1) long a1 -#define __SC_LONG2(t2, a2, ...) long a2, __SC_LONG1(__VA_ARGS__) -#define __SC_LONG3(t3, a3, ...) long a3, __SC_LONG2(__VA_ARGS__) -#define __SC_LONG4(t4, a4, ...) long a4, __SC_LONG3(__VA_ARGS__) -#define __SC_LONG5(t5, a5, ...) long a5, __SC_LONG4(__VA_ARGS__) -#define __SC_LONG6(t6, a6, ...) long a6, __SC_LONG5(__VA_ARGS__) - -#define __SC_CAST1(t1, a1) (t1) a1 -#define __SC_CAST2(t2, a2, ...) (t2) a2, __SC_CAST1(__VA_ARGS__) -#define __SC_CAST3(t3, a3, ...) (t3) a3, __SC_CAST2(__VA_ARGS__) -#define __SC_CAST4(t4, a4, ...) (t4) a4, __SC_CAST3(__VA_ARGS__) -#define __SC_CAST5(t5, a5, ...) (t5) a5, __SC_CAST4(__VA_ARGS__) -#define __SC_CAST6(t6, a6, ...) (t6) a6, __SC_CAST5(__VA_ARGS__) - -#define __SC_TEST(type) BUILD_BUG_ON(sizeof(type) > sizeof(long)) -#define __SC_TEST1(t1, a1) __SC_TEST(t1) -#define __SC_TEST2(t2, a2, ...) __SC_TEST(t2); __SC_TEST1(__VA_ARGS__) -#define __SC_TEST3(t3, a3, ...) __SC_TEST(t3); __SC_TEST2(__VA_ARGS__) -#define __SC_TEST4(t4, a4, ...) __SC_TEST(t4); __SC_TEST3(__VA_ARGS__) -#define __SC_TEST5(t5, a5, ...) __SC_TEST(t5); __SC_TEST4(__VA_ARGS__) -#define __SC_TEST6(t6, a6, ...) __SC_TEST(t6); __SC_TEST5(__VA_ARGS__) +/* + * __MAP - apply a macro to syscall arguments + * __MAP(n, m, t1, a1, t2, a2, ..., tn, an) will expand to + * m(t1, a1), m(t2, a2), ..., m(tn, an) + * The first argument must be equal to the amount of type/name + * pairs given. Note that this list of pairs (i.e. the arguments + * of __MAP starting at the third one) is in the same format as + * for SYSCALL_DEFINE/COMPAT_SYSCALL_DEFINE + */ +#define __MAP1(m,t,a) m(t,a) +#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__) +#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__) +#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__) +#define __MAP5(m,t,a,...) m(t,a), __MAP4(m,__VA_ARGS__) +#define __MAP6(m,t,a,...) m(t,a), __MAP5(m,__VA_ARGS__) +#define __MAP(n,...) __MAP##n(__VA_ARGS__) + +#define __SC_DECL(t, a) t a +#define __SC_LONG(t, a) long a +#define __SC_CAST(t, a) (t) a +#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(sizeof(type) > sizeof(long)) #ifdef CONFIG_FTRACE_SYSCALLS -#define __SC_STR_ADECL1(t, a) #a -#define __SC_STR_ADECL2(t, a, ...) #a, __SC_STR_ADECL1(__VA_ARGS__) -#define __SC_STR_ADECL3(t, a, ...) #a, __SC_STR_ADECL2(__VA_ARGS__) -#define __SC_STR_ADECL4(t, a, ...) #a, __SC_STR_ADECL3(__VA_ARGS__) -#define __SC_STR_ADECL5(t, a, ...) #a, __SC_STR_ADECL4(__VA_ARGS__) -#define __SC_STR_ADECL6(t, a, ...) #a, __SC_STR_ADECL5(__VA_ARGS__) - -#define __SC_STR_TDECL1(t, a) #t -#define __SC_STR_TDECL2(t, a, ...) #t, __SC_STR_TDECL1(__VA_ARGS__) -#define __SC_STR_TDECL3(t, a, ...) #t, __SC_STR_TDECL2(__VA_ARGS__) -#define __SC_STR_TDECL4(t, a, ...) #t, __SC_STR_TDECL3(__VA_ARGS__) -#define __SC_STR_TDECL5(t, a, ...) #t, __SC_STR_TDECL4(__VA_ARGS__) -#define __SC_STR_TDECL6(t, a, ...) #t, __SC_STR_TDECL5(__VA_ARGS__) +#define __SC_STR_ADECL(t, a) #a +#define __SC_STR_TDECL(t, a) #t extern struct ftrace_event_class event_class_syscall_enter; extern struct ftrace_event_class event_class_syscall_exit; @@ -217,10 +199,10 @@ extern struct trace_event_functions exit_syscall_print_funcs; #ifdef CONFIG_FTRACE_SYSCALLS #define SYSCALL_DEFINEx(x, sname, ...) \ static const char *types_##sname[] = { \ - __SC_STR_TDECL##x(__VA_ARGS__) \ + __MAP(x,__SC_STR_TDECL,__VA_ARGS__) \ }; \ static const char *args_##sname[] = { \ - __SC_STR_ADECL##x(__VA_ARGS__) \ + __MAP(x,__SC_STR_ADECL,__VA_ARGS__) \ }; \ SYSCALL_METADATA(sname, x); \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) @@ -234,21 +216,21 @@ extern struct trace_event_functions exit_syscall_print_funcs; #define SYSCALL_DEFINE(name) static inline long SYSC_##name #define __SYSCALL_DEFINEx(x, name, ...) \ - asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)); \ - static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)); \ - asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__)) \ + asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ + static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ + asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \ - __SC_TEST##x(__VA_ARGS__); \ - return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__)); \ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + return SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ } \ SYSCALL_ALIAS(sys##name, SyS##name); \ - static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)) + static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) #else /* CONFIG_HAVE_SYSCALL_WRAPPERS */ #define SYSCALL_DEFINE(name) asmlinkage long sys_##name #define __SYSCALL_DEFINEx(x, name, ...) \ - asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)) + asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) #endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */ -- cgit v1.2.3-70-g09d2 From 4a0fd5bf0fd0795af8f1be3b261f5cf146a4cb9b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 21 Jan 2013 15:16:58 -0500 Subject: teach SYSCALL_DEFINE how to deal with long long/unsigned long long ... and convert a bunch of SYSCALL_DEFINE ones to SYSCALL_DEFINE, killing the boilerplate crap around them. Signed-off-by: Al Viro --- arch/s390/kernel/sys_s390.c | 14 ++------------ fs/dcookies.c | 9 +-------- fs/notify/fanotify/fanotify_user.c | 17 +++-------------- fs/open.c | 28 +++------------------------- fs/read_write.c | 24 ++++-------------------- fs/sync.c | 26 ++++---------------------- include/linux/syscalls.h | 5 +++-- mm/fadvise.c | 18 ++---------------- mm/readahead.c | 9 +-------- 9 files changed, 23 insertions(+), 127 deletions(-) (limited to 'include') diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c index d0964d22adb..23eb222c165 100644 --- a/arch/s390/kernel/sys_s390.c +++ b/arch/s390/kernel/sys_s390.c @@ -132,19 +132,9 @@ SYSCALL_DEFINE1(s390_fadvise64_64, struct fadvise64_64_args __user *, args) * to * %r2: fd, %r3: mode, %r4/%r5: offset, 96(%r15)-103(%r15): len */ -SYSCALL_DEFINE(s390_fallocate)(int fd, int mode, loff_t offset, - u32 len_high, u32 len_low) +SYSCALL_DEFINE5(s390_fallocate, int, fd, int, mode, loff_t, offset, + u32, len_high, u32, len_low) { return sys_fallocate(fd, mode, offset, ((u64)len_high << 32) | len_low); } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_s390_fallocate(long fd, long mode, loff_t offset, - long len_high, long len_low) -{ - return SYSC_s390_fallocate((int) fd, (int) mode, offset, - (u32) len_high, (u32) len_low); -} -SYSCALL_ALIAS(sys_s390_fallocate, SyS_s390_fallocate); -#endif - #endif diff --git a/fs/dcookies.c b/fs/dcookies.c index 17c77996782..f08375b97ff 100644 --- a/fs/dcookies.c +++ b/fs/dcookies.c @@ -145,7 +145,7 @@ out: /* And here is where the userspace process can look up the cookie value * to retrieve the path. */ -SYSCALL_DEFINE(lookup_dcookie)(u64 cookie64, char __user * buf, size_t len) +SYSCALL_DEFINE3(lookup_dcookie, u64, cookie64, char __user *, buf, size_t, len) { unsigned long cookie = (unsigned long)cookie64; int err = -EINVAL; @@ -201,13 +201,6 @@ out: mutex_unlock(&dcookie_mutex); return err; } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_lookup_dcookie(u64 cookie64, long buf, long len) -{ - return SYSC_lookup_dcookie(cookie64, (char __user *) buf, (size_t) len); -} -SYSCALL_ALIAS(sys_lookup_dcookie, SyS_lookup_dcookie); -#endif static int dcookie_init(void) { diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 5d8444268a1..d0be29fa94c 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -755,9 +755,9 @@ out_destroy_group: return fd; } -SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, - __u64 mask, int dfd, - const char __user * pathname) +SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, + __u64, mask, int, dfd, + const char __user *, pathname) { struct inode *inode = NULL; struct vfsmount *mnt = NULL; @@ -857,17 +857,6 @@ fput_and_out: return ret; } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_fanotify_mark(long fanotify_fd, long flags, __u64 mask, - long dfd, long pathname) -{ - return SYSC_fanotify_mark((int) fanotify_fd, (unsigned int) flags, - mask, (int) dfd, - (const char __user *) pathname); -} -SYSCALL_ALIAS(sys_fanotify_mark, SyS_fanotify_mark); -#endif - /* * fanotify_user_setup - Our initialization function. Note that we cannot return * error because we have compiled-in VFS hooks. So an (unlikely) failure here diff --git a/fs/open.c b/fs/open.c index 68354466879..a5392245044 100644 --- a/fs/open.c +++ b/fs/open.c @@ -212,32 +212,18 @@ COMPAT_SYSCALL_DEFINE2(ftruncate, unsigned int, fd, compat_ulong_t, length) /* LFS versions of truncate are only needed on 32 bit machines */ #if BITS_PER_LONG == 32 -SYSCALL_DEFINE(truncate64)(const char __user * path, loff_t length) +SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, length) { return do_sys_truncate(path, length); } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_truncate64(long path, loff_t length) -{ - return SYSC_truncate64((const char __user *) path, length); -} -SYSCALL_ALIAS(sys_truncate64, SyS_truncate64); -#endif -SYSCALL_DEFINE(ftruncate64)(unsigned int fd, loff_t length) +SYSCALL_DEFINE2(ftruncate64, unsigned int, fd, loff_t, length) { long ret = do_sys_ftruncate(fd, length, 0); /* avoid REGPARM breakage on x86: */ asmlinkage_protect(2, ret, fd, length); return ret; } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_ftruncate64(long fd, loff_t length) -{ - return SYSC_ftruncate64((unsigned int) fd, length); -} -SYSCALL_ALIAS(sys_ftruncate64, SyS_ftruncate64); -#endif #endif /* BITS_PER_LONG == 32 */ @@ -299,7 +285,7 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) return ret; } -SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len) +SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len) { struct fd f = fdget(fd); int error = -EBADF; @@ -311,14 +297,6 @@ SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len) return error; } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_fallocate(long fd, long mode, loff_t offset, loff_t len) -{ - return SYSC_fallocate((int)fd, (int)mode, offset, len); -} -SYSCALL_ALIAS(sys_fallocate, SyS_fallocate); -#endif - /* * access() needs to use the real uid/gid, not the effective uid/gid. * We do this by temporarily clearing all FS-related capabilities and diff --git a/fs/read_write.c b/fs/read_write.c index a698eff457f..dcfd58d95f4 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -487,8 +487,8 @@ SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, return ret; } -SYSCALL_DEFINE(pread64)(unsigned int fd, char __user *buf, - size_t count, loff_t pos) +SYSCALL_DEFINE4(pread64, unsigned int, fd, char __user *, buf, + size_t, count, loff_t, pos) { struct fd f; ssize_t ret = -EBADF; @@ -506,17 +506,9 @@ SYSCALL_DEFINE(pread64)(unsigned int fd, char __user *buf, return ret; } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_pread64(long fd, long buf, long count, loff_t pos) -{ - return SYSC_pread64((unsigned int) fd, (char __user *) buf, - (size_t) count, pos); -} -SYSCALL_ALIAS(sys_pread64, SyS_pread64); -#endif -SYSCALL_DEFINE(pwrite64)(unsigned int fd, const char __user *buf, - size_t count, loff_t pos) +SYSCALL_DEFINE4(pwrite64, unsigned int, fd, const char __user *, buf, + size_t, count, loff_t, pos) { struct fd f; ssize_t ret = -EBADF; @@ -534,14 +526,6 @@ SYSCALL_DEFINE(pwrite64)(unsigned int fd, const char __user *buf, return ret; } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_pwrite64(long fd, long buf, long count, loff_t pos) -{ - return SYSC_pwrite64((unsigned int) fd, (const char __user *) buf, - (size_t) count, pos); -} -SYSCALL_ALIAS(sys_pwrite64, SyS_pwrite64); -#endif /* * Reduce an iovec's length in-place. Return the resulting number of segments diff --git a/fs/sync.c b/fs/sync.c index 2c5d6639a66..905f3f6b3d8 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -283,8 +283,8 @@ EXPORT_SYMBOL(generic_write_sync); * already-instantiated disk blocks, there are no guarantees here that the data * will be available after a crash. */ -SYSCALL_DEFINE(sync_file_range)(int fd, loff_t offset, loff_t nbytes, - unsigned int flags) +SYSCALL_DEFINE4(sync_file_range, int, fd, loff_t, offset, loff_t, nbytes, + unsigned int, flags) { int ret; struct fd f; @@ -365,29 +365,11 @@ out_put: out: return ret; } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_sync_file_range(long fd, loff_t offset, loff_t nbytes, - long flags) -{ - return SYSC_sync_file_range((int) fd, offset, nbytes, - (unsigned int) flags); -} -SYSCALL_ALIAS(sys_sync_file_range, SyS_sync_file_range); -#endif /* It would be nice if people remember that not all the world's an i386 when they introduce new system calls */ -SYSCALL_DEFINE(sync_file_range2)(int fd, unsigned int flags, - loff_t offset, loff_t nbytes) +SYSCALL_DEFINE4(sync_file_range2, int, fd, unsigned int, flags, + loff_t, offset, loff_t, nbytes) { return sys_sync_file_range(fd, offset, nbytes, flags); } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_sync_file_range2(long fd, long flags, - loff_t offset, loff_t nbytes) -{ - return SYSC_sync_file_range2((int) fd, (unsigned int) flags, - offset, nbytes); -} -SYSCALL_ALIAS(sys_sync_file_range2, SyS_sync_file_range2); -#endif diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index f9411f0c1c8..3e07b92efbf 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -96,9 +96,10 @@ struct sigaltstack; #define __MAP(n,...) __MAP##n(__VA_ARGS__) #define __SC_DECL(t, a) t a -#define __SC_LONG(t, a) long a +#define __TYPE_IS_LL(t) (__same_type((t)0, 0LL) || __same_type((t)0, 0ULL)) +#define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a #define __SC_CAST(t, a) (t) a -#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(sizeof(type) > sizeof(long)) +#define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long)) #ifdef CONFIG_FTRACE_SYSCALLS #define __SC_STR_ADECL(t, a) #a diff --git a/mm/fadvise.c b/mm/fadvise.c index 7e092689a12..3bcfd81db45 100644 --- a/mm/fadvise.c +++ b/mm/fadvise.c @@ -25,7 +25,7 @@ * POSIX_FADV_WILLNEED could set PG_Referenced, and POSIX_FADV_NOREUSE could * deactivate the pages and clear PG_Referenced. */ -SYSCALL_DEFINE(fadvise64_64)(int fd, loff_t offset, loff_t len, int advice) +SYSCALL_DEFINE4(fadvise64_64, int, fd, loff_t, offset, loff_t, len, int, advice) { struct fd f = fdget(fd); struct address_space *mapping; @@ -145,26 +145,12 @@ out: fdput(f); return ret; } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_fadvise64_64(long fd, loff_t offset, loff_t len, long advice) -{ - return SYSC_fadvise64_64((int) fd, offset, len, (int) advice); -} -SYSCALL_ALIAS(sys_fadvise64_64, SyS_fadvise64_64); -#endif #ifdef __ARCH_WANT_SYS_FADVISE64 -SYSCALL_DEFINE(fadvise64)(int fd, loff_t offset, size_t len, int advice) +SYSCALL_DEFINE4(fadvise64, int, fd, loff_t, offset, size_t, len, int, advice) { return sys_fadvise64_64(fd, offset, len, advice); } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_fadvise64(long fd, loff_t offset, long len, long advice) -{ - return SYSC_fadvise64((int) fd, offset, (size_t)len, (int)advice); -} -SYSCALL_ALIAS(sys_fadvise64, SyS_fadvise64); -#endif #endif diff --git a/mm/readahead.c b/mm/readahead.c index 7963f239123..daed28dd583 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -576,7 +576,7 @@ do_readahead(struct address_space *mapping, struct file *filp, return 0; } -SYSCALL_DEFINE(readahead)(int fd, loff_t offset, size_t count) +SYSCALL_DEFINE3(readahead, int, fd, loff_t, offset, size_t, count) { ssize_t ret; struct fd f; @@ -595,10 +595,3 @@ SYSCALL_DEFINE(readahead)(int fd, loff_t offset, size_t count) } return ret; } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_readahead(long fd, loff_t offset, long count) -{ - return SYSC_readahead((int) fd, offset, (size_t) count); -} -SYSCALL_ALIAS(sys_readahead, SyS_readahead); -#endif -- cgit v1.2.3-70-g09d2 From e1b5bb6d1236d4ad2084c53aa83dde7cdf6f8eea Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 21 Jan 2013 17:16:07 -0500 Subject: consolidate cond_syscall and SYSCALL_ALIAS declarations take them to asm/linkage.h, with default in linux/linkage.h Signed-off-by: Al Viro --- arch/alpha/include/asm/linkage.h | 4 +++- arch/alpha/include/asm/unistd.h | 12 ------------ arch/arm/include/asm/unistd.h | 8 -------- arch/avr32/include/asm/unistd.h | 8 -------- arch/blackfin/include/asm/unistd.h | 8 -------- arch/cris/include/asm/unistd.h | 8 -------- arch/frv/include/asm/unistd.h | 10 ---------- arch/h8300/include/asm/linkage.h | 2 -- arch/h8300/include/asm/unistd.h | 7 ------- arch/ia64/include/asm/linkage.h | 4 ++++ arch/ia64/include/asm/unistd.h | 10 ---------- arch/m32r/include/asm/unistd.h | 10 ---------- arch/m68k/include/asm/unistd.h | 8 -------- arch/microblaze/include/asm/unistd.h | 8 -------- arch/mips/include/asm/linkage.h | 3 +++ arch/mips/include/asm/unistd.h | 8 -------- arch/mn10300/include/asm/unistd.h | 10 ---------- arch/parisc/include/asm/unistd.h | 8 -------- arch/powerpc/include/asm/linkage.h | 13 +++++++++++++ arch/powerpc/include/asm/unistd.h | 6 ------ arch/powerpc/include/uapi/asm/linkage.h | 6 ------ arch/s390/include/asm/unistd.h | 8 -------- arch/sh/include/asm/unistd.h | 8 -------- arch/sparc/include/asm/unistd.h | 8 -------- arch/x86/include/asm/unistd.h | 8 -------- arch/xtensa/include/asm/unistd.h | 8 -------- include/asm-generic/unistd.h | 17 ----------------- include/linux/linkage.h | 21 +++++++++++++++++++++ include/linux/syscalls.h | 14 -------------- 29 files changed, 44 insertions(+), 209 deletions(-) create mode 100644 arch/powerpc/include/asm/linkage.h delete mode 100644 arch/powerpc/include/uapi/asm/linkage.h (limited to 'include') diff --git a/arch/alpha/include/asm/linkage.h b/arch/alpha/include/asm/linkage.h index 291c2d01c44..7cfd06e8c93 100644 --- a/arch/alpha/include/asm/linkage.h +++ b/arch/alpha/include/asm/linkage.h @@ -1,6 +1,8 @@ #ifndef __ASM_LINKAGE_H #define __ASM_LINKAGE_H -/* Nothing to see here... */ +#define cond_syscall(x) asm(".weak\t" #x "\n" #x " = sys_ni_syscall") +#define SYSCALL_ALIAS(alias, name) \ + asm ( #alias " = " #name "\n\t.globl " #alias) #endif diff --git a/arch/alpha/include/asm/unistd.h b/arch/alpha/include/asm/unistd.h index 6d6fe7ab547..43baee17acd 100644 --- a/arch/alpha/include/asm/unistd.h +++ b/arch/alpha/include/asm/unistd.h @@ -18,16 +18,4 @@ #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE -/* "Conditional" syscalls. What we want is - - __attribute__((weak,alias("sys_ni_syscall"))) - - but that raises the problem of what type to give the symbol. If we use - a prototype, it'll conflict with the definition given in this file and - others. If we use __typeof, we discover that not all symbols actually - have declarations. If we use no prototype, then we get warnings from - -Wstrict-prototypes. Ho hum. */ - -#define cond_syscall(x) asm(".weak\t" #x "\n" #x " = sys_ni_syscall") - #endif /* _ALPHA_UNISTD_H */ diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h index e4ddfb39ca3..141baa3f9a7 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h @@ -43,14 +43,6 @@ #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") - /* * Unimplemented (or alternatively implemented) syscalls */ diff --git a/arch/avr32/include/asm/unistd.h b/arch/avr32/include/asm/unistd.h index dc4d5a93111..c1eb080e45f 100644 --- a/arch/avr32/include/asm/unistd.h +++ b/arch/avr32/include/asm/unistd.h @@ -41,12 +41,4 @@ #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); - #endif /* __ASM_AVR32_UNISTD_H */ diff --git a/arch/blackfin/include/asm/unistd.h b/arch/blackfin/include/asm/unistd.h index 04e83ea8d5c..c35414bdf7b 100644 --- a/arch/blackfin/include/asm/unistd.h +++ b/arch/blackfin/include/asm/unistd.h @@ -20,12 +20,4 @@ #define __ARCH_WANT_SYS_NICE #define __ARCH_WANT_SYS_VFORK -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t_" #x "\n\t.set\t_" #x ",_sys_ni_syscall"); - #endif /* __ASM_BFIN_UNISTD_H */ diff --git a/arch/cris/include/asm/unistd.h b/arch/cris/include/asm/unistd.h index be57a988bfb..0ff3f688984 100644 --- a/arch/cris/include/asm/unistd.h +++ b/arch/cris/include/asm/unistd.h @@ -34,12 +34,4 @@ #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") - #endif /* _ASM_CRIS_UNISTD_H_ */ diff --git a/arch/frv/include/asm/unistd.h b/arch/frv/include/asm/unistd.h index 4cfcc7bba25..70ec7293dce 100644 --- a/arch/frv/include/asm/unistd.h +++ b/arch/frv/include/asm/unistd.h @@ -31,14 +31,4 @@ #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#ifndef cond_syscall -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") -#endif - #endif /* _ASM_UNISTD_H_ */ diff --git a/arch/h8300/include/asm/linkage.h b/arch/h8300/include/asm/linkage.h index 6f4df7d4618..1d81604fb0a 100644 --- a/arch/h8300/include/asm/linkage.h +++ b/arch/h8300/include/asm/linkage.h @@ -2,7 +2,5 @@ #define _H8300_LINKAGE_H #undef SYMBOL_NAME_LABEL -#undef SYMBOL_NAME #define SYMBOL_NAME_LABEL(_name_) _##_name_##: -#define SYMBOL_NAME(_name_) _##_name_ #endif diff --git a/arch/h8300/include/asm/unistd.h b/arch/h8300/include/asm/unistd.h index 6721856d841..ab671ecf519 100644 --- a/arch/h8300/include/asm/unistd.h +++ b/arch/h8300/include/asm/unistd.h @@ -33,11 +33,4 @@ #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE -/* - * "Conditional" syscalls - */ -#define cond_syscall(name) \ - asm (".weak\t_" #name "\n" \ - ".set\t_" #name ",_sys_ni_syscall"); - #endif /* _ASM_H8300_UNISTD_H_ */ diff --git a/arch/ia64/include/asm/linkage.h b/arch/ia64/include/asm/linkage.h index ef22a45c189..787575701f1 100644 --- a/arch/ia64/include/asm/linkage.h +++ b/arch/ia64/include/asm/linkage.h @@ -11,4 +11,8 @@ #endif +#define cond_syscall(x) asm(".weak\t" #x "#\n" #x "#\t=\tsys_ni_syscall#") +#define SYSCALL_ALIAS(alias, name) \ + asm ( #alias "# = " #name "#\n\t.globl " #alias "#") + #endif diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h index 096373800f7..afd45e0d552 100644 --- a/arch/ia64/include/asm/unistd.h +++ b/arch/ia64/include/asm/unistd.h @@ -46,15 +46,5 @@ asmlinkage unsigned long sys_mmap2( struct pt_regs; asmlinkage long sys_ia64_pipe(void); -/* - * "Conditional" syscalls - * - * Note, this macro can only be used in the file which defines sys_ni_syscall, i.e., in - * kernel/sys_ni.c. This version causes warnings because the declaration isn't a - * proper prototype, but we can't use __typeof__ either, because not all cond_syscall() - * declarations have prototypes at the moment. - */ -#define cond_syscall(x) asmlinkage long x (void) __attribute__((weak,alias("sys_ni_syscall"))) - #endif /* !__ASSEMBLY__ */ #endif /* _ASM_IA64_UNISTD_H */ diff --git a/arch/m32r/include/asm/unistd.h b/arch/m32r/include/asm/unistd.h index 555629b0526..59db8019345 100644 --- a/arch/m32r/include/asm/unistd.h +++ b/arch/m32r/include/asm/unistd.h @@ -48,14 +48,4 @@ #define __IGNORE_getresgid #define __IGNORE_chown -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#ifndef cond_syscall -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") -#endif - #endif /* _ASM_M32R_UNISTD_H */ diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h index 6cd92671ca5..014f288fc81 100644 --- a/arch/m68k/include/asm/unistd.h +++ b/arch/m68k/include/asm/unistd.h @@ -32,12 +32,4 @@ #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") - #endif /* _ASM_M68K_UNISTD_H_ */ diff --git a/arch/microblaze/include/asm/unistd.h b/arch/microblaze/include/asm/unistd.h index b3778391d9c..6dece2d002d 100644 --- a/arch/microblaze/include/asm/unistd.h +++ b/arch/microblaze/include/asm/unistd.h @@ -37,13 +37,5 @@ #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_FORK -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); - #endif /* __ASSEMBLY__ */ #endif /* _ASM_MICROBLAZE_UNISTD_H */ diff --git a/arch/mips/include/asm/linkage.h b/arch/mips/include/asm/linkage.h index e9a940d1b0c..2767dda9e30 100644 --- a/arch/mips/include/asm/linkage.h +++ b/arch/mips/include/asm/linkage.h @@ -6,5 +6,8 @@ #endif #define __weak __attribute__((weak)) +#define cond_syscall(x) asm(".weak\t" #x "\n" #x "\t=\tsys_ni_syscall") +#define SYSCALL_ALIAS(alias, name) \ + asm ( #alias " = " #name "\n\t.globl " #alias) #endif diff --git a/arch/mips/include/asm/unistd.h b/arch/mips/include/asm/unistd.h index 64f661e3287..63c9c886173 100644 --- a/arch/mips/include/asm/unistd.h +++ b/arch/mips/include/asm/unistd.h @@ -63,12 +63,4 @@ #endif /* !__ASSEMBLY__ */ -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n" #x "\t=\tsys_ni_syscall") - #endif /* _ASM_UNISTD_H */ diff --git a/arch/mn10300/include/asm/unistd.h b/arch/mn10300/include/asm/unistd.h index 7f9d9adfa51..9d4e2d1ef90 100644 --- a/arch/mn10300/include/asm/unistd.h +++ b/arch/mn10300/include/asm/unistd.h @@ -45,14 +45,4 @@ #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#ifndef cond_syscall -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); -#endif - #endif /* _ASM_UNISTD_H */ diff --git a/arch/parisc/include/asm/unistd.h b/arch/parisc/include/asm/unistd.h index ae9a46cbfd9..74d835820ee 100644 --- a/arch/parisc/include/asm/unistd.h +++ b/arch/parisc/include/asm/unistd.h @@ -170,12 +170,4 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ #undef STR -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") - #endif /* _ASM_PARISC_UNISTD_H_ */ diff --git a/arch/powerpc/include/asm/linkage.h b/arch/powerpc/include/asm/linkage.h new file mode 100644 index 00000000000..b36f650a13f --- /dev/null +++ b/arch/powerpc/include/asm/linkage.h @@ -0,0 +1,13 @@ +#ifndef _ASM_POWERPC_LINKAGE_H +#define _ASM_POWERPC_LINKAGE_H + +#ifdef CONFIG_PPC64 +#define cond_syscall(x) \ + asm ("\t.weak " #x "\n\t.set " #x ", sys_ni_syscall\n" \ + "\t.weak ." #x "\n\t.set ." #x ", .sys_ni_syscall\n") +#define SYSCALL_ALIAS(alias, name) \ + asm ("\t.globl " #alias "\n\t.set " #alias ", " #name "\n" \ + "\t.globl ." #alias "\n\t.set ." #alias ", ." #name) +#endif + +#endif /* _ASM_POWERPC_LINKAGE_H */ diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h index f25b5c45c43..91586d979c9 100644 --- a/arch/powerpc/include/asm/unistd.h +++ b/arch/powerpc/include/asm/unistd.h @@ -56,11 +56,5 @@ #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE -/* - * "Conditional" syscalls - */ -#define cond_syscall(x) \ - asmlinkage long x (void) __attribute__((weak,alias("sys_ni_syscall"))) - #endif /* __ASSEMBLY__ */ #endif /* _ASM_POWERPC_UNISTD_H_ */ diff --git a/arch/powerpc/include/uapi/asm/linkage.h b/arch/powerpc/include/uapi/asm/linkage.h deleted file mode 100644 index e1c4ac1cc4b..00000000000 --- a/arch/powerpc/include/uapi/asm/linkage.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_POWERPC_LINKAGE_H -#define _ASM_POWERPC_LINKAGE_H - -/* Nothing to see here... */ - -#endif /* _ASM_POWERPC_LINKAGE_H */ diff --git a/arch/s390/include/asm/unistd.h b/arch/s390/include/asm/unistd.h index a6667a95296..65188635355 100644 --- a/arch/s390/include/asm/unistd.h +++ b/arch/s390/include/asm/unistd.h @@ -54,12 +54,4 @@ #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") - #endif /* _ASM_S390_UNISTD_H_ */ diff --git a/arch/sh/include/asm/unistd.h b/arch/sh/include/asm/unistd.h index 5e90fa2b7ee..e77816c4b9b 100644 --- a/arch/sh/include/asm/unistd.h +++ b/arch/sh/include/asm/unistd.h @@ -30,12 +30,4 @@ # define __ARCH_WANT_SYS_VFORK # define __ARCH_WANT_SYS_CLONE -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -# define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") - #include diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h index 5356810bd7e..dfa53fdd5cb 100644 --- a/arch/sparc/include/asm/unistd.h +++ b/arch/sparc/include/asm/unistd.h @@ -45,12 +45,4 @@ #define __ARCH_WANT_COMPAT_SYS_SENDFILE #endif -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") - #endif /* _SPARC_UNISTD_H */ diff --git a/arch/x86/include/asm/unistd.h b/arch/x86/include/asm/unistd.h index 3d5df1c4447..c2a48139c34 100644 --- a/arch/x86/include/asm/unistd.h +++ b/arch/x86/include/asm/unistd.h @@ -50,12 +50,4 @@ # define __ARCH_WANT_SYS_VFORK # define __ARCH_WANT_SYS_CLONE -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -# define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") - #endif /* _ASM_X86_UNISTD_H */ diff --git a/arch/xtensa/include/asm/unistd.h b/arch/xtensa/include/asm/unistd.h index c38834de9ac..cb4c2ce8d44 100644 --- a/arch/xtensa/include/asm/unistd.h +++ b/arch/xtensa/include/asm/unistd.h @@ -4,14 +4,6 @@ #define __ARCH_WANT_SYS_CLONE #include -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); - #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_UTIME #define __ARCH_WANT_SYS_LLSEEK diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h index 4077b5d9ff8..0501fa3f783 100644 --- a/include/asm-generic/unistd.h +++ b/include/asm-generic/unistd.h @@ -9,20 +9,3 @@ #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_LLSEEK #endif - -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#ifndef cond_syscall -#ifdef CONFIG_SYMBOL_PREFIX -#define __SYMBOL_PREFIX CONFIG_SYMBOL_PREFIX -#else -#define __SYMBOL_PREFIX -#endif -#define cond_syscall(x) asm(".weak\t" __SYMBOL_PREFIX #x "\n\t" \ - ".set\t" __SYMBOL_PREFIX #x "," \ - __SYMBOL_PREFIX "sys_ni_syscall") -#endif diff --git a/include/linux/linkage.h b/include/linux/linkage.h index 807f1e53322..829d66c67fc 100644 --- a/include/linux/linkage.h +++ b/include/linux/linkage.h @@ -2,6 +2,7 @@ #define _LINUX_LINKAGE_H #include +#include #include #ifdef __cplusplus @@ -14,6 +15,26 @@ #define asmlinkage CPP_ASMLINKAGE #endif +#ifndef SYMBOL_NAME +#ifdef CONFIG_SYMBOL_PREFIX +#define SYMBOL_NAME(x) CONFIG_SYMBOL_PREFIX ## x +#else +#define SYMBOL_NAME(x) x +#endif +#endif +#define __SYMBOL_NAME(x) __stringify(SYMBOL_NAME(x)) + +#ifndef cond_syscall +#define cond_syscall(x) asm(".weak\t" __SYMBOL_NAME(x) \ + "\n\t.set\t" __SYMBOL_NAME(x) "," __SYMBOL_NAME(sys_ni_syscall)); +#endif + +#ifndef SYSCALL_ALIAS +#define SYSCALL_ALIAS(alias, name) \ + asm ("\t.globl " __SYMBOL_NAME(alias) \ + "\n\t.set\t" __SYMBOL_NAME(alias) "," __SYMBOL_NAME(name)) +#endif + #define __page_aligned_data __section(.data..page_aligned) __aligned(PAGE_SIZE) #define __page_aligned_bss __section(.bss..page_aligned) __aligned(PAGE_SIZE) diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 3e07b92efbf..87584373305 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -183,20 +183,6 @@ extern struct trace_event_functions exit_syscall_print_funcs; #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) -#ifdef CONFIG_PPC64 -#define SYSCALL_ALIAS(alias, name) \ - asm ("\t.globl " #alias "\n\t.set " #alias ", " #name "\n" \ - "\t.globl ." #alias "\n\t.set ." #alias ", ." #name) -#else -#if defined(CONFIG_ALPHA) || defined(CONFIG_MIPS) -#define SYSCALL_ALIAS(alias, name) \ - asm ( #alias " = " #name "\n\t.globl " #alias) -#else -#define SYSCALL_ALIAS(alias, name) \ - asm ("\t.globl " #alias "\n\t.set " #alias ", " #name) -#endif -#endif - #ifdef CONFIG_FTRACE_SYSCALLS #define SYSCALL_DEFINEx(x, sname, ...) \ static const char *types_##sname[] = { \ -- cgit v1.2.3-70-g09d2 From 22d1a35da0e247a006c286842a1846acb4ffed4f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 21 Jan 2013 17:18:07 -0500 Subject: make HAVE_SYSCALL_WRAPPERS unconditional Signed-off-by: Al Viro --- arch/Kconfig | 3 --- arch/alpha/Kconfig | 1 - arch/mips/Kconfig | 1 - arch/powerpc/Kconfig | 1 - arch/s390/Kconfig | 1 - arch/sparc/Kconfig | 1 - arch/tile/Kconfig | 1 - include/linux/compat.h | 9 --------- include/linux/syscalls.h | 10 ---------- ipc/sem.c | 2 -- 10 files changed, 30 deletions(-) (limited to 'include') diff --git a/arch/Kconfig b/arch/Kconfig index 5a1779c9394..892d6176fcf 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -157,9 +157,6 @@ config ARCH_USE_BUILTIN_BSWAP instructions should set this. And it shouldn't hurt to set it on architectures that don't have such instructions. -config HAVE_SYSCALL_WRAPPERS - bool - config KRETPROBES def_bool y depends on KPROBES && HAVE_KRETPROBES diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 5833aa44148..5469f7b444a 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -4,7 +4,6 @@ config ALPHA select HAVE_AOUT select HAVE_IDE select HAVE_OPROFILE - select HAVE_SYSCALL_WRAPPERS select HAVE_PCSPKR_PLATFORM select HAVE_PERF_EVENTS select HAVE_DMA_ATTRS diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index ae9c716c46b..32eb3d67bbe 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1737,7 +1737,6 @@ config 32BIT config 64BIT bool "64-bit kernel" depends on CPU_SUPPORTS_64BIT_KERNEL && SYS_SUPPORTS_64BIT_KERNEL - select HAVE_SYSCALL_WRAPPERS help Select this option if you want to build a 64-bit kernel. diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index b89d7eb730a..f460e32fe2a 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -113,7 +113,6 @@ config PPC select USE_GENERIC_SMP_HELPERS if SMP select HAVE_OPROFILE select HAVE_DEBUG_KMEMLEAK - select HAVE_SYSCALL_WRAPPERS if PPC64 select GENERIC_ATOMIC64 if PPC32 select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select HAVE_PERF_EVENTS diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 4b505370a1d..f6cc1528df8 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -131,7 +131,6 @@ config S390 select HAVE_PERF_EVENTS select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_SYSCALL_TRACEPOINTS - select HAVE_SYSCALL_WRAPPERS select HAVE_UID16 if 32BIT select HAVE_VIRT_CPU_ACCOUNTING select HAVE_VIRT_TO_BUS diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 289127d5241..67388cdb18c 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -62,7 +62,6 @@ config SPARC64 select HAVE_RCU_TABLE_FREE if SMP select HAVE_MEMBLOCK select HAVE_MEMBLOCK_NODE_MAP - select HAVE_SYSCALL_WRAPPERS select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index ff496ab1e79..95bd2ef6c94 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -16,7 +16,6 @@ config TILE select GENERIC_PENDING_IRQ if SMP select GENERIC_IRQ_SHOW select HAVE_DEBUG_BUGVERBOSE - select HAVE_SYSCALL_WRAPPERS if TILEGX select HAVE_VIRT_TO_BUS select SYS_HYPERVISOR select ARCH_HAVE_NMI_SAFE_CMPXCHG diff --git a/include/linux/compat.h b/include/linux/compat.h index 8c1dfc8d830..110132527e4 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -40,8 +40,6 @@ #define COMPAT_SYSCALL_DEFINE6(name, ...) \ COMPAT_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS - #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ @@ -52,13 +50,6 @@ SYSCALL_ALIAS(compat_sys##name, compat_SyS##name); \ static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) -#else /* CONFIG_HAVE_SYSCALL_WRAPPERS */ - -#define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ - asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) - -#endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */ - #ifndef compat_user_stack_pointer #define compat_user_stack_pointer() current_user_stack_pointer() #endif diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 87584373305..3b6fc13cb46 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -198,8 +198,6 @@ extern struct trace_event_functions exit_syscall_print_funcs; __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) #endif -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS - #define SYSCALL_DEFINE(name) static inline long SYSC_##name #define __SYSCALL_DEFINEx(x, name, ...) \ @@ -213,14 +211,6 @@ extern struct trace_event_functions exit_syscall_print_funcs; SYSCALL_ALIAS(sys##name, SyS##name); \ static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) -#else /* CONFIG_HAVE_SYSCALL_WRAPPERS */ - -#define SYSCALL_DEFINE(name) asmlinkage long sys_##name -#define __SYSCALL_DEFINEx(x, name, ...) \ - asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) - -#endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */ - asmlinkage long sys_time(time_t __user *tloc); asmlinkage long sys_stime(time_t __user *tptr); asmlinkage long sys_gettimeofday(struct timeval __user *tv, diff --git a/ipc/sem.c b/ipc/sem.c index 58d31f1c1eb..e7236df7a47 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1156,13 +1156,11 @@ SYSCALL_DEFINE(semctl)(int semid, int semnum, int cmd, union semun arg) return -EINVAL; } } -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS asmlinkage long SyS_semctl(int semid, int semnum, int cmd, union semun arg) { return SYSC_semctl((int) semid, (int) semnum, (int) cmd, arg); } SYSCALL_ALIAS(sys_semctl, SyS_semctl); -#endif /* If the task doesn't already have a undo_list, then allocate one * here. We guarantee there is only one thread using this undo list, -- cgit v1.2.3-70-g09d2 From 2cf0966683430b6468f36ca20515a33ca7f2403c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 21 Jan 2013 15:25:54 -0500 Subject: make SYSCALL_DEFINE-generated wrappers do asmlinkage_protect ... and switch i386 to HAVE_SYSCALL_WRAPPERS, killing open-coded uses of asmlinkage_protect() in a bunch of syscalls. Signed-off-by: Al Viro --- arch/x86/include/asm/syscalls.h | 4 +-- arch/x86/kernel/tls.c | 14 ++++------- arch/x86/um/tls_32.c | 5 ++-- fs/aio.c | 2 -- fs/open.c | 24 +++--------------- include/linux/syscalls.h | 6 ++++- kernel/exit.c | 5 ---- kernel/fork.c | 5 +--- kernel/uid16.c | 55 +++++++++-------------------------------- 9 files changed, 31 insertions(+), 89 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/syscalls.h b/arch/x86/include/asm/syscalls.h index 6cf0a9cc60c..5f87b35fd2e 100644 --- a/arch/x86/include/asm/syscalls.h +++ b/arch/x86/include/asm/syscalls.h @@ -27,8 +27,8 @@ asmlinkage int sys_modify_ldt(int, void __user *, unsigned long); long sys_rt_sigreturn(void); /* kernel/tls.c */ -asmlinkage int sys_set_thread_area(struct user_desc __user *); -asmlinkage int sys_get_thread_area(struct user_desc __user *); +asmlinkage long sys_set_thread_area(struct user_desc __user *); +asmlinkage long sys_get_thread_area(struct user_desc __user *); /* X86_32 only */ #ifdef CONFIG_X86_32 diff --git a/arch/x86/kernel/tls.c b/arch/x86/kernel/tls.c index 9d9d2f9e77a..f7fec09e3e3 100644 --- a/arch/x86/kernel/tls.c +++ b/arch/x86/kernel/tls.c @@ -3,13 +3,13 @@ #include #include #include +#include #include #include #include #include #include -#include #include "tls.h" @@ -89,11 +89,9 @@ int do_set_thread_area(struct task_struct *p, int idx, return 0; } -asmlinkage int sys_set_thread_area(struct user_desc __user *u_info) +SYSCALL_DEFINE1(set_thread_area, struct user_desc __user *, u_info) { - int ret = do_set_thread_area(current, -1, u_info, 1); - asmlinkage_protect(1, ret, u_info); - return ret; + return do_set_thread_area(current, -1, u_info, 1); } @@ -139,11 +137,9 @@ int do_get_thread_area(struct task_struct *p, int idx, return 0; } -asmlinkage int sys_get_thread_area(struct user_desc __user *u_info) +SYSCALL_DEFINE1(get_thread_area, struct user_desc __user *, u_info) { - int ret = do_get_thread_area(current, -1, u_info); - asmlinkage_protect(1, ret, u_info); - return ret; + return do_get_thread_area(current, -1, u_info); } int regset_tls_active(struct task_struct *target, diff --git a/arch/x86/um/tls_32.c b/arch/x86/um/tls_32.c index 5f5feff3d24..80ffa5b9982 100644 --- a/arch/x86/um/tls_32.c +++ b/arch/x86/um/tls_32.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -274,7 +275,7 @@ clear: goto out; } -int sys_set_thread_area(struct user_desc __user *user_desc) +SYSCALL_DEFINE1(set_thread_area, struct user_desc __user *, user_desc) { struct user_desc info; int idx, ret; @@ -322,7 +323,7 @@ int ptrace_set_thread_area(struct task_struct *child, int idx, return set_tls_entry(child, &info, idx, 0); } -int sys_get_thread_area(struct user_desc __user *user_desc) +SYSCALL_DEFINE1(get_thread_area, struct user_desc __user *, user_desc) { struct user_desc info; int idx, ret; diff --git a/fs/aio.c b/fs/aio.c index 3f941f2a305..c3ebb98a527 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1790,7 +1790,5 @@ SYSCALL_DEFINE5(io_getevents, aio_context_t, ctx_id, ret = read_events(ioctx, min_nr, nr, events, timeout); put_ioctx(ioctx); } - - asmlinkage_protect(5, ret, ctx_id, min_nr, nr, events, timeout); return ret; } diff --git a/fs/open.c b/fs/open.c index a5392245044..8c741002f94 100644 --- a/fs/open.c +++ b/fs/open.c @@ -197,10 +197,7 @@ out: SYSCALL_DEFINE2(ftruncate, unsigned int, fd, unsigned long, length) { - long ret = do_sys_ftruncate(fd, length, 1); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(2, ret, fd, length); - return ret; + return do_sys_ftruncate(fd, length, 1); } #ifdef CONFIG_COMPAT @@ -219,10 +216,7 @@ SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, length) SYSCALL_DEFINE2(ftruncate64, unsigned int, fd, loff_t, length) { - long ret = do_sys_ftruncate(fd, length, 0); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(2, ret, fd, length); - return ret; + return do_sys_ftruncate(fd, length, 0); } #endif /* BITS_PER_LONG == 32 */ @@ -961,29 +955,19 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) { - long ret; - if (force_o_largefile()) flags |= O_LARGEFILE; - ret = do_sys_open(AT_FDCWD, filename, flags, mode); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(3, ret, filename, flags, mode); - return ret; + return do_sys_open(AT_FDCWD, filename, flags, mode); } SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, umode_t, mode) { - long ret; - if (force_o_largefile()) flags |= O_LARGEFILE; - ret = do_sys_open(dfd, filename, flags, mode); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(4, ret, dfd, filename, flags, mode); - return ret; + return do_sys_open(dfd, filename, flags, mode); } #ifndef __alpha__ diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 3b6fc13cb46..9660a8bdcbb 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -99,6 +99,7 @@ struct sigaltstack; #define __TYPE_IS_LL(t) (__same_type((t)0, 0LL) || __same_type((t)0, 0ULL)) #define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a #define __SC_CAST(t, a) (t) a +#define __SC_ARGS(t, a) a #define __SC_TEST(t, a) (void)BUILD_BUG_ON_ZERO(!__TYPE_IS_LL(t) && sizeof(t) > sizeof(long)) #ifdef CONFIG_FTRACE_SYSCALLS @@ -200,13 +201,16 @@ extern struct trace_event_functions exit_syscall_print_funcs; #define SYSCALL_DEFINE(name) static inline long SYSC_##name +#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__) #define __SYSCALL_DEFINEx(x, name, ...) \ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \ + long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ __MAP(x,__SC_TEST,__VA_ARGS__); \ - return SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ } \ SYSCALL_ALIAS(sys##name, SyS##name); \ static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) diff --git a/kernel/exit.c b/kernel/exit.c index 51e485ca993..25d0108d745 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1629,9 +1629,6 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, } put_pid(pid); - - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(5, ret, which, upid, infop, options, ru); return ret; } @@ -1669,8 +1666,6 @@ SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr, ret = do_wait(&wo); put_pid(pid); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(4, ret, upid, stat_addr, options, ru); return ret; } diff --git a/kernel/fork.c b/kernel/fork.c index 8d932b1c905..e1f34abe588 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1674,10 +1674,7 @@ SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, int, tls_val) #endif { - long ret = do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr); - asmlinkage_protect(5, ret, clone_flags, newsp, - parent_tidptr, child_tidptr, tls_val); - return ret; + return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr); } #endif diff --git a/kernel/uid16.c b/kernel/uid16.c index d7948eb1022..f6c83d7ef00 100644 --- a/kernel/uid16.c +++ b/kernel/uid16.c @@ -18,67 +18,43 @@ SYSCALL_DEFINE3(chown16, const char __user *, filename, old_uid_t, user, old_gid_t, group) { - long ret = sys_chown(filename, low2highuid(user), low2highgid(group)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(3, ret, filename, user, group); - return ret; + return sys_chown(filename, low2highuid(user), low2highgid(group)); } SYSCALL_DEFINE3(lchown16, const char __user *, filename, old_uid_t, user, old_gid_t, group) { - long ret = sys_lchown(filename, low2highuid(user), low2highgid(group)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(3, ret, filename, user, group); - return ret; + return sys_lchown(filename, low2highuid(user), low2highgid(group)); } SYSCALL_DEFINE3(fchown16, unsigned int, fd, old_uid_t, user, old_gid_t, group) { - long ret = sys_fchown(fd, low2highuid(user), low2highgid(group)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(3, ret, fd, user, group); - return ret; + return sys_fchown(fd, low2highuid(user), low2highgid(group)); } SYSCALL_DEFINE2(setregid16, old_gid_t, rgid, old_gid_t, egid) { - long ret = sys_setregid(low2highgid(rgid), low2highgid(egid)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(2, ret, rgid, egid); - return ret; + return sys_setregid(low2highgid(rgid), low2highgid(egid)); } SYSCALL_DEFINE1(setgid16, old_gid_t, gid) { - long ret = sys_setgid(low2highgid(gid)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(1, ret, gid); - return ret; + return sys_setgid(low2highgid(gid)); } SYSCALL_DEFINE2(setreuid16, old_uid_t, ruid, old_uid_t, euid) { - long ret = sys_setreuid(low2highuid(ruid), low2highuid(euid)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(2, ret, ruid, euid); - return ret; + return sys_setreuid(low2highuid(ruid), low2highuid(euid)); } SYSCALL_DEFINE1(setuid16, old_uid_t, uid) { - long ret = sys_setuid(low2highuid(uid)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(1, ret, uid); - return ret; + return sys_setuid(low2highuid(uid)); } SYSCALL_DEFINE3(setresuid16, old_uid_t, ruid, old_uid_t, euid, old_uid_t, suid) { - long ret = sys_setresuid(low2highuid(ruid), low2highuid(euid), + return sys_setresuid(low2highuid(ruid), low2highuid(euid), low2highuid(suid)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(3, ret, ruid, euid, suid); - return ret; } SYSCALL_DEFINE3(getresuid16, old_uid_t __user *, ruidp, old_uid_t __user *, euidp, old_uid_t __user *, suidp) @@ -100,11 +76,8 @@ SYSCALL_DEFINE3(getresuid16, old_uid_t __user *, ruidp, old_uid_t __user *, euid SYSCALL_DEFINE3(setresgid16, old_gid_t, rgid, old_gid_t, egid, old_gid_t, sgid) { - long ret = sys_setresgid(low2highgid(rgid), low2highgid(egid), + return sys_setresgid(low2highgid(rgid), low2highgid(egid), low2highgid(sgid)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(3, ret, rgid, egid, sgid); - return ret; } @@ -127,18 +100,12 @@ SYSCALL_DEFINE3(getresgid16, old_gid_t __user *, rgidp, old_gid_t __user *, egid SYSCALL_DEFINE1(setfsuid16, old_uid_t, uid) { - long ret = sys_setfsuid(low2highuid(uid)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(1, ret, uid); - return ret; + return sys_setfsuid(low2highuid(uid)); } SYSCALL_DEFINE1(setfsgid16, old_gid_t, gid) { - long ret = sys_setfsgid(low2highgid(gid)); - /* avoid REGPARM breakage on x86: */ - asmlinkage_protect(1, ret, gid); - return ret; + return sys_setfsgid(low2highgid(gid)); } static int groups16_to_user(old_gid_t __user *grouplist, -- cgit v1.2.3-70-g09d2 From 19f4fc3aee180000fe45952691bbe69dde1d9e95 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 24 Feb 2013 02:17:03 -0500 Subject: convert sendfile{,64} to COMPAT_SYSCALL_DEFINE Signed-off-by: Al Viro --- arch/mips/kernel/linux32.c | 20 ----------------- arch/mips/kernel/scall64-n32.S | 2 +- arch/mips/kernel/scall64-o32.S | 2 +- arch/parisc/kernel/sys_parisc32.c | 19 ---------------- arch/parisc/kernel/syscall_table.S | 4 ++-- arch/powerpc/include/asm/systbl.h | 4 ++-- arch/powerpc/kernel/sys_ppc32.c | 18 ---------------- arch/s390/kernel/compat_linux.c | 42 ------------------------------------ arch/s390/kernel/compat_linux.h | 4 ---- arch/s390/kernel/compat_wrapper.S | 14 ------------ arch/s390/kernel/syscalls.S | 4 ++-- arch/sparc/kernel/sys32.S | 1 - arch/sparc/kernel/systbls_64.S | 2 +- arch/x86/ia32/sys_ia32.c | 20 ----------------- arch/x86/include/asm/sys_ia32.h | 1 - arch/x86/syscalls/syscall_32.tbl | 2 +- fs/compat.c | 22 ------------------- fs/read_write.c | 44 ++++++++++++++++++++++++++++++++++++-- fs/read_write.h | 2 -- include/linux/compat.h | 2 ++ 20 files changed, 54 insertions(+), 175 deletions(-) (limited to 'include') diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index 8eeee1c860c..b0cc2a7df59 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -226,26 +226,6 @@ SYSCALL_DEFINE1(32_personality, unsigned long, personality) return ret; } -SYSCALL_DEFINE4(32_sendfile, long, out_fd, long, in_fd, - compat_off_t __user *, offset, s32, count) -{ - mm_segment_t old_fs = get_fs(); - int ret; - off_t of; - - if (offset && get_user(of, offset)) - return -EFAULT; - - set_fs(KERNEL_DS); - ret = sys_sendfile(out_fd, in_fd, offset ? (off_t __user *)&of : NULL, count); - set_fs(old_fs); - - if (offset && put_user(of, offset)) - return -EFAULT; - - return ret; -} - asmlinkage ssize_t sys32_readahead(int fd, u32 pad0, u64 a2, u64 a3, size_t count) { diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 693d60b0855..9b4df498fc5 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -143,7 +143,7 @@ EXPORT(sysn32_call_table) PTR compat_sys_setitimer PTR sys_alarm PTR sys_getpid - PTR sys_32_sendfile + PTR compat_sys_sendfile PTR sys_socket /* 6040 */ PTR sys_connect PTR sys_accept diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index af8887f779f..c1a70e80575 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -399,7 +399,7 @@ sys_call_table: PTR sys_capget PTR sys_capset /* 4205 */ PTR compat_sys_sigaltstack - PTR sys_32_sendfile + PTR compat_sys_sendfile PTR sys_ni_syscall PTR sys_ni_syscall PTR sys_mips_mmap2 /* 4210 */ diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c index 051c8b90231..035ab3f9481 100644 --- a/arch/parisc/kernel/sys_parisc32.c +++ b/arch/parisc/kernel/sys_parisc32.c @@ -60,25 +60,6 @@ asmlinkage long sys32_unimplemented(int r26, int r25, int r24, int r23, return -ENOSYS; } -/* Note: it is necessary to treat out_fd and in_fd as unsigned ints, with the - * corresponding cast to a signed int to insure that the proper conversion - * (sign extension) between the register representation of a signed int (msr in - * 32-bit mode) and the register representation of a signed int (msr in 64-bit - * mode) is performed. - */ -asmlinkage long sys32_sendfile(u32 out_fd, u32 in_fd, - compat_off_t __user *offset, compat_size_t count) -{ - return compat_sys_sendfile((int)out_fd, (int)in_fd, offset, count); -} - -asmlinkage long sys32_sendfile64(u32 out_fd, u32 in_fd, - compat_loff_t __user *offset, compat_size_t count) -{ - return sys_sendfile64((int)out_fd, (int)in_fd, - (loff_t __user *)offset, count); -} - asmlinkage long sys32_semctl(int semid, int semnum, int cmd, union semun arg) { union semun u; diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index f57dc137b8d..f232672a9e2 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -198,7 +198,7 @@ ENTRY_SAME(madvise) ENTRY_SAME(clone_wrapper) /* 120 */ ENTRY_SAME(setdomainname) - ENTRY_DIFF(sendfile) + ENTRY_COMP(sendfile) /* struct sockaddr... */ ENTRY_SAME(recvfrom) /* struct timex contains longs */ @@ -304,7 +304,7 @@ ENTRY_SAME(gettid) ENTRY_OURS(readahead) ENTRY_SAME(tkill) - ENTRY_DIFF(sendfile64) + ENTRY_COMP(sendfile64) ENTRY_COMP(futex) /* 210 */ ENTRY_COMP(sched_setaffinity) ENTRY_COMP(sched_getaffinity) diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h index 535b6d8a41c..634db7d2dc9 100644 --- a/arch/powerpc/include/asm/systbl.h +++ b/arch/powerpc/include/asm/systbl.h @@ -190,7 +190,7 @@ SYSCALL_SPU(getcwd) SYSCALL_SPU(capget) SYSCALL_SPU(capset) COMPAT_SYS(sigaltstack) -SYSX_SPU(sys_sendfile,compat_sys_sendfile_wrapper,sys_sendfile) +COMPAT_SYS_SPU(sendfile) SYSCALL(ni_syscall) SYSCALL(ni_syscall) PPC_SYS(vfork) @@ -230,7 +230,7 @@ COMPAT_SYS_SPU(sched_setaffinity) COMPAT_SYS_SPU(sched_getaffinity) SYSCALL(ni_syscall) SYSCALL(ni_syscall) -SYSX(sys_ni_syscall,compat_sys_sendfile64_wrapper,sys_sendfile64) +SYS32ONLY(sendfile64) COMPAT_SYS_SPU(io_setup) SYSCALL_SPU(io_destroy) COMPAT_SYS_SPU(io_getevents) diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index d0bafc0cdf0..6e7c2509bd2 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -128,24 +128,6 @@ long compat_sys_ipc(u32 call, u32 first, u32 second, u32 third, compat_uptr_t pt } #endif -/* Note: it is necessary to treat out_fd and in_fd as unsigned ints, - * with the corresponding cast to a signed int to insure that the - * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) - * and the register representation of a signed int (msr in 64-bit mode) is performed. - */ -asmlinkage long compat_sys_sendfile_wrapper(u32 out_fd, u32 in_fd, - compat_off_t __user *offset, u32 count) -{ - return compat_sys_sendfile((int)out_fd, (int)in_fd, offset, count); -} - -asmlinkage long compat_sys_sendfile64_wrapper(u32 out_fd, u32 in_fd, - compat_loff_t __user *offset, u32 count) -{ - return sys_sendfile((int)out_fd, (int)in_fd, - (off_t __user *)offset, count); -} - unsigned long compat_sys_mmap2(unsigned long addr, size_t len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff) diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c index 19f26de27fa..fbd29c70a29 100644 --- a/arch/s390/kernel/compat_linux.c +++ b/arch/s390/kernel/compat_linux.c @@ -373,48 +373,6 @@ asmlinkage compat_ssize_t sys32_readahead(int fd, u32 offhi, u32 offlo, s32 coun return sys_readahead(fd, ((loff_t)AA(offhi) << 32) | AA(offlo), count); } -asmlinkage long sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, size_t count) -{ - mm_segment_t old_fs = get_fs(); - int ret; - off_t of; - - if (offset && get_user(of, offset)) - return -EFAULT; - - set_fs(KERNEL_DS); - ret = sys_sendfile(out_fd, in_fd, - offset ? (off_t __force __user *) &of : NULL, count); - set_fs(old_fs); - - if (offset && put_user(of, offset)) - return -EFAULT; - - return ret; -} - -asmlinkage long sys32_sendfile64(int out_fd, int in_fd, - compat_loff_t __user *offset, s32 count) -{ - mm_segment_t old_fs = get_fs(); - int ret; - loff_t lof; - - if (offset && get_user(lof, offset)) - return -EFAULT; - - set_fs(KERNEL_DS); - ret = sys_sendfile64(out_fd, in_fd, - offset ? (loff_t __force __user *) &lof : NULL, - count); - set_fs(old_fs); - - if (offset && put_user(lof, offset)) - return -EFAULT; - - return ret; -} - struct stat64_emu31 { unsigned long long st_dev; unsigned int __pad1; diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h index 00d92a5a6f6..bce0b7aec8f 100644 --- a/arch/s390/kernel/compat_linux.h +++ b/arch/s390/kernel/compat_linux.h @@ -106,10 +106,6 @@ long sys32_pread64(unsigned int fd, char __user *ubuf, size_t count, long sys32_pwrite64(unsigned int fd, const char __user *ubuf, size_t count, u32 poshi, u32 poslo); compat_ssize_t sys32_readahead(int fd, u32 offhi, u32 offlo, s32 count); -long sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, - size_t count); -long sys32_sendfile64(int out_fd, int in_fd, compat_loff_t __user *offset, - s32 count); long sys32_stat64(const char __user * filename, struct stat64_emu31 __user * statbuf); long sys32_lstat64(const char __user * filename, struct stat64_emu31 __user * statbuf); diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index 626cc6f0f44..a1dda9c67ef 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -666,13 +666,6 @@ ENTRY(sys32_capset_wrapper) llgtr %r3,%r3 # const cap_user_data_t jg sys_capset # branch to system call -ENTRY(sys32_sendfile_wrapper) - lgfr %r2,%r2 # int - lgfr %r3,%r3 # int - llgtr %r4,%r4 # __kernel_off_emu31_t * - llgfr %r5,%r5 # size_t - jg sys32_sendfile # branch to system call - #sys32_vfork_wrapper # done in vfork_glue ENTRY(sys32_truncate64_wrapper) @@ -1348,13 +1341,6 @@ ENTRY(sys32_readahead_wrapper) lgfr %r5,%r5 # s32 jg sys32_readahead # branch to system call -ENTRY(sys32_sendfile64_wrapper) - lgfr %r2,%r2 # int - lgfr %r3,%r3 # int - llgtr %r4,%r4 # compat_loff_t * - lgfr %r5,%r5 # s32 - jg sys32_sendfile64 # branch to system call - ENTRY(sys_tkill_wrapper) lgfr %r2,%r2 # pid_t lgfr %r3,%r3 # int diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index 2695bb89699..5f3f7fbc546 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -195,7 +195,7 @@ SYSCALL(sys_getcwd,sys_getcwd,sys32_getcwd_wrapper) SYSCALL(sys_capget,sys_capget,sys32_capget_wrapper) SYSCALL(sys_capset,sys_capset,sys32_capset_wrapper) /* 185 */ SYSCALL(sys_sigaltstack,sys_sigaltstack,compat_sys_sigaltstack) -SYSCALL(sys_sendfile,sys_sendfile64,sys32_sendfile_wrapper) +SYSCALL(sys_sendfile,sys_sendfile64,compat_sys_sendfile) NI_SYSCALL /* streams1 */ NI_SYSCALL /* streams2 */ SYSCALL(sys_vfork,sys_vfork,sys_vfork) /* 190 */ @@ -231,7 +231,7 @@ SYSCALL(sys_madvise,sys_madvise,sys32_madvise_wrapper) SYSCALL(sys_getdents64,sys_getdents64,sys32_getdents64_wrapper) /* 220 */ SYSCALL(sys_fcntl64,sys_ni_syscall,compat_sys_fcntl64_wrapper) SYSCALL(sys_readahead,sys_readahead,sys32_readahead_wrapper) -SYSCALL(sys_sendfile64,sys_ni_syscall,sys32_sendfile64_wrapper) +SYSCALL(sys_sendfile64,sys_ni_syscall,compat_sys_sendfile64) SYSCALL(sys_setxattr,sys_setxattr,sys32_setxattr_wrapper) SYSCALL(sys_lsetxattr,sys_lsetxattr,sys32_lsetxattr_wrapper) /* 225 */ SYSCALL(sys_fsetxattr,sys_fsetxattr,sys32_fsetxattr_wrapper) diff --git a/arch/sparc/kernel/sys32.S b/arch/sparc/kernel/sys32.S index 240a3cecc11..6c65d69c663 100644 --- a/arch/sparc/kernel/sys32.S +++ b/arch/sparc/kernel/sys32.S @@ -46,7 +46,6 @@ SIGN1(sys32_io_submit, compat_sys_io_submit, %o1) SIGN1(sys32_mq_open, compat_sys_mq_open, %o1) SIGN1(sys32_select, compat_sys_select, %o0) SIGN3(sys32_futex, compat_sys_futex, %o1, %o2, %o5) -SIGN2(sys32_sendfile, compat_sys_sendfile, %o0, %o1) SIGN1(sys32_recvfrom, compat_sys_recvfrom, %o0) SIGN1(sys32_recvmsg, compat_sys_recvmsg, %o0) SIGN1(sys32_sendmsg, compat_sys_sendmsg, %o0) diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S index 088134834da..a1444d0d08e 100644 --- a/arch/sparc/kernel/systbls_64.S +++ b/arch/sparc/kernel/systbls_64.S @@ -25,7 +25,7 @@ sys_call_table32: /*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16 /*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, compat_sys_sigaltstack, sys_pause /*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys_access, sys_nice - .word sys_chown, sys_sync, sys_kill, compat_sys_newstat, sys32_sendfile + .word sys_chown, sys_sync, sys_kill, compat_sys_newstat, compat_sys_sendfile /*40*/ .word compat_sys_newlstat, sys_dup, sys_sparc_pipe, compat_sys_times, sys_getuid .word sys_umount, sys_setgid16, sys_getgid16, sys_signal, sys_geteuid16 /*50*/ .word sys_getegid16, sys_acct, sys_nis_syscall, sys_getgid, compat_sys_ioctl diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c index ad7a20cbc69..ad6ca047272 100644 --- a/arch/x86/ia32/sys_ia32.c +++ b/arch/x86/ia32/sys_ia32.c @@ -194,26 +194,6 @@ asmlinkage long sys32_pwrite(unsigned int fd, const char __user *ubuf, } -asmlinkage long sys32_sendfile(int out_fd, int in_fd, - compat_off_t __user *offset, s32 count) -{ - mm_segment_t old_fs = get_fs(); - int ret; - off_t of; - - if (offset && get_user(of, offset)) - return -EFAULT; - - set_fs(KERNEL_DS); - ret = sys_sendfile(out_fd, in_fd, offset ? (off_t __user *)&of : NULL, - count); - set_fs(old_fs); - - if (offset && put_user(of, offset)) - return -EFAULT; - return ret; -} - /* * Some system calls that need sign extended arguments. This could be * done by a generic wrapper. diff --git a/arch/x86/include/asm/sys_ia32.h b/arch/x86/include/asm/sys_ia32.h index 8459efc3968..6d944e4bb52 100644 --- a/arch/x86/include/asm/sys_ia32.h +++ b/arch/x86/include/asm/sys_ia32.h @@ -41,7 +41,6 @@ asmlinkage long sys32_pread(unsigned int, char __user *, u32, u32, u32); asmlinkage long sys32_pwrite(unsigned int, const char __user *, u32, u32, u32); asmlinkage long sys32_personality(unsigned long); -asmlinkage long sys32_sendfile(int, int, compat_off_t __user *, s32); long sys32_kill(int, int); long sys32_fadvise64_64(int, __u32, __u32, __u32, __u32, int); diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl index e6d55f0064d..6a00b1257d6 100644 --- a/arch/x86/syscalls/syscall_32.tbl +++ b/arch/x86/syscalls/syscall_32.tbl @@ -193,7 +193,7 @@ 184 i386 capget sys_capget 185 i386 capset sys_capset 186 i386 sigaltstack sys_sigaltstack compat_sys_sigaltstack -187 i386 sendfile sys_sendfile sys32_sendfile +187 i386 sendfile sys_sendfile compat_sys_sendfile 188 i386 getpmsg 189 i386 putpmsg 190 i386 vfork sys_vfork stub32_vfork diff --git a/fs/compat.c b/fs/compat.c index cc09312f9ae..2ae2a98891c 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1718,25 +1718,3 @@ COMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd, return do_handle_open(mountdirfd, handle, flags); } #endif - -#ifdef __ARCH_WANT_COMPAT_SYS_SENDFILE -asmlinkage long compat_sys_sendfile(int out_fd, int in_fd, - compat_off_t __user *offset, compat_size_t count) -{ - loff_t pos; - off_t off; - ssize_t ret; - - if (offset) { - if (unlikely(get_user(off, offset))) - return -EFAULT; - pos = off; - ret = do_sendfile(out_fd, in_fd, &pos, count, MAX_NON_LFS); - if (unlikely(put_user(pos, offset))) - return -EFAULT; - return ret; - } - - return do_sendfile(out_fd, in_fd, NULL, count, 0); -} -#endif /* __ARCH_WANT_COMPAT_SYS_SENDFILE */ diff --git a/fs/read_write.c b/fs/read_write.c index dcfd58d95f4..f738e4dccfa 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -853,8 +853,8 @@ SYSCALL_DEFINE5(pwritev, unsigned long, fd, const struct iovec __user *, vec, return ret; } -ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, size_t count, - loff_t max) +static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, + size_t count, loff_t max) { struct fd in, out; struct inode *in_inode, *out_inode; @@ -978,3 +978,43 @@ SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, si return do_sendfile(out_fd, in_fd, NULL, count, 0); } + +#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE4(sendfile, int, out_fd, int, in_fd, + compat_off_t __user *, offset, compat_size_t, count) +{ + loff_t pos; + off_t off; + ssize_t ret; + + if (offset) { + if (unlikely(get_user(off, offset))) + return -EFAULT; + pos = off; + ret = do_sendfile(out_fd, in_fd, &pos, count, MAX_NON_LFS); + if (unlikely(put_user(pos, offset))) + return -EFAULT; + return ret; + } + + return do_sendfile(out_fd, in_fd, NULL, count, 0); +} + +COMPAT_SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, + compat_loff_t __user *, offset, compat_size_t, count) +{ + loff_t pos; + ssize_t ret; + + if (offset) { + if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t)))) + return -EFAULT; + ret = do_sendfile(out_fd, in_fd, &pos, count, 0); + if (unlikely(put_user(pos, offset))) + return -EFAULT; + return ret; + } + + return do_sendfile(out_fd, in_fd, NULL, count, 0); +} +#endif diff --git a/fs/read_write.h b/fs/read_write.h index d3e00ef6742..d07b954c6e0 100644 --- a/fs/read_write.h +++ b/fs/read_write.h @@ -12,5 +12,3 @@ ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov, unsigned long nr_segs, size_t len, loff_t *ppos, iov_fn_t fn); ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov, unsigned long nr_segs, loff_t *ppos, io_fn_t fn); -ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, size_t count, - loff_t max); diff --git a/include/linux/compat.h b/include/linux/compat.h index 110132527e4..ad299afcd48 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -670,6 +670,8 @@ asmlinkage ssize_t compat_sys_process_vm_writev(compat_pid_t pid, asmlinkage long compat_sys_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, compat_size_t count); +asmlinkage long compat_sys_sendfile64(int out_fd, int in_fd, + compat_loff_t __user *offset, compat_size_t count); asmlinkage long compat_sys_sigaltstack(const compat_stack_t __user *uss_ptr, compat_stack_t __user *uoss_ptr); -- cgit v1.2.3-70-g09d2 From 35280bd4a3fa841897e2638437607fdec6c34f31 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 24 Feb 2013 14:52:17 -0500 Subject: switch epoll_pwait to COMPAT_SYSCALL_DEFINE Signed-off-by: Al Viro --- arch/s390/kernel/compat_wrapper.S | 10 -------- arch/s390/kernel/syscalls.S | 2 +- fs/compat.c | 49 --------------------------------------- fs/eventpoll.c | 47 +++++++++++++++++++++++++++++++++++++ include/linux/compat.h | 5 ++-- 5 files changed, 50 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index a1dda9c67ef..52bea71d93e 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -1270,16 +1270,6 @@ ENTRY(sys_getcpu_wrapper) llgtr %r4,%r4 # struct getcpu_cache * jg sys_getcpu -ENTRY(compat_sys_epoll_pwait_wrapper) - lgfr %r2,%r2 # int - llgtr %r3,%r3 # struct compat_epoll_event * - lgfr %r4,%r4 # int - lgfr %r5,%r5 # int - llgtr %r6,%r6 # compat_sigset_t * - llgf %r0,164(%r15) # compat_size_t - stg %r0,160(%r15) - jg compat_sys_epoll_pwait - ENTRY(compat_sys_utimes_wrapper) llgtr %r2,%r2 # char * llgtr %r3,%r3 # struct compat_timeval * diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index 5f3f7fbc546..63d6b434319 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -320,7 +320,7 @@ SYSCALL(sys_tee,sys_tee,sys_tee_wrapper) SYSCALL(sys_vmsplice,sys_vmsplice,compat_sys_vmsplice_wrapper) NI_SYSCALL /* 310 sys_move_pages */ SYSCALL(sys_getcpu,sys_getcpu,sys_getcpu_wrapper) -SYSCALL(sys_epoll_pwait,sys_epoll_pwait,compat_sys_epoll_pwait_wrapper) +SYSCALL(sys_epoll_pwait,sys_epoll_pwait,compat_sys_epoll_pwait) SYSCALL(sys_utimes,sys_utimes,compat_sys_utimes_wrapper) SYSCALL(sys_s390_fallocate,sys_fallocate,sys_fallocate_wrapper) SYSCALL(sys_utimensat,sys_utimensat,compat_sys_utimensat_wrapper) /* 315 */ diff --git a/fs/compat.c b/fs/compat.c index 2ae2a98891c..45137a3832f 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -1659,54 +1658,6 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds, return ret; } -#ifdef CONFIG_EPOLL - -asmlinkage long compat_sys_epoll_pwait(int epfd, - struct compat_epoll_event __user *events, - int maxevents, int timeout, - const compat_sigset_t __user *sigmask, - compat_size_t sigsetsize) -{ - long err; - compat_sigset_t csigmask; - sigset_t ksigmask, sigsaved; - - /* - * If the caller wants a certain signal mask to be set during the wait, - * we apply it here. - */ - if (sigmask) { - if (sigsetsize != sizeof(compat_sigset_t)) - return -EINVAL; - if (copy_from_user(&csigmask, sigmask, sizeof(csigmask))) - return -EFAULT; - sigset_from_compat(&ksigmask, &csigmask); - sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP)); - sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); - } - - err = sys_epoll_wait(epfd, events, maxevents, timeout); - - /* - * If we changed the signal mask, we need to restore the original one. - * In case we've got a signal while waiting, we do not restore the - * signal mask yet, and we allow do_signal() to deliver the signal on - * the way back to userspace, before the signal mask is restored. - */ - if (sigmask) { - if (err == -EINTR) { - memcpy(¤t->saved_sigmask, &sigsaved, - sizeof(sigsaved)); - set_restore_sigmask(); - } else - sigprocmask(SIG_SETMASK, &sigsaved, NULL); - } - - return err; -} - -#endif /* CONFIG_EPOLL */ - #ifdef CONFIG_FHANDLE /* * Exactly like fs/open.c:sys_open_by_handle_at(), except that it diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 9fec1836057..495d15558f4 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -40,6 +40,7 @@ #include #include #include +#include /* * LOCKING: @@ -1940,6 +1941,52 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, return error; } +#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd, + struct epoll_event __user *, events, + int, maxevents, int, timeout, + const compat_sigset_t __user *, sigmask, + compat_size_t, sigsetsize) +{ + long err; + compat_sigset_t csigmask; + sigset_t ksigmask, sigsaved; + + /* + * If the caller wants a certain signal mask to be set during the wait, + * we apply it here. + */ + if (sigmask) { + if (sigsetsize != sizeof(compat_sigset_t)) + return -EINVAL; + if (copy_from_user(&csigmask, sigmask, sizeof(csigmask))) + return -EFAULT; + sigset_from_compat(&ksigmask, &csigmask); + sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP)); + sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + } + + err = sys_epoll_wait(epfd, events, maxevents, timeout); + + /* + * If we changed the signal mask, we need to restore the original one. + * In case we've got a signal while waiting, we do not restore the + * signal mask yet, and we allow do_signal() to deliver the signal on + * the way back to userspace, before the signal mask is restored. + */ + if (sigmask) { + if (err == -EINTR) { + memcpy(¤t->saved_sigmask, &sigsaved, + sizeof(sigsaved)); + set_restore_sigmask(); + } else + sigprocmask(SIG_SETMASK, &sigsaved, NULL); + } + + return err; +} +#endif + static int __init eventpoll_init(void) { struct sysinfo si; diff --git a/include/linux/compat.h b/include/linux/compat.h index ad299afcd48..cdec8f2e9e2 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -432,10 +432,9 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, /* * epoll (fs/eventpoll.c) compat bits follow ... */ -struct epoll_event; -#define compat_epoll_event epoll_event +struct epoll_event; /* fortunately, this one is fixed-layout */ asmlinkage long compat_sys_epoll_pwait(int epfd, - struct compat_epoll_event __user *events, + struct epoll_event __user *events, int maxevents, int timeout, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize); -- cgit v1.2.3-70-g09d2 From d5dc77bfeeab0b03a32e3db5e31e2f64605634ab Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 25 Feb 2013 18:42:04 -0500 Subject: consolidate compat lookup_dcookie() Signed-off-by: Al Viro --- arch/arm64/kernel/sys32.S | 7 ------- arch/mips/kernel/linux32.c | 6 ------ arch/mips/kernel/scall64-o32.S | 2 +- arch/parisc/kernel/sys_parisc32.c | 7 ------- arch/parisc/kernel/syscall_table.S | 2 +- arch/powerpc/include/asm/systbl.h | 2 +- arch/powerpc/kernel/sys_ppc32.c | 7 ------- arch/s390/kernel/compat_wrapper.S | 7 ------- arch/s390/kernel/syscalls.S | 2 +- arch/sparc/kernel/sys_sparc32.c | 8 -------- arch/sparc/kernel/systbls_64.S | 2 +- arch/tile/kernel/compat.c | 5 ----- arch/x86/ia32/sys_ia32.c | 6 ------ arch/x86/include/asm/sys_ia32.h | 1 - arch/x86/syscalls/syscall_32.tbl | 2 +- fs/dcookies.c | 12 ++++++++++++ include/linux/compat.h | 1 + kernel/sys_ni.c | 1 + 18 files changed, 20 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/arch/arm64/kernel/sys32.S b/arch/arm64/kernel/sys32.S index 9416d045a68..db01aa978c4 100644 --- a/arch/arm64/kernel/sys32.S +++ b/arch/arm64/kernel/sys32.S @@ -84,13 +84,6 @@ compat_sys_readahead_wrapper: b sys_readahead ENDPROC(compat_sys_readahead_wrapper) -compat_sys_lookup_dcookie: - orr x0, x0, x1, lsl #32 - mov w1, w2 - mov w2, w3 - b sys_lookup_dcookie -ENDPROC(compat_sys_lookup_dcookie) - compat_sys_fadvise64_64_wrapper: mov w6, w1 orr x1, x2, x3, lsl #32 diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index b0cc2a7df59..6852d4876f8 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -259,12 +259,6 @@ asmlinkage long sys32_fallocate(int fd, int mode, unsigned offset_a2, merge_64(len_a4, len_a5)); } -asmlinkage long sys32_lookup_dcookie(u32 a0, u32 a1, char __user *buf, - size_t len) -{ - return sys_lookup_dcookie(merge_64(a0, a1), buf, len); -} - SYSCALL_DEFINE6(32_fanotify_mark, int, fanotify_fd, unsigned int, flags, u64, a3, u64, a4, int, dfd, const char __user *, pathname) { diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index c1a70e80575..91c8c6ea7b0 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -439,7 +439,7 @@ sys_call_table: PTR compat_sys_io_submit PTR sys_io_cancel /* 4245 */ PTR sys_exit_group - PTR sys32_lookup_dcookie + PTR compat_sys_lookup_dcookie PTR sys_epoll_create PTR sys_epoll_ctl PTR sys_epoll_wait /* 4250 */ diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c index 035ab3f9481..46bdf6080fe 100644 --- a/arch/parisc/kernel/sys_parisc32.c +++ b/arch/parisc/kernel/sys_parisc32.c @@ -75,13 +75,6 @@ asmlinkage long sys32_semctl(int semid, int semnum, int cmd, union semun arg) return sys_semctl (semid, semnum, cmd, arg); } -long sys32_lookup_dcookie(u32 cookie_high, u32 cookie_low, char __user *buf, - size_t len) -{ - return sys_lookup_dcookie((u64)cookie_high << 32 | cookie_low, - buf, len); -} - asmlinkage long compat_sys_fanotify_mark(int fan_fd, int flags, u32 mask_hi, u32 mask_lo, int fd, const char __user *pathname) diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index f232672a9e2..30c9a3bba1c 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -318,7 +318,7 @@ ENTRY_SAME(alloc_hugepages) /* 220 */ ENTRY_SAME(free_hugepages) ENTRY_SAME(exit_group) - ENTRY_DIFF(lookup_dcookie) + ENTRY_COMP(lookup_dcookie) ENTRY_SAME(epoll_create) ENTRY_SAME(epoll_ctl) /* 225 */ ENTRY_SAME(epoll_wait) diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h index 634db7d2dc9..afef04d6ee5 100644 --- a/arch/powerpc/include/asm/systbl.h +++ b/arch/powerpc/include/asm/systbl.h @@ -239,7 +239,7 @@ SYSCALL_SPU(io_cancel) SYSCALL(set_tid_address) SYSX_SPU(sys_fadvise64,ppc32_fadvise64,sys_fadvise64) SYSCALL(exit_group) -SYSX(sys_lookup_dcookie,ppc32_lookup_dcookie,sys_lookup_dcookie) +COMPAT_SYS(lookup_dcookie) SYSCALL_SPU(epoll_create) SYSCALL_SPU(epoll_ctl) SYSCALL_SPU(epoll_wait) diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index 6e7c2509bd2..e695230ca18 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -177,13 +177,6 @@ asmlinkage int compat_sys_ftruncate64(unsigned int fd, u32 reg4, unsigned long h return sys_ftruncate(fd, (high << 32) | low); } -long ppc32_lookup_dcookie(u32 cookie_high, u32 cookie_low, char __user *buf, - size_t len) -{ - return sys_lookup_dcookie((u64)cookie_high << 32 | cookie_low, - buf, len); -} - long ppc32_fadvise64(int fd, u32 unused, u32 offset_high, u32 offset_low, size_t len, int advice) { diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index 68117a3dd25..6d4958ea390 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -926,13 +926,6 @@ ENTRY(sys_epoll_wait_wrapper) lgfr %r5,%r5 # int jg sys_epoll_wait # branch to system call -ENTRY(sys32_lookup_dcookie_wrapper) - sllg %r2,%r2,32 # get high word of 64bit dcookie - or %r2,%r3 # get low word of 64bit dcookie - llgtr %r3,%r4 # char * - llgfr %r4,%r5 # size_t - jg sys_lookup_dcookie - ENTRY(sys32_fadvise64_wrapper) lgfr %r2,%r2 # int sllg %r3,%r3,32 # get high word of 64bit loff_t diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index 102254a4397..9154e17f25b 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -118,7 +118,7 @@ SYSCALL(sys_newstat,sys_newstat,compat_sys_newstat_wrapper) SYSCALL(sys_newlstat,sys_newlstat,compat_sys_newlstat_wrapper) SYSCALL(sys_newfstat,sys_newfstat,compat_sys_newfstat_wrapper) NI_SYSCALL /* old uname syscall */ -SYSCALL(sys_lookup_dcookie,sys_lookup_dcookie,sys32_lookup_dcookie_wrapper) /* 110 */ +SYSCALL(sys_lookup_dcookie,sys_lookup_dcookie,compat_sys_lookup_dcookie) /* 110 */ SYSCALL(sys_vhangup,sys_vhangup,sys_vhangup) NI_SYSCALL /* old "idle" system call */ NI_SYSCALL /* vm86old for i386 */ diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c index f38f2280fad..5d4ee8374c8 100644 --- a/arch/sparc/kernel/sys_sparc32.c +++ b/arch/sparc/kernel/sys_sparc32.c @@ -303,14 +303,6 @@ long compat_sys_fadvise64_64(int fd, advice); } -long sys32_lookup_dcookie(unsigned long cookie_high, - unsigned long cookie_low, - char __user *buf, size_t len) -{ - return sys_lookup_dcookie((cookie_high << 32) | cookie_low, - buf, len); -} - long compat_sync_file_range(int fd, unsigned long off_high, unsigned long off_low, unsigned long nb_high, unsigned long nb_low, int flags) { return sys_sync_file_range(fd, diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S index 46d575b6f69..8fd93208021 100644 --- a/arch/sparc/kernel/systbls_64.S +++ b/arch/sparc/kernel/systbls_64.S @@ -59,7 +59,7 @@ sys_call_table32: /*190*/ .word sys_init_module, sys_sparc64_personality, sys_remap_file_pages, sys_epoll_create, sys_epoll_ctl .word sys_epoll_wait, sys_ioprio_set, sys_getppid, compat_sys_sparc_sigaction, sys_sgetmask /*200*/ .word sys_ssetmask, sys_sigsuspend, compat_sys_newlstat, sys_uselib, compat_sys_old_readdir - .word sys32_readahead, sys32_socketcall, sys_syslog, sys32_lookup_dcookie, sys32_fadvise64 + .word sys32_readahead, sys32_socketcall, sys_syslog, compat_sys_lookup_dcookie, sys32_fadvise64 /*210*/ .word sys32_fadvise64_64, sys_tgkill, sys_waitpid, sys_swapoff, compat_sys_sysinfo .word compat_sys_ipc, sys32_sigreturn, sys_clone, sys_ioprio_get, compat_sys_adjtimex /*220*/ .word compat_sys_sigprocmask, sys_ni_syscall, sys_delete_module, sys_ni_syscall, sys_getpgid diff --git a/arch/tile/kernel/compat.c b/arch/tile/kernel/compat.c index 7f72401b4f4..c262a02d8ef 100644 --- a/arch/tile/kernel/compat.c +++ b/arch/tile/kernel/compat.c @@ -54,11 +54,6 @@ long compat_sys_pwrite64(unsigned int fd, char __user *ubuf, size_t count, return sys_pwrite64(fd, ubuf, count, ((loff_t)high << 32) | low); } -long compat_sys_lookup_dcookie(u32 low, u32 high, char __user *buf, size_t len) -{ - return sys_lookup_dcookie(((loff_t)high << 32) | low, buf, len); -} - long compat_sys_sync_file_range2(int fd, unsigned int flags, u32 offset_lo, u32 offset_hi, u32 nbytes_lo, u32 nbytes_hi) diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c index ad6ca047272..c0df976b0b7 100644 --- a/arch/x86/ia32/sys_ia32.c +++ b/arch/x86/ia32/sys_ia32.c @@ -226,12 +226,6 @@ long sys32_vm86_warning(void) return -ENOSYS; } -long sys32_lookup_dcookie(u32 addr_low, u32 addr_high, - char __user *buf, size_t len) -{ - return sys_lookup_dcookie(((u64)addr_high << 32) | addr_low, buf, len); -} - asmlinkage ssize_t sys32_readahead(int fd, unsigned off_lo, unsigned off_hi, size_t count) { diff --git a/arch/x86/include/asm/sys_ia32.h b/arch/x86/include/asm/sys_ia32.h index 6d944e4bb52..2b0e0c2d537 100644 --- a/arch/x86/include/asm/sys_ia32.h +++ b/arch/x86/include/asm/sys_ia32.h @@ -45,7 +45,6 @@ asmlinkage long sys32_personality(unsigned long); long sys32_kill(int, int); long sys32_fadvise64_64(int, __u32, __u32, __u32, __u32, int); long sys32_vm86_warning(void); -long sys32_lookup_dcookie(u32, u32, char __user *, size_t); asmlinkage ssize_t sys32_readahead(int, unsigned, unsigned, size_t); asmlinkage long sys32_sync_file_range(int, unsigned, unsigned, diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl index 6a00b1257d6..0b55cd773e4 100644 --- a/arch/x86/syscalls/syscall_32.tbl +++ b/arch/x86/syscalls/syscall_32.tbl @@ -259,7 +259,7 @@ 250 i386 fadvise64 sys_fadvise64 sys32_fadvise64 # 251 is available for reuse (was briefly sys_set_zone_reclaim) 252 i386 exit_group sys_exit_group -253 i386 lookup_dcookie sys_lookup_dcookie sys32_lookup_dcookie +253 i386 lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie 254 i386 epoll_create sys_epoll_create 255 i386 epoll_ctl sys_epoll_ctl 256 i386 epoll_wait sys_epoll_wait diff --git a/fs/dcookies.c b/fs/dcookies.c index f08375b97ff..ab5954b5026 100644 --- a/fs/dcookies.c +++ b/fs/dcookies.c @@ -25,6 +25,7 @@ #include #include #include +#include #include /* The dcookies are allocated from a kmem_cache and @@ -202,6 +203,17 @@ out: return err; } +#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE4(lookup_dcookie, u32, w0, u32, w1, char __user *, buf, size_t, len) +{ +#ifdef __BIG_ENDIAN + return sys_lookup_dcookie(((u64)w0 << 32) | w1, buf, len); +#else + return sys_lookup_dcookie(((u64)w1 << 32) | w0, buf, len); +#endif +} +#endif + static int dcookie_init(void) { struct list_head * d; diff --git a/include/linux/compat.h b/include/linux/compat.h index cdec8f2e9e2..482c9e65b5b 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -429,6 +429,7 @@ extern long compat_arch_ptrace(struct task_struct *child, compat_long_t request, asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, compat_long_t addr, compat_long_t data); +asmlinkage long compat_sys_lookup_dcookie(u32, u32, char __user *, size_t); /* * epoll (fs/eventpoll.c) compat bits follow ... */ diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 395084d4ce1..b50e2a003c5 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -20,6 +20,7 @@ cond_syscall(sys_quotactl); cond_syscall(sys32_quotactl); cond_syscall(sys_acct); cond_syscall(sys_lookup_dcookie); +cond_syscall(compat_sys_lookup_dcookie); cond_syscall(sys_swapon); cond_syscall(sys_swapoff); cond_syscall(sys_kexec_load); -- cgit v1.2.3-70-g09d2 From 56e41d3c5aa84d679eebdb3cb8a70b03c5fbd6c3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 21 Jan 2013 23:15:25 -0500 Subject: merge compat sys_ipc instances Signed-off-by: Al Viro --- arch/mips/kernel/linux32.c | 69 --------------------------------------- arch/mips/kernel/scall64-o32.S | 2 +- arch/powerpc/kernel/sys_ppc32.c | 67 ------------------------------------- arch/s390/kernel/compat_linux.c | 44 ++----------------------- arch/s390/kernel/compat_linux.h | 1 - arch/s390/kernel/compat_wrapper.S | 8 ----- arch/s390/kernel/syscalls.S | 2 +- arch/sparc/kernel/sys_sparc32.c | 65 ------------------------------------ arch/x86/ia32/Makefile | 3 -- arch/x86/ia32/ipc32.c | 54 ------------------------------ arch/x86/include/asm/sys_ia32.h | 3 -- arch/x86/syscalls/syscall_32.tbl | 2 +- include/linux/compat.h | 1 + ipc/compat.c | 44 +++++++++++++++++++++++++ kernel/sys_ni.c | 2 +- 15 files changed, 52 insertions(+), 315 deletions(-) delete mode 100644 arch/x86/ia32/ipc32.c (limited to 'include') diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index 6852d4876f8..7c57b8d7b25 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -119,75 +119,6 @@ SYSCALL_DEFINE6(32_pwrite, unsigned int, fd, const char __user *, buf, return sys_pwrite64(fd, buf, count, merge_64(a4, a5)); } -#ifdef CONFIG_SYSVIPC - -SYSCALL_DEFINE6(32_ipc, u32, call, long, first, long, second, long, third, - unsigned long, ptr, unsigned long, fifth) -{ - int version, err; - - version = call >> 16; /* hack for backward compatibility */ - call &= 0xffff; - - switch (call) { - case SEMOP: - /* struct sembuf is the same on 32 and 64bit :)) */ - err = sys_semtimedop(first, compat_ptr(ptr), second, NULL); - break; - case SEMTIMEDOP: - err = compat_sys_semtimedop(first, compat_ptr(ptr), second, - compat_ptr(fifth)); - break; - case SEMGET: - err = sys_semget(first, second, third); - break; - case SEMCTL: - err = compat_sys_semctl(first, second, third, compat_ptr(ptr)); - break; - case MSGSND: - err = compat_sys_msgsnd(first, second, third, compat_ptr(ptr)); - break; - case MSGRCV: - err = compat_sys_msgrcv(first, second, fifth, third, - version, compat_ptr(ptr)); - break; - case MSGGET: - err = sys_msgget((key_t) first, second); - break; - case MSGCTL: - err = compat_sys_msgctl(first, second, compat_ptr(ptr)); - break; - case SHMAT: - err = compat_sys_shmat(first, second, third, version, - compat_ptr(ptr)); - break; - case SHMDT: - err = sys_shmdt(compat_ptr(ptr)); - break; - case SHMGET: - err = sys_shmget(first, (unsigned)second, third); - break; - case SHMCTL: - err = compat_sys_shmctl(first, second, compat_ptr(ptr)); - break; - default: - err = -EINVAL; - break; - } - - return err; -} - -#else - -SYSCALL_DEFINE6(32_ipc, u32, call, int, first, int, second, int, third, - u32, ptr, u32, fifth) -{ - return -ENOSYS; -} - -#endif /* CONFIG_SYSVIPC */ - #ifdef CONFIG_MIPS32_N32 SYSCALL_DEFINE4(n32_semctl, int, semid, int, semnum, int, cmd, u32, arg) { diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index 91c8c6ea7b0..103bfe570fe 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -309,7 +309,7 @@ sys_call_table: PTR compat_sys_wait4 PTR sys_swapoff /* 4115 */ PTR compat_sys_sysinfo - PTR sys_32_ipc + PTR compat_sys_ipc PTR sys_fsync PTR sys32_sigreturn PTR __sys_clone /* 4120 */ diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index e695230ca18..d78ad7b6c46 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -61,73 +61,6 @@ asmlinkage long ppc32_select(u32 n, compat_ulong_t __user *inp, return compat_sys_select((int)n, inp, outp, exp, compat_ptr(tvp_x)); } -#ifdef CONFIG_SYSVIPC -long compat_sys_ipc(u32 call, u32 first, u32 second, u32 third, compat_uptr_t ptr, - u32 fifth) -{ - int version; - - version = call >> 16; /* hack for backward compatibility */ - call &= 0xffff; - - switch (call) { - - case SEMTIMEDOP: - if (fifth) - /* sign extend semid */ - return compat_sys_semtimedop((int)first, - compat_ptr(ptr), second, - compat_ptr(fifth)); - /* else fall through for normal semop() */ - case SEMOP: - /* struct sembuf is the same on 32 and 64bit :)) */ - /* sign extend semid */ - return sys_semtimedop((int)first, compat_ptr(ptr), second, - NULL); - case SEMGET: - /* sign extend key, nsems */ - return sys_semget((int)first, (int)second, third); - case SEMCTL: - /* sign extend semid, semnum */ - return compat_sys_semctl((int)first, (int)second, third, - compat_ptr(ptr)); - - case MSGSND: - /* sign extend msqid */ - return compat_sys_msgsnd((int)first, (int)second, third, - compat_ptr(ptr)); - case MSGRCV: - /* sign extend msqid, msgtyp */ - return compat_sys_msgrcv((int)first, second, (int)fifth, - third, version, compat_ptr(ptr)); - case MSGGET: - /* sign extend key */ - return sys_msgget((int)first, second); - case MSGCTL: - /* sign extend msqid */ - return compat_sys_msgctl((int)first, second, compat_ptr(ptr)); - - case SHMAT: - /* sign extend shmid */ - return compat_sys_shmat((int)first, second, third, version, - compat_ptr(ptr)); - case SHMDT: - return sys_shmdt(compat_ptr(ptr)); - case SHMGET: - /* sign extend key_t */ - return sys_shmget((int)first, second, third); - case SHMCTL: - /* sign extend shmid */ - return compat_sys_shmctl((int)first, second, compat_ptr(ptr)); - - default: - return -ENOSYS; - } - - return -ENOSYS; -} -#endif - unsigned long compat_sys_mmap2(unsigned long addr, size_t len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff) diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c index fbd29c70a29..8b6e4f5288a 100644 --- a/arch/s390/kernel/compat_linux.c +++ b/arch/s390/kernel/compat_linux.c @@ -288,51 +288,13 @@ asmlinkage long sys32_getegid16(void) return high2lowgid(from_kgid_munged(current_user_ns(), current_egid())); } -/* - * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation. - * - * This is really horribly ugly. - */ #ifdef CONFIG_SYSVIPC -asmlinkage long sys32_ipc(u32 call, int first, int second, int third, u32 ptr) +COMPAT_SYSCALL_DEFINE5(s390_ipc, uint, call, int, first, unsigned long, second, + unsigned long, third, compat_uptr_t, ptr) { if (call >> 16) /* hack for backward compatibility */ return -EINVAL; - switch (call) { - case SEMTIMEDOP: - return compat_sys_semtimedop(first, compat_ptr(ptr), - second, compat_ptr(third)); - case SEMOP: - /* struct sembuf is the same on 32 and 64bit :)) */ - return sys_semtimedop(first, compat_ptr(ptr), - second, NULL); - case SEMGET: - return sys_semget(first, second, third); - case SEMCTL: - return compat_sys_semctl(first, second, third, - compat_ptr(ptr)); - case MSGSND: - return compat_sys_msgsnd(first, second, third, - compat_ptr(ptr)); - case MSGRCV: - return compat_sys_msgrcv(first, second, 0, third, - 0, compat_ptr(ptr)); - case MSGGET: - return sys_msgget((key_t) first, second); - case MSGCTL: - return compat_sys_msgctl(first, second, compat_ptr(ptr)); - case SHMAT: - return compat_sys_shmat(first, second, third, - 0, compat_ptr(ptr)); - case SHMDT: - return sys_shmdt(compat_ptr(ptr)); - case SHMGET: - return sys_shmget(first, (unsigned)second, third); - case SHMCTL: - return compat_sys_shmctl(first, second, compat_ptr(ptr)); - } - - return -ENOSYS; + return compat_sys_ipc(call, first, second, third, ptr, third); } #endif diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h index bce0b7aec8f..976518c0592 100644 --- a/arch/s390/kernel/compat_linux.h +++ b/arch/s390/kernel/compat_linux.h @@ -94,7 +94,6 @@ long sys32_getuid16(void); long sys32_geteuid16(void); long sys32_getgid16(void); long sys32_getegid16(void); -long sys32_ipc(u32 call, int first, int second, int third, u32 ptr); long sys32_truncate64(const char __user * path, unsigned long high, unsigned long low); long sys32_ftruncate64(unsigned int fd, unsigned long high, unsigned long low); diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index 6d4958ea390..17644c8e10e 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -388,14 +388,6 @@ ENTRY(compat_sys_sysinfo_wrapper) llgtr %r2,%r2 # struct sysinfo_emu31 * jg compat_sys_sysinfo # branch to system call -ENTRY(sys32_ipc_wrapper) - llgfr %r2,%r2 # uint - lgfr %r3,%r3 # int - lgfr %r4,%r4 # int - lgfr %r5,%r5 # int - llgfr %r6,%r6 # u32 - jg sys32_ipc # branch to system call - ENTRY(sys32_fsync_wrapper) llgfr %r2,%r2 # unsigned int jg sys_fsync # branch to system call diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index 9154e17f25b..d2baabed714 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -125,7 +125,7 @@ NI_SYSCALL /* vm86old for i386 */ SYSCALL(sys_wait4,sys_wait4,compat_sys_wait4) SYSCALL(sys_swapoff,sys_swapoff,sys32_swapoff_wrapper) /* 115 */ SYSCALL(sys_sysinfo,sys_sysinfo,compat_sys_sysinfo_wrapper) -SYSCALL(sys_s390_ipc,sys_s390_ipc,sys32_ipc_wrapper) +SYSCALL(sys_s390_ipc,sys_s390_ipc,compat_sys_s390_ipc) SYSCALL(sys_fsync,sys_fsync,sys32_fsync_wrapper) SYSCALL(sys_sigreturn,sys_sigreturn,sys32_sigreturn) SYSCALL(sys_clone,sys_clone,sys_clone_wrapper) /* 120 */ diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c index 5d4ee8374c8..d546188b13d 100644 --- a/arch/sparc/kernel/sys_sparc32.c +++ b/arch/sparc/kernel/sys_sparc32.c @@ -49,71 +49,6 @@ #include #include -#ifdef CONFIG_SYSVIPC -asmlinkage long compat_sys_ipc(u32 call, u32 first, u32 second, u32 third, compat_uptr_t ptr, u32 fifth) -{ - int version; - - version = call >> 16; /* hack for backward compatibility */ - call &= 0xffff; - - switch (call) { - case SEMTIMEDOP: - if (fifth) - /* sign extend semid */ - return compat_sys_semtimedop((int)first, - compat_ptr(ptr), second, - compat_ptr(fifth)); - /* else fall through for normal semop() */ - case SEMOP: - /* struct sembuf is the same on 32 and 64bit :)) */ - /* sign extend semid */ - return sys_semtimedop((int)first, compat_ptr(ptr), second, - NULL); - case SEMGET: - /* sign extend key, nsems */ - return sys_semget((int)first, (int)second, third); - case SEMCTL: - /* sign extend semid, semnum */ - return compat_sys_semctl((int)first, (int)second, third, - compat_ptr(ptr)); - - case MSGSND: - /* sign extend msqid */ - return compat_sys_msgsnd((int)first, (int)second, third, - compat_ptr(ptr)); - case MSGRCV: - /* sign extend msqid, msgtyp */ - return compat_sys_msgrcv((int)first, second, (int)fifth, - third, version, compat_ptr(ptr)); - case MSGGET: - /* sign extend key */ - return sys_msgget((int)first, second); - case MSGCTL: - /* sign extend msqid */ - return compat_sys_msgctl((int)first, second, compat_ptr(ptr)); - - case SHMAT: - /* sign extend shmid */ - return compat_sys_shmat((int)first, second, third, version, - compat_ptr(ptr)); - case SHMDT: - return sys_shmdt(compat_ptr(ptr)); - case SHMGET: - /* sign extend key_t */ - return sys_shmget((int)first, second, third); - case SHMCTL: - /* sign extend shmid */ - return compat_sys_shmctl((int)first, second, compat_ptr(ptr)); - - default: - return -ENOSYS; - } - - return -ENOSYS; -} -#endif - asmlinkage long sys32_truncate64(const char __user * path, unsigned long high, unsigned long low) { if ((int)high < 0) diff --git a/arch/x86/ia32/Makefile b/arch/x86/ia32/Makefile index 455646e0e53..e785b422b76 100644 --- a/arch/x86/ia32/Makefile +++ b/arch/x86/ia32/Makefile @@ -5,9 +5,6 @@ obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_signal.o obj-$(CONFIG_IA32_EMULATION) += nosyscall.o syscall_ia32.o -sysv-$(CONFIG_SYSVIPC) := ipc32.o -obj-$(CONFIG_IA32_EMULATION) += $(sysv-y) - obj-$(CONFIG_IA32_AOUT) += ia32_aout.o audit-class-$(CONFIG_AUDIT) := audit.o diff --git a/arch/x86/ia32/ipc32.c b/arch/x86/ia32/ipc32.c deleted file mode 100644 index 29cdcd02ead..00000000000 --- a/arch/x86/ia32/ipc32.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -asmlinkage long sys32_ipc(u32 call, int first, int second, int third, - compat_uptr_t ptr, u32 fifth) -{ - int version; - - version = call >> 16; /* hack for backward compatibility */ - call &= 0xffff; - - switch (call) { - case SEMOP: - /* struct sembuf is the same on 32 and 64bit :)) */ - return sys_semtimedop(first, compat_ptr(ptr), second, NULL); - case SEMTIMEDOP: - return compat_sys_semtimedop(first, compat_ptr(ptr), second, - compat_ptr(fifth)); - case SEMGET: - return sys_semget(first, second, third); - case SEMCTL: - return compat_sys_semctl(first, second, third, compat_ptr(ptr)); - - case MSGSND: - return compat_sys_msgsnd(first, second, third, compat_ptr(ptr)); - case MSGRCV: - return compat_sys_msgrcv(first, second, fifth, third, - version, compat_ptr(ptr)); - case MSGGET: - return sys_msgget((key_t) first, second); - case MSGCTL: - return compat_sys_msgctl(first, second, compat_ptr(ptr)); - - case SHMAT: - return compat_sys_shmat(first, second, third, version, - compat_ptr(ptr)); - case SHMDT: - return sys_shmdt(compat_ptr(ptr)); - case SHMGET: - return sys_shmget(first, (unsigned)second, third); - case SHMCTL: - return compat_sys_shmctl(first, second, compat_ptr(ptr)); - } - return -ENOSYS; -} diff --git a/arch/x86/include/asm/sys_ia32.h b/arch/x86/include/asm/sys_ia32.h index 2b0e0c2d537..df8ad3b3920 100644 --- a/arch/x86/include/asm/sys_ia32.h +++ b/arch/x86/include/asm/sys_ia32.h @@ -57,9 +57,6 @@ asmlinkage long sys32_fallocate(int, int, unsigned, asmlinkage long sys32_sigreturn(void); asmlinkage long sys32_rt_sigreturn(void); -/* ia32/ipc32.c */ -asmlinkage long sys32_ipc(u32, int, int, int, compat_uptr_t, u32); - asmlinkage long sys32_fanotify_mark(int, unsigned int, u32, u32, int, const char __user *); diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl index 0b55cd773e4..0f6f5becab0 100644 --- a/arch/x86/syscalls/syscall_32.tbl +++ b/arch/x86/syscalls/syscall_32.tbl @@ -123,7 +123,7 @@ 114 i386 wait4 sys_wait4 compat_sys_wait4 115 i386 swapoff sys_swapoff 116 i386 sysinfo sys_sysinfo compat_sys_sysinfo -117 i386 ipc sys_ipc sys32_ipc +117 i386 ipc sys_ipc compat_sys_ipc 118 i386 fsync sys_fsync 119 i386 sigreturn sys_sigreturn stub32_sigreturn 120 i386 clone sys_clone stub32_clone diff --git a/include/linux/compat.h b/include/linux/compat.h index 482c9e65b5b..79a4781ac50 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -318,6 +318,7 @@ long compat_sys_msgrcv(int first, int second, int msgtyp, int third, int version, void __user *uptr); long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, void __user *uptr); +asmlinkage long compat_sys_ipc(u32, int, int, u32, compat_uptr_t, u32); #else long compat_sys_semctl(int semid, int semnum, int cmd, int arg); long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp, diff --git a/ipc/compat.c b/ipc/compat.c index 2547f29dcd1..1da2e2eb9d7 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -368,6 +368,50 @@ long compat_sys_msgrcv(int first, int second, int msgtyp, int third, return do_msgrcv(first, uptr, second, msgtyp, third, compat_do_msg_fill); } + +COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second, + u32, third, compat_uptr_t, ptr, u32, fifth) +{ + int version; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + /* struct sembuf is the same on 32 and 64bit :)) */ + return sys_semtimedop(first, compat_ptr(ptr), second, NULL); + case SEMTIMEDOP: + return compat_sys_semtimedop(first, compat_ptr(ptr), second, + compat_ptr(fifth)); + case SEMGET: + return sys_semget(first, second, third); + case SEMCTL: + return compat_sys_semctl(first, second, third, compat_ptr(ptr)); + + case MSGSND: + return compat_sys_msgsnd(first, second, third, compat_ptr(ptr)); + case MSGRCV: + return compat_sys_msgrcv(first, second, fifth, third, + version, compat_ptr(ptr)); + case MSGGET: + return sys_msgget(first, second); + case MSGCTL: + return compat_sys_msgctl(first, second, compat_ptr(ptr)); + + case SHMAT: + return compat_sys_shmat(first, second, third, version, + compat_ptr(ptr)); + case SHMDT: + return sys_shmdt(compat_ptr(ptr)); + case SHMGET: + return sys_shmget(first, (unsigned)second, third); + case SHMCTL: + return compat_sys_shmctl(first, second, compat_ptr(ptr)); + } + + return -ENOSYS; +} #else long compat_sys_semctl(int semid, int semnum, int cmd, int arg) { diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index b50e2a003c5..bfd6787b355 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -156,7 +156,7 @@ cond_syscall(compat_sys_process_vm_writev); cond_syscall(sys_pciconfig_read); cond_syscall(sys_pciconfig_write); cond_syscall(sys_pciconfig_iobase); -cond_syscall(sys32_ipc); +cond_syscall(compat_sys_s390_ipc); cond_syscall(ppc_rtas); cond_syscall(sys_spu_run); cond_syscall(sys_spu_create); -- cgit v1.2.3-70-g09d2 From 0e65a81b105a3f646793d46740ad90fa5c067986 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 Feb 2013 14:36:44 -0500 Subject: get rid of compat_sys_semctl() and friends in case of ARCH_WANT_OLD_COMPAT_IPC Signed-off-by: Al Viro --- arch/mips/kernel/linux32.c | 24 ------- arch/mips/kernel/scall64-n32.S | 6 +- include/linux/compat.h | 17 ++--- ipc/compat.c | 158 +++++++++++++++++------------------------ 4 files changed, 73 insertions(+), 132 deletions(-) (limited to 'include') diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index 7c57b8d7b25..d1d576b765f 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -119,30 +119,6 @@ SYSCALL_DEFINE6(32_pwrite, unsigned int, fd, const char __user *, buf, return sys_pwrite64(fd, buf, count, merge_64(a4, a5)); } -#ifdef CONFIG_MIPS32_N32 -SYSCALL_DEFINE4(n32_semctl, int, semid, int, semnum, int, cmd, u32, arg) -{ - /* compat_sys_semctl expects a pointer to union semun */ - u32 __user *uptr = compat_alloc_user_space(sizeof(u32)); - if (put_user(arg, uptr)) - return -EFAULT; - return compat_sys_semctl(semid, semnum, cmd, uptr); -} - -SYSCALL_DEFINE4(n32_msgsnd, int, msqid, u32, msgp, unsigned int, msgsz, - int, msgflg) -{ - return compat_sys_msgsnd(msqid, msgsz, msgflg, compat_ptr(msgp)); -} - -SYSCALL_DEFINE5(n32_msgrcv, int, msqid, u32, msgp, size_t, msgsz, - int, msgtyp, int, msgflg) -{ - return compat_sys_msgrcv(msqid, msgsz, msgtyp, msgflg, IPC_64, - compat_ptr(msgp)); -} -#endif - SYSCALL_DEFINE1(32_personality, unsigned long, personality) { unsigned int p = personality & 0xffffffff; diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 9b4df498fc5..edcb6594e7b 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -168,11 +168,11 @@ EXPORT(sysn32_call_table) PTR sys_newuname PTR sys_semget PTR sys_semop - PTR sys_n32_semctl + PTR compat_sys_semctl PTR sys_shmdt /* 6065 */ PTR sys_msgget - PTR sys_n32_msgsnd - PTR sys_n32_msgrcv + PTR compat_sys_msgsnd + PTR compat_sys_msgrcv PTR compat_sys_msgctl PTR compat_sys_fcntl /* 6070 */ PTR sys_flock diff --git a/include/linux/compat.h b/include/linux/compat.h index 79a4781ac50..2bfe67329dc 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -311,22 +311,13 @@ asmlinkage long compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, compat_size_t __user *len_ptr); -#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC -long compat_sys_semctl(int first, int second, int third, void __user *uptr); -long compat_sys_msgsnd(int first, int second, int third, void __user *uptr); -long compat_sys_msgrcv(int first, int second, int msgtyp, int third, - int version, void __user *uptr); -long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, - void __user *uptr); asmlinkage long compat_sys_ipc(u32, int, int, u32, compat_uptr_t, u32); -#else -long compat_sys_semctl(int semid, int semnum, int cmd, int arg); -long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp, +asmlinkage long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg); +asmlinkage long compat_sys_semctl(int semid, int semnum, int cmd, int arg); +asmlinkage long compat_sys_msgsnd(int msqid, compat_uptr_t msgp, compat_ssize_t msgsz, int msgflg); -long compat_sys_msgrcv(int msqid, struct compat_msgbuf __user *msgp, +asmlinkage long compat_sys_msgrcv(int msqid, compat_uptr_t msgp, compat_ssize_t msgsz, long msgtyp, int msgflg); -long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg); -#endif long compat_sys_msgctl(int first, int second, void __user *uptr); long compat_sys_shmctl(int first, int second, void __user *uptr); long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, diff --git a/ipc/compat.c b/ipc/compat.c index 1da2e2eb9d7..6cb6a4df86e 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -306,7 +306,7 @@ static long do_compat_semctl(int first, int second, int third, u32 pad) return err; } -long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) +static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) { struct compat_msgbuf __user *msgp = dest; size_t msgsz; @@ -320,59 +320,16 @@ long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) return msgsz; } -#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC -long compat_sys_semctl(int first, int second, int third, void __user *uptr) -{ - u32 pad; - - if (!uptr) - return -EINVAL; - if (get_user(pad, (u32 __user *) uptr)) - return -EFAULT; - return do_compat_semctl(first, second, third, pad); -} - -long compat_sys_msgsnd(int first, int second, int third, void __user *uptr) -{ - struct compat_msgbuf __user *up = uptr; - long type; - - if (first < 0) - return -EINVAL; - if (second < 0) - return -EINVAL; - - if (get_user(type, &up->mtype)) - return -EFAULT; - - return do_msgsnd(first, type, up->mtext, second, third); -} - -long compat_sys_msgrcv(int first, int second, int msgtyp, int third, - int version, void __user *uptr) -{ - if (first < 0) - return -EINVAL; - if (second < 0) - return -EINVAL; - - if (!version) { - struct compat_ipc_kludge ipck; - if (!uptr) - return -EINVAL; - if (copy_from_user (&ipck, uptr, sizeof(ipck))) - return -EFAULT; - uptr = compat_ptr(ipck.msgp); - msgtyp = ipck.msgtyp; - } - return do_msgrcv(first, uptr, second, msgtyp, third, - compat_do_msg_fill); -} +#ifndef COMPAT_SHMLBA +#define COMPAT_SHMLBA SHMLBA +#endif +#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second, u32, third, compat_uptr_t, ptr, u32, fifth) { int version; + u32 pad; version = call >> 16; /* hack for backward compatibility */ call &= 0xffff; @@ -387,21 +344,59 @@ COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second, case SEMGET: return sys_semget(first, second, third); case SEMCTL: - return compat_sys_semctl(first, second, third, compat_ptr(ptr)); + if (!ptr) + return -EINVAL; + if (get_user(pad, (u32 __user *) compat_ptr(ptr))) + return -EFAULT; + return do_compat_semctl(first, second, third, pad); + + case MSGSND: { + struct compat_msgbuf __user *up = compat_ptr(ptr); + compat_long_t type; + + if (first < 0 || second < 0) + return -EINVAL; - case MSGSND: - return compat_sys_msgsnd(first, second, third, compat_ptr(ptr)); - case MSGRCV: - return compat_sys_msgrcv(first, second, fifth, third, - version, compat_ptr(ptr)); + if (get_user(type, &up->mtype)) + return -EFAULT; + + return do_msgsnd(first, type, up->mtext, second, third); + } + case MSGRCV: { + void __user *uptr = compat_ptr(ptr); + + if (first < 0 || second < 0) + return -EINVAL; + + if (!version) { + struct compat_ipc_kludge ipck; + if (!uptr) + return -EINVAL; + if (copy_from_user (&ipck, uptr, sizeof(ipck))) + return -EFAULT; + uptr = compat_ptr(ipck.msgp); + fifth = ipck.msgtyp; + } + return do_msgrcv(first, uptr, second, fifth, third, + compat_do_msg_fill); + } case MSGGET: return sys_msgget(first, second); case MSGCTL: return compat_sys_msgctl(first, second, compat_ptr(ptr)); - case SHMAT: - return compat_sys_shmat(first, second, third, version, - compat_ptr(ptr)); + case SHMAT: { + int err; + unsigned long raddr; + + if (version == 1) + return -EINVAL; + err = do_shmat(first, compat_ptr(ptr), second, &raddr, + COMPAT_SHMLBA); + if (err < 0) + return err; + return put_user(raddr, (compat_ulong_t *)compat_ptr(third)); + } case SHMDT: return sys_shmdt(compat_ptr(ptr)); case SHMGET: @@ -412,29 +407,30 @@ COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second, return -ENOSYS; } -#else -long compat_sys_semctl(int semid, int semnum, int cmd, int arg) +#endif + +COMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg) { return do_compat_semctl(semid, semnum, cmd, arg); } -long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp, - compat_ssize_t msgsz, int msgflg) +COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp, + compat_ssize_t, msgsz, int, msgflg) { + struct compat_msgbuf __user *up = compat_ptr(msgp); compat_long_t mtype; - if (get_user(mtype, &msgp->mtype)) + if (get_user(mtype, &up->mtype)) return -EFAULT; - return do_msgsnd(msqid, mtype, msgp->mtext, (ssize_t)msgsz, msgflg); + return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg); } -long compat_sys_msgrcv(int msqid, struct compat_msgbuf __user *msgp, - compat_ssize_t msgsz, long msgtyp, int msgflg) +COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp, + compat_ssize_t, msgsz, long, msgtyp, int, msgflg) { - return do_msgrcv(msqid, msgp, (ssize_t)msgsz, msgtyp, msgflg, - compat_do_msg_fill); + return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, msgtyp, + msgflg, compat_do_msg_fill); } -#endif static inline int get_compat_msqid64(struct msqid64_ds *m64, struct compat_msqid64_ds __user *up64) @@ -552,28 +548,7 @@ long compat_sys_msgctl(int first, int second, void __user *uptr) return err; } -#ifndef COMPAT_SHMLBA -#define COMPAT_SHMLBA SHMLBA -#endif - -#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC -long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, - void __user *uptr) -{ - int err; - unsigned long raddr; - compat_ulong_t __user *uaddr; - - if (version == 1) - return -EINVAL; - err = do_shmat(first, uptr, second, &raddr, COMPAT_SHMLBA); - if (err < 0) - return err; - uaddr = compat_ptr(third); - return put_user(raddr, uaddr); -} -#else -long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg) +COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg) { unsigned long ret; long err; @@ -584,7 +559,6 @@ long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg) force_successful_syscall_return(); return (long)ret; } -#endif static inline int get_compat_shmid64_ds(struct shmid64_ds *s64, struct compat_shmid64_ds __user *up64) -- cgit v1.2.3-70-g09d2 From a33ec399e9fc266ba20f9b71d693aa63658bf2aa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 23:05:29 +0100 Subject: ACPI / scan: Introduce common code for ACPI-based device hotplug Multiple drivers handling hotplug-capable ACPI device nodes install notify handlers covering the same types of events in a very similar way. Moreover, those handlers are installed in separate namespace walks, although that really should be done during namespace scans carried out by acpi_bus_scan(). This leads to substantial code duplication, unnecessary overhead and behavior that is hard to follow. For this reason, introduce common code in drivers/acpi/scan.c for handling hotplug-related notification and carrying out device insertion and eject operations in a generic fashion, such that it may be used by all of the relevant drivers in the future. To cover the existing differences between those drivers introduce struct acpi_hotplug_profile for representing collections of hotplug settings associated with different ACPI scan handlers that can be used by the drivers to make the common code reflect their current behavior. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Tested-by: Toshi Kani --- drivers/acpi/scan.c | 269 ++++++++++++++++++++++++++++++++++++++---------- include/acpi/acpi_bus.h | 12 +++ 2 files changed, 228 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index cc1b0020478..de73fdf8959 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -107,32 +107,19 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -/** - * acpi_bus_hot_remove_device: hot-remove a device and its children - * @context: struct acpi_eject_event pointer (freed in this func) - * - * Hot-remove a device and its children. This function frees up the - * memory space passed by arg context, so that the caller may call - * this function asynchronously through acpi_os_hotplug_execute(). - */ -void acpi_bus_hot_remove_device(void *context) +static int acpi_scan_hot_remove(struct acpi_device *device) { - struct acpi_eject_event *ej_event = context; - struct acpi_device *device = ej_event->device; acpi_handle handle = device->handle; - acpi_handle temp; + acpi_handle not_used; struct acpi_object_list arg_list; union acpi_object arg; - acpi_status status = AE_OK; - u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ - - mutex_lock(&acpi_scan_lock); + acpi_status status; /* If there is no handle, the device node has been unregistered. */ - if (!device->handle) { + if (!handle) { dev_dbg(&device->dev, "ACPI handle missing\n"); put_device(&device->dev); - goto out; + return -EINVAL; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -143,7 +130,7 @@ void acpi_bus_hot_remove_device(void *context) put_device(&device->dev); device = NULL; - if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) { + if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", ¬_used))) { arg_list.count = 1; arg_list.pointer = &arg; arg.type = ACPI_TYPE_INTEGER; @@ -161,18 +148,158 @@ void acpi_bus_hot_remove_device(void *context) */ status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); if (ACPI_FAILURE(status)) { - if (status != AE_NOT_FOUND) + if (status == AE_NOT_FOUND) { + return -ENODEV; + } else { acpi_handle_warn(handle, "Eject failed\n"); + return -EIO; + } + } + return 0; +} - /* Tell the firmware the hot-remove operation has failed. */ - acpi_evaluate_hotplug_ost(handle, ej_event->event, - ost_code, NULL); +static void acpi_bus_device_eject(void *context) +{ + acpi_handle handle = context; + struct acpi_device *device = NULL; + struct acpi_scan_handler *handler; + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + + mutex_lock(&acpi_scan_lock); + + acpi_bus_get_device(handle, &device); + if (!device) + goto err_out; + + handler = device->handler; + if (!handler || !handler->hotplug.enabled) { + ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; + goto err_out; + } + acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + if (handler->hotplug.mode == AHM_CONTAINER) { + device->flags.eject_pending = true; + kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); + } else { + int error; + + get_device(&device->dev); + error = acpi_scan_hot_remove(device); + if (error) + goto err_out; } out: mutex_unlock(&acpi_scan_lock); - kfree(context); return; + + err_out: + acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, ost_code, + NULL); + goto out; +} + +static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) +{ + struct acpi_device *device = NULL; + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + int error; + + mutex_lock(&acpi_scan_lock); + + acpi_bus_get_device(handle, &device); + if (device) { + dev_warn(&device->dev, "Attempt to re-insert\n"); + goto out; + } + acpi_evaluate_hotplug_ost(handle, ost_source, + ACPI_OST_SC_INSERT_IN_PROGRESS, NULL); + error = acpi_bus_scan(handle); + if (error) { + acpi_handle_warn(handle, "Namespace scan failure\n"); + goto out; + } + error = acpi_bus_get_device(handle, &device); + if (error) { + acpi_handle_warn(handle, "Missing device node object\n"); + goto out; + } + ost_code = ACPI_OST_SC_SUCCESS; + if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) + kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); + + out: + acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); + mutex_unlock(&acpi_scan_lock); +} + +static void acpi_scan_bus_check(void *context) +{ + acpi_scan_bus_device_check((acpi_handle)context, + ACPI_NOTIFY_BUS_CHECK); +} + +static void acpi_scan_device_check(void *context) +{ + acpi_scan_bus_device_check((acpi_handle)context, + ACPI_NOTIFY_DEVICE_CHECK); +} + +static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *not_used) +{ + acpi_osd_exec_callback callback; + acpi_status status; + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); + callback = acpi_scan_bus_check; + break; + case ACPI_NOTIFY_DEVICE_CHECK: + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n"); + callback = acpi_scan_device_check; + break; + case ACPI_NOTIFY_EJECT_REQUEST: + acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); + callback = acpi_bus_device_eject; + break; + default: + /* non-hotplug event; possibly handled by other handler */ + return; + } + status = acpi_os_hotplug_execute(callback, handle); + if (ACPI_FAILURE(status)) + acpi_evaluate_hotplug_ost(handle, type, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, + NULL); +} + +/** + * acpi_bus_hot_remove_device: hot-remove a device and its children + * @context: struct acpi_eject_event pointer (freed in this func) + * + * Hot-remove a device and its children. This function frees up the + * memory space passed by arg context, so that the caller may call + * this function asynchronously through acpi_os_hotplug_execute(). + */ +void acpi_bus_hot_remove_device(void *context) +{ + struct acpi_eject_event *ej_event = context; + struct acpi_device *device = ej_event->device; + acpi_handle handle = device->handle; + int error; + + mutex_lock(&acpi_scan_lock); + + error = acpi_scan_hot_remove(device); + if (error && handle) + acpi_evaluate_hotplug_ost(handle, ej_event->event, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, + NULL); + + mutex_unlock(&acpi_scan_lock); + kfree(context); } EXPORT_SYMBOL(acpi_bus_hot_remove_device); @@ -206,51 +333,61 @@ static ssize_t acpi_eject_store(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int ret = count; - acpi_status status; - acpi_object_type type = 0; struct acpi_device *acpi_device = to_acpi_device(d); struct acpi_eject_event *ej_event; + acpi_object_type not_used; + acpi_status status; + u32 ost_source; + int ret; - if ((!count) || (buf[0] != '1')) { + if (!count || buf[0] != '1') return -EINVAL; - } - if (!acpi_device->driver && !acpi_device->handler) { - ret = -ENODEV; - goto err; - } - status = acpi_get_type(acpi_device->handle, &type); - if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) { - ret = -ENODEV; - goto err; - } - ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); - if (!ej_event) { - ret = -ENOMEM; - goto err; - } + if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) + && !acpi_device->driver) + return -ENODEV; + + status = acpi_get_type(acpi_device->handle, ¬_used); + if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable) + return -ENODEV; + + mutex_lock(&acpi_scan_lock); - get_device(&acpi_device->dev); - ej_event->device = acpi_device; if (acpi_device->flags.eject_pending) { - /* event originated from ACPI eject notification */ - ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; + /* ACPI eject notification event. */ + ost_source = ACPI_NOTIFY_EJECT_REQUEST; acpi_device->flags.eject_pending = 0; } else { - /* event originated from user */ - ej_event->event = ACPI_OST_EC_OSPM_EJECT; - (void) acpi_evaluate_hotplug_ost(acpi_device->handle, - ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + /* Eject initiated by user space. */ + ost_source = ACPI_OST_EC_OSPM_EJECT; } - + ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); + if (!ej_event) { + ret = -ENOMEM; + goto err_out; + } + acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + ej_event->device = acpi_device; + ej_event->event = ost_source; + get_device(&acpi_device->dev); status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); if (ACPI_FAILURE(status)) { put_device(&acpi_device->dev); kfree(ej_event); + ret = status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN; + goto err_out; } -err: + ret = count; + + out: + mutex_unlock(&acpi_scan_lock); return ret; + + err_out: + acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); + goto out; } static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); @@ -1555,6 +1692,30 @@ static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, return NULL; } +static void acpi_scan_init_hotplug(acpi_handle handle) +{ + struct acpi_device_info *info; + struct acpi_scan_handler *handler; + + if (ACPI_FAILURE(acpi_get_object_info(handle, &info))) + return; + + if (!(info->valid & ACPI_VALID_HID)) { + kfree(info); + return; + } + + /* + * This relies on the fact that acpi_install_notify_handler() will not + * install the same notify handler routine twice for the same handle. + */ + handler = acpi_scan_match_handler(info->hardware_id.string, NULL); + kfree(info); + if (handler && handler->hotplug.enabled) + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb, NULL); +} + static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, void *not_used, void **return_value) { @@ -1577,6 +1738,8 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } + acpi_scan_init_hotplug(handle); + if (!(sta & ACPI_STA_DEVICE_PRESENT) && !(sta & ACPI_STA_DEVICE_FUNCTIONING)) { struct acpi_device_wakeup wakeup; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index e65278f560c..f2c1d08a479 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -88,11 +88,23 @@ struct acpi_device; * ----------------- */ +enum acpi_hotplug_mode { + AHM_GENERIC = 0, + AHM_CONTAINER, + AHM_COUNT +}; + +struct acpi_hotplug_profile { + bool enabled:1; + enum acpi_hotplug_mode mode; +}; + struct acpi_scan_handler { const struct acpi_device_id *ids; struct list_head list_node; int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); void (*detach)(struct acpi_device *dev); + struct acpi_hotplug_profile hotplug; }; /* -- cgit v1.2.3-70-g09d2 From 3f8055c3583640ed3e4c81864dd76e06a7faa505 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 23:08:16 +0100 Subject: ACPI / hotplug: Introduce user space interface for hotplug profiles Introduce user space interface for manipulating hotplug profiles associated with ACPI scan handlers. The interface consists of sysfs directories under /sys/firmware/acpi/hotplug/, one for each hotplug profile, containing an attribute allowing user space to manipulate the enabled field of the corresponding profile. Namely, switching the enabled attribute from '0' to '1' will cause the common hotplug notify handler to be installed for all ACPI namespace objects representing devices matching the scan handler associated with the given hotplug profile (and analogously for the converse switch). Drivers willing to use the new user space interface should add their ACPI scan handlers with the help of new funtion acpi_scan_add_handler_with_hotplug(). Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Tested-by: Toshi Kani --- Documentation/ABI/testing/sysfs-firmware-acpi | 26 +++++++++++ drivers/acpi/internal.h | 6 +++ drivers/acpi/scan.c | 59 ++++++++++++++++++++++++ drivers/acpi/sysfs.c | 66 +++++++++++++++++++++++++++ include/acpi/acpi_bus.h | 7 +++ 5 files changed, 164 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi index dd930c8db41..ce9bee98b43 100644 --- a/Documentation/ABI/testing/sysfs-firmware-acpi +++ b/Documentation/ABI/testing/sysfs-firmware-acpi @@ -18,6 +18,32 @@ Description: yoffset: The number of pixels between the top of the screen and the top edge of the image. +What: /sys/firmware/acpi/hotplug/ +Date: February 2013 +Contact: Rafael J. Wysocki +Description: + There are separate hotplug profiles for different classes of + devices supported by ACPI, such as containers, memory modules, + processors, PCI root bridges etc. A hotplug profile for a given + class of devices is a collection of settings defining the way + that class of devices will be handled by the ACPI core hotplug + code. Those profiles are represented in sysfs as subdirectories + of /sys/firmware/acpi/hotplug/. + + The following setting is available to user space for each + hotplug profile: + + enabled: If set, the ACPI core will handle notifications of + hotplug events associated with the given class of + devices and will allow those devices to be ejected with + the help of the _EJ0 control method. Unsetting it + effectively disables hotplug for the correspoinding + class of devices. + + The value of the above attribute is an integer number: 1 (set) + or 0 (unset). Attempts to write any other values to it will + cause -EINVAL to be returned. + What: /sys/firmware/acpi/interrupts/ Date: February 2008 Contact: Len Brown diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 3c94a732b4b..c708e4bad96 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -42,6 +42,12 @@ void acpi_container_init(void); static inline void acpi_container_init(void) {} #endif +void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, + const char *name); +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name); +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val); + #ifdef CONFIG_DEBUG_FS extern struct dentry *acpi_debugfs_dir; int acpi_debugfs_init(void); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 45fbe95ba1f..5458403c824 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -63,6 +63,19 @@ int acpi_scan_add_handler(struct acpi_scan_handler *handler) return 0; } +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name) +{ + int error; + + error = acpi_scan_add_handler(handler); + if (error) + return error; + + acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name); + return 0; +} + /* * Creates hid/cid(s) string needed for modalias and uevent * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: @@ -1690,6 +1703,52 @@ static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, return false; } +static acpi_status acpi_scan_hotplug_modify(acpi_handle handle, + u32 lvl_not_used, void *data, + void **ret_not_used) +{ + struct acpi_scan_handler *handler = data; + struct acpi_device_info *info; + bool match = false; + + if (ACPI_FAILURE(acpi_get_object_info(handle, &info))) + return AE_OK; + + if (info->valid & ACPI_VALID_HID) { + char *idstr = info->hardware_id.string; + match = acpi_scan_handler_matching(handler, idstr, NULL); + } + kfree(info); + if (!match) + return AE_OK; + + if (handler->hotplug.enabled) + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb, NULL); + else + acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb); + + return AE_OK; +} + +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val) +{ + struct acpi_scan_handler *handler; + + if (!!hotplug->enabled == !!val) + return; + + mutex_lock(&acpi_scan_lock); + + hotplug->enabled = val; + handler = container_of(hotplug, struct acpi_scan_handler, hotplug); + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, + acpi_scan_hotplug_modify, NULL, handler, NULL); + + mutex_unlock(&acpi_scan_lock); +} + static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, const struct acpi_device_id **matchid) { diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 41c0504470d..83db3a68a7e 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -7,6 +7,8 @@ #include #include +#include "internal.h" + #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("sysfs"); @@ -249,6 +251,7 @@ module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); static LIST_HEAD(acpi_table_attr_list); static struct kobject *tables_kobj; static struct kobject *dynamic_tables_kobj; +static struct kobject *hotplug_kobj; struct acpi_table_attr { struct bin_attribute attr; @@ -716,6 +719,67 @@ acpi_show_profile(struct device *dev, struct device_attribute *attr, static const struct device_attribute pm_profile_attr = __ATTR(pm_profile, S_IRUGO, acpi_show_profile, NULL); +static ssize_t hotplug_enabled_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); + + return sprintf(buf, "%d\n", hotplug->enabled); +} + +static ssize_t hotplug_enabled_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t size) +{ + struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); + unsigned int val; + + if (kstrtouint(buf, 10, &val) || val > 1) + return -EINVAL; + + acpi_scan_hotplug_enabled(hotplug, val); + return size; +} + +static struct kobj_attribute hotplug_enabled_attr = + __ATTR(enabled, S_IRUGO | S_IWUSR, hotplug_enabled_show, + hotplug_enabled_store); + +static struct attribute *hotplug_profile_attrs[] = { + &hotplug_enabled_attr.attr, + NULL +}; + +struct kobj_type acpi_hotplug_profile_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = hotplug_profile_attrs, +}; + +void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, + const char *name) +{ + int error; + + if (!hotplug_kobj) + goto err_out; + + kobject_init(&hotplug->kobj, &acpi_hotplug_profile_ktype); + error = kobject_set_name(&hotplug->kobj, "%s", name); + if (error) + goto err_out; + + hotplug->kobj.parent = hotplug_kobj; + error = kobject_add(&hotplug->kobj, hotplug_kobj, NULL); + if (error) + goto err_out; + + kobject_uevent(&hotplug->kobj, KOBJ_ADD); + return; + + err_out: + pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name); +} + int __init acpi_sysfs_init(void) { int result; @@ -723,6 +787,8 @@ int __init acpi_sysfs_init(void) result = acpi_tables_sysfs_init(); if (result) return result; + + hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr); return result; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index f2c1d08a479..533ef039c5e 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -95,10 +95,17 @@ enum acpi_hotplug_mode { }; struct acpi_hotplug_profile { + struct kobject kobj; bool enabled:1; enum acpi_hotplug_mode mode; }; +static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile( + struct kobject *kobj) +{ + return container_of(kobj, struct acpi_hotplug_profile, kobj); +} + struct acpi_scan_handler { const struct acpi_device_id *ids; struct list_head list_node; -- cgit v1.2.3-70-g09d2 From 4b377bab29e6a241db42f27541e7fb63713ee178 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 4 Mar 2013 10:47:59 -0500 Subject: make do_mremap() static The extern in sys_sparc_64.c was a rudiment of time when do_mremap() used to exist in MMU case (it doesn't anymore). As for !MMU one, nothing uses it outside of mm/nommu.c... Signed-off-by: Al Viro --- arch/sparc/kernel/sys_sparc_64.c | 4 ---- include/linux/mm.h | 3 --- mm/nommu.c | 3 +-- 3 files changed, 1 insertion(+), 9 deletions(-) (limited to 'include') diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c index 708bc29d36a..42beb6fc4ad 100644 --- a/arch/sparc/kernel/sys_sparc_64.c +++ b/arch/sparc/kernel/sys_sparc_64.c @@ -470,10 +470,6 @@ SYSCALL_DEFINE2(64_munmap, unsigned long, addr, size_t, len) return vm_munmap(addr, len); } - -extern unsigned long do_mremap(unsigned long addr, - unsigned long old_len, unsigned long new_len, - unsigned long flags, unsigned long new_addr); SYSCALL_DEFINE5(64_mremap, unsigned long, addr, unsigned long, old_len, unsigned long, new_len, unsigned long, flags, diff --git a/include/linux/mm.h b/include/linux/mm.h index 7acc9dc73c9..f4c8aa99044 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1079,9 +1079,6 @@ extern unsigned long move_page_tables(struct vm_area_struct *vma, unsigned long old_addr, struct vm_area_struct *new_vma, unsigned long new_addr, unsigned long len, bool need_rmap_locks); -extern unsigned long do_mremap(unsigned long addr, - unsigned long old_len, unsigned long new_len, - unsigned long flags, unsigned long new_addr); extern unsigned long change_protection(struct vm_area_struct *vma, unsigned long start, unsigned long end, pgprot_t newprot, int dirty_accountable, int prot_numa); diff --git a/mm/nommu.c b/mm/nommu.c index e1932808753..66737e0584a 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1770,7 +1770,7 @@ unsigned long vm_brk(unsigned long addr, unsigned long len) * * MREMAP_FIXED is not supported under NOMMU conditions */ -unsigned long do_mremap(unsigned long addr, +static unsigned long do_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr) { @@ -1805,7 +1805,6 @@ unsigned long do_mremap(unsigned long addr, vma->vm_end = vma->vm_start + new_len; return vma->vm_start; } -EXPORT_SYMBOL(do_mremap); SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len, unsigned long, new_len, unsigned long, flags, -- cgit v1.2.3-70-g09d2 From 45d9550a0e7e9230606ca3c4c6f4dc6297848b2f Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Tue, 19 Feb 2013 12:17:01 -0800 Subject: workqueue: allow more off-queue flag space When a work item is off-queue, its work->data contains WORK_STRUCT_* and WORK_OFFQ_* flags. As WORK_OFFQ_* flags are used only while a work item is off-queue, it can occupy bits of work->data which aren't used while off-queue. WORK_OFFQ_* currently only use bits used by on-queue CWQ pointer. As color bits aren't used while off-queue, there's no reason to not use them. Lower WORK_OFFQ_FLAG_BASE from WORK_STRUCT_FLAG_BITS to WORK_STRUCT_COLOR_SHIFT thus giving 4 more bits to off-queue flag space which is also used to record worker_pool ID while off-queue. This doesn't introduce any visible behavior difference. tj: Rewrote the description. Signed-off-by: Lai Jiangshan Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 8afab27cdbc..5bd030f630a 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -68,7 +68,7 @@ enum { WORK_STRUCT_COLOR_BITS, /* data contains off-queue information when !WORK_STRUCT_PWQ */ - WORK_OFFQ_FLAG_BASE = WORK_STRUCT_FLAG_BITS, + WORK_OFFQ_FLAG_BASE = WORK_STRUCT_COLOR_SHIFT, WORK_OFFQ_CANCELING = (1 << WORK_OFFQ_FLAG_BASE), -- cgit v1.2.3-70-g09d2 From 65dff759d2948cf18e2029fc5c0c595b8b7da3a5 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Fri, 1 Mar 2013 15:01:56 +0800 Subject: cgroup: fix cgroup_path() vs rename() race rename() will change dentry->d_name. The result of this race can be worse than seeing partially rewritten name, but we might access a stale pointer because rename() will re-allocate memory to hold a longer name. As accessing dentry->name must be protected by dentry->d_lock or parent inode's i_mutex, while on the other hand cgroup-path() can be called with some irq-safe spinlocks held, we can't generate cgroup path using dentry->d_name. Alternatively we make a copy of dentry->d_name and save it in cgrp->name when a cgroup is created, and update cgrp->name at rename(). v5: use flexible array instead of zero-size array. v4: - allocate root_cgroup_name and all root_cgroup->name points to it. - add cgroup_name() wrapper. v3: use kfree_rcu() instead of synchronize_rcu() in user-visible path. v2: make cgrp->name RCU safe. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- block/blk-cgroup.h | 2 - include/linux/cgroup.h | 24 +++++++++++ kernel/cgroup.c | 106 +++++++++++++++++++++++++++++++++++-------------- 3 files changed, 100 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index f2b292925cc..4e595ee8c91 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -247,9 +247,7 @@ static inline int blkg_path(struct blkcg_gq *blkg, char *buf, int buflen) { int ret; - rcu_read_lock(); ret = cgroup_path(blkg->blkcg->css.cgroup, buf, buflen); - rcu_read_unlock(); if (ret) strncpy(buf, "", buflen); return ret; diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 900af5964f5..75c6ec1ba1b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -150,6 +150,11 @@ enum { CGRP_CPUSET_CLONE_CHILDREN, }; +struct cgroup_name { + struct rcu_head rcu_head; + char name[]; +}; + struct cgroup { unsigned long flags; /* "unsigned long" so bitops work */ @@ -172,6 +177,19 @@ struct cgroup { struct cgroup *parent; /* my parent */ struct dentry *dentry; /* cgroup fs entry, RCU protected */ + /* + * This is a copy of dentry->d_name, and it's needed because + * we can't use dentry->d_name in cgroup_path(). + * + * You must acquire rcu_read_lock() to access cgrp->name, and + * the only place that can change it is rename(), which is + * protected by parent dir's i_mutex. + * + * Normally you should use cgroup_name() wrapper rather than + * access it directly. + */ + struct cgroup_name __rcu *name; + /* Private pointers for each registered subsystem */ struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT]; @@ -404,6 +422,12 @@ struct cgroup_scanner { void *data; }; +/* Caller should hold rcu_read_lock() */ +static inline const char *cgroup_name(const struct cgroup *cgrp) +{ + return rcu_dereference(cgrp->name)->name; +} + int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); int cgroup_rm_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index a32f9432666..50682168abc 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -238,6 +238,8 @@ static DEFINE_SPINLOCK(hierarchy_id_lock); /* dummytop is a shorthand for the dummy hierarchy's top cgroup */ #define dummytop (&rootnode.top_cgroup) +static struct cgroup_name root_cgroup_name = { .name = "/" }; + /* This flag indicates whether tasks in the fork and exit paths should * check for fork/exit handlers to call. This avoids us having to do * extra work in the fork/exit path if none of the subsystems need to @@ -859,6 +861,17 @@ static struct inode *cgroup_new_inode(umode_t mode, struct super_block *sb) return inode; } +static struct cgroup_name *cgroup_alloc_name(struct dentry *dentry) +{ + struct cgroup_name *name; + + name = kmalloc(sizeof(*name) + dentry->d_name.len + 1, GFP_KERNEL); + if (!name) + return NULL; + strcpy(name->name, dentry->d_name.name); + return name; +} + static void cgroup_free_fn(struct work_struct *work) { struct cgroup *cgrp = container_of(work, struct cgroup, free_work); @@ -889,6 +902,7 @@ static void cgroup_free_fn(struct work_struct *work) simple_xattrs_free(&cgrp->xattrs); ida_simple_remove(&cgrp->root->cgroup_ida, cgrp->id); + kfree(rcu_dereference_raw(cgrp->name)); kfree(cgrp); } @@ -1421,6 +1435,7 @@ static void init_cgroup_root(struct cgroupfs_root *root) INIT_LIST_HEAD(&root->allcg_list); root->number_of_cgroups = 1; cgrp->root = root; + cgrp->name = &root_cgroup_name; cgrp->top_cgroup = cgrp; init_cgroup_housekeeping(cgrp); list_add_tail(&cgrp->allcg_node, &root->allcg_list); @@ -1769,49 +1784,45 @@ static struct kobject *cgroup_kobj; * @buf: the buffer to write the path into * @buflen: the length of the buffer * - * Called with cgroup_mutex held or else with an RCU-protected cgroup - * reference. Writes path of cgroup into buf. Returns 0 on success, - * -errno on error. + * Writes path of cgroup into buf. Returns 0 on success, -errno on error. + * + * We can't generate cgroup path using dentry->d_name, as accessing + * dentry->name must be protected by irq-unsafe dentry->d_lock or parent + * inode's i_mutex, while on the other hand cgroup_path() can be called + * with some irq-safe spinlocks held. */ int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen) { - struct dentry *dentry = cgrp->dentry; + int ret = -ENAMETOOLONG; char *start; - rcu_lockdep_assert(rcu_read_lock_held() || cgroup_lock_is_held(), - "cgroup_path() called without proper locking"); - - if (cgrp == dummytop) { - /* - * Inactive subsystems have no dentry for their root - * cgroup - */ - strcpy(buf, "/"); - return 0; - } - start = buf + buflen - 1; - *start = '\0'; - for (;;) { - int len = dentry->d_name.len; + rcu_read_lock(); + while (cgrp) { + const char *name = cgroup_name(cgrp); + int len; + + len = strlen(name); if ((start -= len) < buf) - return -ENAMETOOLONG; - memcpy(start, dentry->d_name.name, len); - cgrp = cgrp->parent; - if (!cgrp) - break; + goto out; + memcpy(start, name, len); - dentry = cgrp->dentry; if (!cgrp->parent) - continue; + break; + if (--start < buf) - return -ENAMETOOLONG; + goto out; *start = '/'; + + cgrp = cgrp->parent; } + ret = 0; memmove(buf, start, buf + buflen - start); - return 0; +out: + rcu_read_unlock(); + return ret; } EXPORT_SYMBOL_GPL(cgroup_path); @@ -2537,13 +2548,40 @@ static int cgroup_file_release(struct inode *inode, struct file *file) static int cgroup_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { + int ret; + struct cgroup_name *name, *old_name; + struct cgroup *cgrp; + + /* + * It's convinient to use parent dir's i_mutex to protected + * cgrp->name. + */ + lockdep_assert_held(&old_dir->i_mutex); + if (!S_ISDIR(old_dentry->d_inode->i_mode)) return -ENOTDIR; if (new_dentry->d_inode) return -EEXIST; if (old_dir != new_dir) return -EIO; - return simple_rename(old_dir, old_dentry, new_dir, new_dentry); + + cgrp = __d_cgrp(old_dentry); + + name = cgroup_alloc_name(new_dentry); + if (!name) + return -ENOMEM; + + ret = simple_rename(old_dir, old_dentry, new_dir, new_dentry); + if (ret) { + kfree(name); + return ret; + } + + old_name = cgrp->name; + rcu_assign_pointer(cgrp->name, name); + + kfree_rcu(old_name, rcu_head); + return 0; } static struct simple_xattrs *__d_xattrs(struct dentry *dentry) @@ -4158,6 +4196,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, umode_t mode) { struct cgroup *cgrp; + struct cgroup_name *name; struct cgroupfs_root *root = parent->root; int err = 0; struct cgroup_subsys *ss; @@ -4168,9 +4207,14 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, if (!cgrp) return -ENOMEM; + name = cgroup_alloc_name(dentry); + if (!name) + goto err_free_cgrp; + rcu_assign_pointer(cgrp->name, name); + cgrp->id = ida_simple_get(&root->cgroup_ida, 1, 0, GFP_KERNEL); if (cgrp->id < 0) - goto err_free_cgrp; + goto err_free_name; /* * Only live parents can have children. Note that the liveliness @@ -4276,6 +4320,8 @@ err_free_all: deactivate_super(sb); err_free_id: ida_simple_remove(&root->cgroup_ida, cgrp->id); +err_free_name: + kfree(rcu_dereference_raw(cgrp->name)); err_free_cgrp: kfree(cgrp); return err; -- cgit v1.2.3-70-g09d2 From 462fce46065ec4b200c08619c047b9e5a8fd154a Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Wed, 27 Feb 2013 19:41:56 +0900 Subject: KVM: set_memory_region: Drop user_alloc from prepare/commit_memory_region() X86 does not use this any more. The remaining user, s390's !user_alloc check, can be simply removed since KVM_SET_MEMORY_REGION ioctl is no longer supported. Note: fixed powerpc's indentations with spaces to suppress checkpatch errors. Signed-off-by: Takuya Yoshikawa Signed-off-by: Marcelo Tosatti --- arch/arm/kvm/arm.c | 6 ++---- arch/ia64/kvm/kvm-ia64.c | 6 ++---- arch/powerpc/kvm/powerpc.c | 12 +++++------- arch/s390/kvm/kvm-s390.c | 9 ++------- arch/x86/kvm/x86.c | 6 ++---- include/linux/kvm_host.h | 6 ++---- virt/kvm/kvm_main.c | 4 ++-- 7 files changed, 17 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 5a936988eb2..24cb5f66787 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -231,16 +231,14 @@ int kvm_arch_set_memory_region(struct kvm *kvm, int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem, - bool user_alloc) + struct kvm_userspace_memory_region *mem) { return 0; } void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old, - bool user_alloc) + struct kvm_memory_slot old) { } diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index ad3126a5864..cbc5b0417da 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1579,8 +1579,7 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem, - bool user_alloc) + struct kvm_userspace_memory_region *mem) { unsigned long i; unsigned long pfn; @@ -1610,8 +1609,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old, - bool user_alloc) + struct kvm_memory_slot old) { return; } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 934413cd3a1..22b33159fbc 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -411,18 +411,16 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) } int kvm_arch_prepare_memory_region(struct kvm *kvm, - struct kvm_memory_slot *memslot, - struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem, - bool user_alloc) + struct kvm_memory_slot *memslot, + struct kvm_memory_slot old, + struct kvm_userspace_memory_region *mem) { return kvmppc_core_prepare_memory_region(kvm, memslot, mem); } void kvm_arch_commit_memory_region(struct kvm *kvm, - struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old, - bool user_alloc) + struct kvm_userspace_memory_region *mem, + struct kvm_memory_slot old) { kvmppc_core_commit_memory_region(kvm, mem, old); } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 4cf35a0a79e..07ac302ce24 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -975,8 +975,7 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem, - bool user_alloc) + struct kvm_userspace_memory_region *mem) { /* A few sanity checks. We can have exactly one memory slot which has to start at guest virtual zero and which has to be located at a @@ -997,16 +996,12 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, if (mem->memory_size & 0xffffful) return -EINVAL; - if (!user_alloc) - return -EINVAL; - return 0; } void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old, - bool user_alloc) + struct kvm_memory_slot old) { int rc; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 811c5c9c888..26216bb4403 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6907,8 +6907,7 @@ out_free: int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem, - bool user_alloc) + struct kvm_userspace_memory_region *mem) { int npages = memslot->npages; @@ -6938,8 +6937,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old, - bool user_alloc) + struct kvm_memory_slot old) { int nr_mmu_pages = 0, npages = mem->memory_size >> PAGE_SHIFT; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index cad77fe09d7..b4757a1cc4c 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -464,12 +464,10 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages); int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem, - bool user_alloc); + struct kvm_userspace_memory_region *mem); void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old, - bool user_alloc); + struct kvm_memory_slot old); bool kvm_largepages_enabled(void); void kvm_disable_largepages(void); /* flush all memory translations */ diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index adc68feb5c5..fd3037010e7 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -875,7 +875,7 @@ int __kvm_set_memory_region(struct kvm *kvm, slots = old_memslots; } - r = kvm_arch_prepare_memory_region(kvm, &new, old, mem, user_alloc); + r = kvm_arch_prepare_memory_region(kvm, &new, old, mem); if (r) goto out_slots; @@ -915,7 +915,7 @@ int __kvm_set_memory_region(struct kvm *kvm, old_memslots = install_new_memslots(kvm, slots, &new); - kvm_arch_commit_memory_region(kvm, mem, old, user_alloc); + kvm_arch_commit_memory_region(kvm, mem, old); kvm_free_physmem_slot(&old, &new); kfree(old_memslots); -- cgit v1.2.3-70-g09d2 From 47ae31e257c548abdb199e0d26723139a9a967ba Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Wed, 27 Feb 2013 19:43:00 +0900 Subject: KVM: set_memory_region: Drop user_alloc from set_memory_region() Except ia64's stale code, KVM_SET_MEMORY_REGION support, this is only used for sanity checks in __kvm_set_memory_region() which can easily be changed to use slot id instead. Signed-off-by: Takuya Yoshikawa Signed-off-by: Marcelo Tosatti --- arch/ia64/kvm/kvm-ia64.c | 18 ------------------ arch/x86/kvm/vmx.c | 6 +++--- include/linux/kvm_host.h | 10 +++------- virt/kvm/kvm_main.c | 18 +++++++----------- 4 files changed, 13 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index cbc5b0417da..43701f0c0f7 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -942,24 +942,6 @@ long kvm_arch_vm_ioctl(struct file *filp, int r = -ENOTTY; switch (ioctl) { - case KVM_SET_MEMORY_REGION: { - struct kvm_memory_region kvm_mem; - struct kvm_userspace_memory_region kvm_userspace_mem; - - r = -EFAULT; - if (copy_from_user(&kvm_mem, argp, sizeof kvm_mem)) - goto out; - kvm_userspace_mem.slot = kvm_mem.slot; - kvm_userspace_mem.flags = kvm_mem.flags; - kvm_userspace_mem.guest_phys_addr = - kvm_mem.guest_phys_addr; - kvm_userspace_mem.memory_size = kvm_mem.memory_size; - r = kvm_vm_ioctl_set_memory_region(kvm, - &kvm_userspace_mem, false); - if (r) - goto out; - break; - } case KVM_CREATE_IRQCHIP: r = -EFAULT; r = kvm_ioapic_init(kvm); diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 7cc566b09ff..58fb7c27e3b 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3694,7 +3694,7 @@ static int alloc_apic_access_page(struct kvm *kvm) kvm_userspace_mem.flags = 0; kvm_userspace_mem.guest_phys_addr = 0xfee00000ULL; kvm_userspace_mem.memory_size = PAGE_SIZE; - r = __kvm_set_memory_region(kvm, &kvm_userspace_mem, false); + r = __kvm_set_memory_region(kvm, &kvm_userspace_mem); if (r) goto out; @@ -3724,7 +3724,7 @@ static int alloc_identity_pagetable(struct kvm *kvm) kvm_userspace_mem.guest_phys_addr = kvm->arch.ept_identity_map_addr; kvm_userspace_mem.memory_size = PAGE_SIZE; - r = __kvm_set_memory_region(kvm, &kvm_userspace_mem, false); + r = __kvm_set_memory_region(kvm, &kvm_userspace_mem); if (r) goto out; @@ -4364,7 +4364,7 @@ static int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr) .flags = 0, }; - ret = kvm_set_memory_region(kvm, &tss_mem, false); + ret = kvm_set_memory_region(kvm, &tss_mem); if (ret) return ret; kvm->arch.tss_addr = addr; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index b4757a1cc4c..84a994c7a5c 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -453,11 +453,9 @@ id_to_memslot(struct kvm_memslots *slots, int id) } int kvm_set_memory_region(struct kvm *kvm, - struct kvm_userspace_memory_region *mem, - bool user_alloc); + struct kvm_userspace_memory_region *mem); int __kvm_set_memory_region(struct kvm *kvm, - struct kvm_userspace_memory_region *mem, - bool user_alloc); + struct kvm_userspace_memory_region *mem); void kvm_arch_free_memslot(struct kvm_memory_slot *free, struct kvm_memory_slot *dont); int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages); @@ -553,9 +551,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log); int kvm_vm_ioctl_set_memory_region(struct kvm *kvm, - struct - kvm_userspace_memory_region *mem, - bool user_alloc); + struct kvm_userspace_memory_region *mem); int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level); long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index fd3037010e7..5b3e41b81f0 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -745,8 +745,7 @@ enum kvm_mr_change { * Must be called holding mmap_sem for write. */ int __kvm_set_memory_region(struct kvm *kvm, - struct kvm_userspace_memory_region *mem, - bool user_alloc) + struct kvm_userspace_memory_region *mem) { int r; gfn_t base_gfn; @@ -767,7 +766,7 @@ int __kvm_set_memory_region(struct kvm *kvm, if (mem->guest_phys_addr & (PAGE_SIZE - 1)) goto out; /* We can read the guest memory with __xxx_user() later on. */ - if (user_alloc && + if ((mem->slot < KVM_USER_MEM_SLOTS) && ((mem->userspace_addr & (PAGE_SIZE - 1)) || !access_ok(VERIFY_WRITE, (void __user *)(unsigned long)mem->userspace_addr, @@ -932,26 +931,23 @@ out: EXPORT_SYMBOL_GPL(__kvm_set_memory_region); int kvm_set_memory_region(struct kvm *kvm, - struct kvm_userspace_memory_region *mem, - bool user_alloc) + struct kvm_userspace_memory_region *mem) { int r; mutex_lock(&kvm->slots_lock); - r = __kvm_set_memory_region(kvm, mem, user_alloc); + r = __kvm_set_memory_region(kvm, mem); mutex_unlock(&kvm->slots_lock); return r; } EXPORT_SYMBOL_GPL(kvm_set_memory_region); int kvm_vm_ioctl_set_memory_region(struct kvm *kvm, - struct - kvm_userspace_memory_region *mem, - bool user_alloc) + struct kvm_userspace_memory_region *mem) { if (mem->slot >= KVM_USER_MEM_SLOTS) return -EINVAL; - return kvm_set_memory_region(kvm, mem, user_alloc); + return kvm_set_memory_region(kvm, mem); } int kvm_get_dirty_log(struct kvm *kvm, @@ -2198,7 +2194,7 @@ static long kvm_vm_ioctl(struct file *filp, sizeof kvm_userspace_mem)) goto out; - r = kvm_vm_ioctl_set_memory_region(kvm, &kvm_userspace_mem, true); + r = kvm_vm_ioctl_set_memory_region(kvm, &kvm_userspace_mem); break; } case KVM_GET_DIRTY_LOG: { -- cgit v1.2.3-70-g09d2 From 74d0727cb7aaaea48a6353209093be26abc8d160 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Wed, 27 Feb 2013 19:43:44 +0900 Subject: KVM: set_memory_region: Make kvm_mr_change available to arch code This will be used for cleaning up prepare/commit_memory_region() later. Signed-off-by: Takuya Yoshikawa Signed-off-by: Marcelo Tosatti --- include/linux/kvm_host.h | 18 ++++++++++++++++++ virt/kvm/kvm_main.c | 18 ------------------ 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 84a994c7a5c..8eaf61f7b02 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -452,6 +452,24 @@ id_to_memslot(struct kvm_memslots *slots, int id) return slot; } +/* + * KVM_SET_USER_MEMORY_REGION ioctl allows the following operations: + * - create a new memory slot + * - delete an existing memory slot + * - modify an existing memory slot + * -- move it in the guest physical memory space + * -- just change its flags + * + * Since flags can be changed by some of these operations, the following + * differentiation is the best we can do for __kvm_set_memory_region(): + */ +enum kvm_mr_change { + KVM_MR_CREATE, + KVM_MR_DELETE, + KVM_MR_MOVE, + KVM_MR_FLAGS_ONLY, +}; + int kvm_set_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem); int __kvm_set_memory_region(struct kvm *kvm, diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 5b3e41b81f0..c7979ed4192 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -718,24 +718,6 @@ static struct kvm_memslots *install_new_memslots(struct kvm *kvm, return old_memslots; } -/* - * KVM_SET_USER_MEMORY_REGION ioctl allows the following operations: - * - create a new memory slot - * - delete an existing memory slot - * - modify an existing memory slot - * -- move it in the guest physical memory space - * -- just change its flags - * - * Since flags can be changed by some of these operations, the following - * differentiation is the best we can do for __kvm_set_memory_region(): - */ -enum kvm_mr_change { - KVM_MR_CREATE, - KVM_MR_DELETE, - KVM_MR_MOVE, - KVM_MR_FLAGS_ONLY, -}; - /* * Allocate some memory and give it an address in the guest physical address * space. -- cgit v1.2.3-70-g09d2 From 7b6195a91d60909a2834ab7181e2b9476e6fe749 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Wed, 27 Feb 2013 19:44:34 +0900 Subject: KVM: set_memory_region: Refactor prepare_memory_region() This patch drops the parameter old, a copy of the old memory slot, and adds a new parameter named change to know the change being requested. This not only cleans up the code but also removes extra copying of the memory slot structure. Signed-off-by: Takuya Yoshikawa Signed-off-by: Marcelo Tosatti --- arch/arm/kvm/arm.c | 4 ++-- arch/ia64/kvm/kvm-ia64.c | 4 ++-- arch/powerpc/kvm/powerpc.c | 4 ++-- arch/s390/kvm/kvm-s390.c | 4 ++-- arch/x86/kvm/x86.c | 10 ++++------ include/linux/kvm_host.h | 4 ++-- virt/kvm/kvm_main.c | 2 +- 7 files changed, 15 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 24cb5f66787..96ebab7a195 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -230,8 +230,8 @@ int kvm_arch_set_memory_region(struct kvm *kvm, int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, - struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem) + struct kvm_userspace_memory_region *mem, + enum kvm_mr_change change) { return 0; } diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 43701f0c0f7..5c2b07e8c3d 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1560,8 +1560,8 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, - struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem) + struct kvm_userspace_memory_region *mem, + enum kvm_mr_change change) { unsigned long i; unsigned long pfn; diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 22b33159fbc..8aa51cd67c2 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -412,8 +412,8 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, - struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem) + struct kvm_userspace_memory_region *mem, + enum kvm_mr_change change) { return kvmppc_core_prepare_memory_region(kvm, memslot, mem); } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 07ac302ce24..4288780c86b 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -974,8 +974,8 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) /* Section: memory related */ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, - struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem) + struct kvm_userspace_memory_region *mem, + enum kvm_mr_change change) { /* A few sanity checks. We can have exactly one memory slot which has to start at guest virtual zero and which has to be located at a diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 26216bb4403..7198234fa08 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6906,23 +6906,21 @@ out_free: int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, - struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem) + struct kvm_userspace_memory_region *mem, + enum kvm_mr_change change) { - int npages = memslot->npages; - /* * Only private memory slots need to be mapped here since * KVM_SET_MEMORY_REGION ioctl is no longer supported. */ - if ((memslot->id >= KVM_USER_MEM_SLOTS) && npages && !old.npages) { + if ((memslot->id >= KVM_USER_MEM_SLOTS) && (change == KVM_MR_CREATE)) { unsigned long userspace_addr; /* * MAP_SHARED to prevent internal slot pages from being moved * by fork()/COW. */ - userspace_addr = vm_mmap(NULL, 0, npages * PAGE_SIZE, + userspace_addr = vm_mmap(NULL, 0, memslot->npages * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 8eaf61f7b02..caa72cf7e8e 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -479,8 +479,8 @@ void kvm_arch_free_memslot(struct kvm_memory_slot *free, int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages); int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, - struct kvm_memory_slot old, - struct kvm_userspace_memory_region *mem); + struct kvm_userspace_memory_region *mem, + enum kvm_mr_change change); void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, struct kvm_memory_slot old); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index c7979ed4192..8f85bae862c 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -856,7 +856,7 @@ int __kvm_set_memory_region(struct kvm *kvm, slots = old_memslots; } - r = kvm_arch_prepare_memory_region(kvm, &new, old, mem); + r = kvm_arch_prepare_memory_region(kvm, &new, mem, change); if (r) goto out_slots; -- cgit v1.2.3-70-g09d2 From 8482644aea11e0647867732319ccf35879a9acc2 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Wed, 27 Feb 2013 19:45:25 +0900 Subject: KVM: set_memory_region: Refactor commit_memory_region() This patch makes the parameter old a const pointer to the old memory slot and adds a new parameter named change to know the change being requested: the former is for removing extra copying and the latter is for cleaning up the code. Signed-off-by: Takuya Yoshikawa Signed-off-by: Marcelo Tosatti --- arch/arm/kvm/arm.c | 3 ++- arch/ia64/kvm/kvm-ia64.c | 3 ++- arch/powerpc/include/asm/kvm_ppc.h | 2 +- arch/powerpc/kvm/book3s_hv.c | 4 ++-- arch/powerpc/kvm/book3s_pr.c | 2 +- arch/powerpc/kvm/booke.c | 2 +- arch/powerpc/kvm/powerpc.c | 3 ++- arch/s390/kvm/kvm-s390.c | 3 ++- arch/x86/kvm/x86.c | 15 ++++++++------- include/linux/kvm_host.h | 3 ++- virt/kvm/kvm_main.c | 2 +- 11 files changed, 24 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 96ebab7a195..b32dc446e80 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -238,7 +238,8 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old) + const struct kvm_memory_slot *old, + enum kvm_mr_change change) { } diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 5c2b07e8c3d..7a54455dde3 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1591,7 +1591,8 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old) + const struct kvm_memory_slot *old, + enum kvm_mr_change change) { return; } diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 44a657adf41..44fa9ad1d62 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -152,7 +152,7 @@ extern int kvmppc_core_prepare_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem); extern void kvmppc_core_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old); + const struct kvm_memory_slot *old); extern int kvm_vm_ioctl_get_smmu_info(struct kvm *kvm, struct kvm_ppc_smmu_info *info); extern void kvmppc_core_flush_memslot(struct kvm *kvm, diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 80dcc53a1ab..1e521baf9a7 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1639,12 +1639,12 @@ int kvmppc_core_prepare_memory_region(struct kvm *kvm, void kvmppc_core_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old) + const struct kvm_memory_slot *old) { unsigned long npages = mem->memory_size >> PAGE_SHIFT; struct kvm_memory_slot *memslot; - if (npages && old.npages) { + if (npages && old->npages) { /* * If modifying a memslot, reset all the rmap dirty bits. * If this is a new memslot, we don't need to do anything diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 5e93438afb0..286e23e6b92 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -1283,7 +1283,7 @@ int kvmppc_core_prepare_memory_region(struct kvm *kvm, void kvmppc_core_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old) + const struct kvm_memory_slot *old) { } diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 020923e4313..eb88fa62107 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -1531,7 +1531,7 @@ int kvmppc_core_prepare_memory_region(struct kvm *kvm, void kvmppc_core_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old) + const struct kvm_memory_slot *old) { } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 8aa51cd67c2..7b5d4d20cdc 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -420,7 +420,8 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old) + const struct kvm_memory_slot *old, + enum kvm_mr_change change) { kvmppc_core_commit_memory_region(kvm, mem, old); } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 4288780c86b..6cae4ad647a 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -1001,7 +1001,8 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old) + const struct kvm_memory_slot *old, + enum kvm_mr_change change) { int rc; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 7198234fa08..35b491229c3 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6935,16 +6935,17 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old) + const struct kvm_memory_slot *old, + enum kvm_mr_change change) { - int nr_mmu_pages = 0, npages = mem->memory_size >> PAGE_SHIFT; + int nr_mmu_pages = 0; - if ((mem->slot >= KVM_USER_MEM_SLOTS) && old.npages && !npages) { + if ((mem->slot >= KVM_USER_MEM_SLOTS) && (change == KVM_MR_DELETE)) { int ret; - ret = vm_munmap(old.userspace_addr, - old.npages * PAGE_SIZE); + ret = vm_munmap(old->userspace_addr, + old->npages * PAGE_SIZE); if (ret < 0) printk(KERN_WARNING "kvm_vm_ioctl_set_memory_region: " @@ -6961,13 +6962,13 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, * Existing largepage mappings are destroyed here and new ones will * not be created until the end of the logging. */ - if (npages && (mem->flags & KVM_MEM_LOG_DIRTY_PAGES)) + if ((change != KVM_MR_DELETE) && (mem->flags & KVM_MEM_LOG_DIRTY_PAGES)) kvm_mmu_slot_remove_write_access(kvm, mem->slot); /* * If memory slot is created, or moved, we need to clear all * mmio sptes. */ - if (npages && old.base_gfn != mem->guest_phys_addr >> PAGE_SHIFT) { + if ((change == KVM_MR_CREATE) || (change == KVM_MR_MOVE)) { kvm_mmu_zap_all(kvm); kvm_reload_remote_mmus(kvm); } diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index caa72cf7e8e..ac584cc5358 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -483,7 +483,8 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, enum kvm_mr_change change); void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - struct kvm_memory_slot old); + const struct kvm_memory_slot *old, + enum kvm_mr_change change); bool kvm_largepages_enabled(void); void kvm_disable_largepages(void); /* flush all memory translations */ diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 8f85bae862c..0e919a1d4d5 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -896,7 +896,7 @@ int __kvm_set_memory_region(struct kvm *kvm, old_memslots = install_new_memslots(kvm, slots, &new); - kvm_arch_commit_memory_region(kvm, mem, old); + kvm_arch_commit_memory_region(kvm, mem, &old, change); kvm_free_physmem_slot(&old, &new); kfree(old_memslots); -- cgit v1.2.3-70-g09d2 From 51dcdafcb720a9d1fd73b597d0ccf48837abc59f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 5 Mar 2013 14:16:00 +0800 Subject: regulator: core: Add enable_is_inverted flag to indicate set enable_mask bits to disable Add enable_is_inverted flag to indicate set enable_mask bits to disable when using regulator_enable_regmap and friends APIs. Signed-off-by: Axel Lin Reviewed-by: Haojian Zhuang Signed-off-by: Mark Brown --- drivers/regulator/core.c | 24 ++++++++++++++++++++---- include/linux/regulator/driver.h | 3 +++ 2 files changed, 23 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 154bc8f0c1a..d887b9f5b21 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1794,7 +1794,10 @@ int regulator_is_enabled_regmap(struct regulator_dev *rdev) if (ret != 0) return ret; - return (val & rdev->desc->enable_mask) != 0; + if (rdev->desc->enable_is_inverted) + return (val & rdev->desc->enable_mask) == 0; + else + return (val & rdev->desc->enable_mask) != 0; } EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap); @@ -1809,9 +1812,15 @@ EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap); */ int regulator_enable_regmap(struct regulator_dev *rdev) { + unsigned int val; + + if (rdev->desc->enable_is_inverted) + val = 0; + else + val = rdev->desc->enable_mask; + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, - rdev->desc->enable_mask, - rdev->desc->enable_mask); + rdev->desc->enable_mask, val); } EXPORT_SYMBOL_GPL(regulator_enable_regmap); @@ -1826,8 +1835,15 @@ EXPORT_SYMBOL_GPL(regulator_enable_regmap); */ int regulator_disable_regmap(struct regulator_dev *rdev) { + unsigned int val; + + if (rdev->desc->enable_is_inverted) + val = rdev->desc->enable_mask; + else + val = 0; + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, - rdev->desc->enable_mask, 0); + rdev->desc->enable_mask, val); } EXPORT_SYMBOL_GPL(regulator_disable_regmap); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 7df93f52db0..07ea8f1a127 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -199,6 +199,8 @@ enum regulator_type { * output when using regulator_set_voltage_sel_regmap * @enable_reg: Register for control when using regmap enable/disable ops * @enable_mask: Mask for control when using regmap enable/disable ops + * @enable_is_inverted: A flag to indicate set enable_mask bits to disable + * when using regulator_enable_regmap and friends APIs. * @bypass_reg: Register for control when using regmap set_bypass * @bypass_mask: Mask for control when using regmap set_bypass * @@ -228,6 +230,7 @@ struct regulator_desc { unsigned int apply_bit; unsigned int enable_reg; unsigned int enable_mask; + bool enable_is_inverted; unsigned int bypass_reg; unsigned int bypass_mask; -- cgit v1.2.3-70-g09d2 From 1b9e94dc69959e963fe57ede46259f792641af4d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 9 Sep 2012 09:23:31 -0300 Subject: [media] bttv: add VIDIOC_DBG_G_CHIP_IDENT VIDIOC_DBG_G_CHIP_IDENT is a prerequisite for the G/S_REGISTER ioctls. In addition, add support to call G/S_REGISTER for supporting i2c devices. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-driver.c | 40 +++++++++++++++++++++++++++++++---- drivers/media/pci/bt8xx/bttv.h | 3 +++ include/media/v4l2-chip-ident.h | 8 +++++++ 3 files changed, 47 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index cc7f58f94cd..b36d6757753 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -49,6 +49,7 @@ #include "bttvp.h" #include #include +#include #include #include @@ -2059,6 +2060,28 @@ static int bttv_log_status(struct file *file, void *f) return 0; } +static int bttv_g_chip_ident(struct file *file, void *f, struct v4l2_dbg_chip_ident *chip) +{ + struct bttv_fh *fh = f; + struct bttv *btv = fh->btv; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (chip->match.type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(&chip->match)) { + chip->ident = btv->id; + if (chip->ident == PCI_DEVICE_ID_FUSION879) + chip->ident = V4L2_IDENT_BT879; + } + return 0; + } + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + /* TODO: is this correct? */ + return bttv_call_all_err(btv, core, g_chip_ident, chip); +} + #ifdef CONFIG_VIDEO_ADV_DEBUG static int bttv_g_register(struct file *file, void *f, struct v4l2_dbg_register *reg) @@ -2069,8 +2092,12 @@ static int bttv_g_register(struct file *file, void *f, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; + if (!v4l2_chip_match_host(®->match)) { + /* TODO: subdev errors should not be ignored, this should become a + subdev helper function. */ + bttv_call_all(btv, core, g_register, reg); + return 0; + } /* bt848 has a 12-bit register space */ reg->reg &= 0xfff; @@ -2089,8 +2116,12 @@ static int bttv_s_register(struct file *file, void *f, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; + if (!v4l2_chip_match_host(®->match)) { + /* TODO: subdev errors should not be ignored, this should become a + subdev helper function. */ + bttv_call_all(btv, core, s_register, reg); + return 0; + } /* bt848 has a 12-bit register space */ reg->reg &= 0xfff; @@ -3394,6 +3425,7 @@ static const struct v4l2_ioctl_ops bttv_ioctl_ops = { .vidioc_s_frequency = bttv_s_frequency, .vidioc_log_status = bttv_log_status, .vidioc_querystd = bttv_querystd, + .vidioc_g_chip_ident = bttv_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = bttv_g_register, .vidioc_s_register = bttv_s_register, diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h index 79a11240a59..6139ce26dc2 100644 --- a/drivers/media/pci/bt8xx/bttv.h +++ b/drivers/media/pci/bt8xx/bttv.h @@ -359,6 +359,9 @@ void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits); #define bttv_call_all(btv, o, f, args...) \ v4l2_device_call_all(&btv->c.v4l2_dev, 0, o, f, ##args) +#define bttv_call_all_err(btv, o, f, args...) \ + v4l2_device_call_until_err(&btv->c.v4l2_dev, 0, o, f, ##args) + extern int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for); extern int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, unsigned char b2, int both); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 4ee125bae71..b5996f959a3 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -96,12 +96,20 @@ enum { /* module au0828 */ V4L2_IDENT_AU0828 = 828, + /* module bttv: ident 848 + 849 */ + V4L2_IDENT_BT848 = 848, + V4L2_IDENT_BT849 = 849, + /* module bt856: just ident 856 */ V4L2_IDENT_BT856 = 856, /* module bt866: just ident 866 */ V4L2_IDENT_BT866 = 866, + /* module bttv: ident 878 + 879 */ + V4L2_IDENT_BT878 = 878, + V4L2_IDENT_BT879 = 879, + /* module ks0127: reserved range 1120-1129 */ V4L2_IDENT_KS0122S = 1122, V4L2_IDENT_KS0127 = 1127, -- cgit v1.2.3-70-g09d2 From 01df530c2791610727e345b3dd97ef75943c7320 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 6 Feb 2013 12:40:28 -0300 Subject: [media] bttv: convert to the control framework Note that the private chroma agc control has been replaced with the standard CHROMA_AGC control. Also fixes a mute/automute problem where closing the file handle would force mute on. That's not what you want since that would make the mute state out of sync with the mute control. Instead check against the user count. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-cards.c | 5 +- drivers/media/pci/bt8xx/bttv-driver.c | 682 +++++++++++++--------------------- drivers/media/pci/bt8xx/bttvp.h | 16 +- include/uapi/linux/v4l2-controls.h | 5 + 4 files changed, 271 insertions(+), 437 deletions(-) (limited to 'include') diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c index c4c59175e52..682ed893d71 100644 --- a/drivers/media/pci/bt8xx/bttv-cards.c +++ b/drivers/media/pci/bt8xx/bttv-cards.c @@ -3554,8 +3554,9 @@ void bttv_init_card2(struct bttv *btv) I2C_CLIENT_END }; - if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev, - &btv->c.i2c_adap, "tda7432", 0, addrs)) + btv->sd_tda7432 = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tda7432", 0, addrs); + if (btv->sd_tda7432) return; } diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 73404b7a139..8e0a44d03fc 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -94,7 +94,7 @@ static unsigned int combfilter; static unsigned int lumafilter; static unsigned int automute = 1; static unsigned int chroma_agc; -static unsigned int adc_crush = 1; +static unsigned int agc_crush = 1; static unsigned int whitecrush_upper = 0xCF; static unsigned int whitecrush_lower = 0x7F; static unsigned int vcr_hack; @@ -126,7 +126,7 @@ module_param(combfilter, int, 0444); module_param(lumafilter, int, 0444); module_param(automute, int, 0444); module_param(chroma_agc, int, 0444); -module_param(adc_crush, int, 0444); +module_param(agc_crush, int, 0444); module_param(whitecrush_upper, int, 0444); module_param(whitecrush_lower, int, 0444); module_param(vcr_hack, int, 0444); @@ -139,27 +139,27 @@ module_param_array(video_nr, int, NULL, 0444); module_param_array(radio_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); -MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)"); -MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian"); -MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)"); -MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)"); -MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)"); -MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)"); +MODULE_PARM_DESC(radio, "The TV card supports radio, default is 0 (no)"); +MODULE_PARM_DESC(bigendian, "byte order of the framebuffer, default is native endian"); +MODULE_PARM_DESC(bttv_verbose, "verbose startup messages, default is 1 (yes)"); +MODULE_PARM_DESC(bttv_gpio, "log gpio changes, default is 0 (no)"); +MODULE_PARM_DESC(bttv_debug, "debug messages, default is 0 (no)"); +MODULE_PARM_DESC(irq_debug, "irq handler debug messages, default is 0 (no)"); MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); -MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8"); -MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000"); -MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default " +MODULE_PARM_DESC(gbuffers, "number of capture buffers. range 2-32, default 8"); +MODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 0x208000"); +MODULE_PARM_DESC(reset_crop, "reset cropping parameters at open(), default " "is 1 (yes) for compatibility with older applications"); -MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)"); -MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)"); -MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)"); -MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207"); -MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127"); -MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)"); -MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler"); -MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50"); -MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)"); -MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)"); +MODULE_PARM_DESC(automute, "mute audio on bad/missing video signal, default is 1 (yes)"); +MODULE_PARM_DESC(chroma_agc, "enables the AGC of chroma signal, default is 0 (no)"); +MODULE_PARM_DESC(agc_crush, "enables the luminance AGC crush, default is 1 (yes)"); +MODULE_PARM_DESC(whitecrush_upper, "sets the white crush upper value, default is 207"); +MODULE_PARM_DESC(whitecrush_lower, "sets the white crush lower value, default is 127"); +MODULE_PARM_DESC(vcr_hack, "enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)"); +MODULE_PARM_DESC(irq_iswitch, "switch inputs in irq handler"); +MODULE_PARM_DESC(uv_ratio, "ratio between u and v gains, default is 50"); +MODULE_PARM_DESC(full_luma_range, "use the full luma range, default is 0 (no)"); +MODULE_PARM_DESC(coring, "set the luma coring level, default is 0 (no)"); MODULE_PARM_DESC(video_nr, "video device numbers"); MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); MODULE_PARM_DESC(radio_nr, "radio device numbers"); @@ -169,6 +169,17 @@ MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); MODULE_LICENSE("GPL"); MODULE_VERSION(BTTV_VERSION); +#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_USER_BTTV_BASE + 0) +#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_USER_BTTV_BASE + 1) +#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_USER_BTTV_BASE + 2) +#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_USER_BTTV_BASE + 3) +#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_USER_BTTV_BASE + 4) +#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER (V4L2_CID_USER_BTTV_BASE + 5) +#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER (V4L2_CID_USER_BTTV_BASE + 6) +#define V4L2_CID_PRIVATE_UV_RATIO (V4L2_CID_USER_BTTV_BASE + 7) +#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE (V4L2_CID_USER_BTTV_BASE + 8) +#define V4L2_CID_PRIVATE_CORING (V4L2_CID_USER_BTTV_BASE + 9) + /* ----------------------------------------------------------------------- */ /* sysfs */ @@ -622,198 +633,6 @@ static const struct bttv_format formats[] = { }; static const unsigned int FORMATS = ARRAY_SIZE(formats); -/* ----------------------------------------------------------------------- */ - -#define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0) -#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1) -#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2) -#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_PRIVATE_BASE + 3) -#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_PRIVATE_BASE + 4) -#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_PRIVATE_BASE + 5) -#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER (V4L2_CID_PRIVATE_BASE + 6) -#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER (V4L2_CID_PRIVATE_BASE + 7) -#define V4L2_CID_PRIVATE_UV_RATIO (V4L2_CID_PRIVATE_BASE + 8) -#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE (V4L2_CID_PRIVATE_BASE + 9) -#define V4L2_CID_PRIVATE_CORING (V4L2_CID_PRIVATE_BASE + 10) -#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 11) - -static const struct v4l2_queryctrl no_ctl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; -static const struct v4l2_queryctrl bttv_ctls[] = { - /* --- video --- */ - { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = 0, - .maximum = 65535, - .step = 256, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 65535, - .step = 128, - .default_value = 27648, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 65535, - .step = 128, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_COLOR_KILLER, - .name = "Color killer", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, { - .id = V4L2_CID_HUE, - .name = "Hue", - .minimum = 0, - .maximum = 65535, - .step = 256, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - /* --- audio --- */ - { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 65535, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_AUDIO_BALANCE, - .name = "Balance", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_AUDIO_BASS, - .name = "Bass", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_AUDIO_TREBLE, - .name = "Treble", - .minimum = 0, - .maximum = 65535, - .step = 65535/100, - .default_value = 32768, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - /* --- private --- */ - { - .id = V4L2_CID_PRIVATE_CHROMA_AGC, - .name = "chroma agc", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_COMBFILTER, - .name = "combfilter", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_AUTOMUTE, - .name = "automute", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_LUMAFILTER, - .name = "luma decimation filter", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_AGC_CRUSH, - .name = "agc crush", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_VCR_HACK, - .name = "vcr hack", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER, - .name = "whitecrush upper", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 0xCF, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER, - .name = "whitecrush lower", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 0x7F, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_PRIVATE_UV_RATIO, - .name = "uv ratio", - .minimum = 0, - .maximum = 100, - .step = 1, - .default_value = 50, - .type = V4L2_CTRL_TYPE_INTEGER, - },{ - .id = V4L2_CID_PRIVATE_FULL_LUMA_RANGE, - .name = "full luma range", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_PRIVATE_CORING, - .name = "coring", - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - } - - - -}; - -static const struct v4l2_queryctrl *ctrl_by_id(int id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++) - if (bttv_ctls[i].id == id) - return bttv_ctls+i; - - return NULL; -} - /* ----------------------------------------------------------------------- */ /* resource management */ @@ -1173,7 +992,7 @@ static int audio_mux(struct bttv *btv, int input, int mute) { int gpio_val, signal; - struct v4l2_control ctrl; + struct v4l2_ctrl *ctrl; gpio_inout(bttv_tvcards[btv->c.type].gpiomask, bttv_tvcards[btv->c.type].gpiomask); @@ -1183,7 +1002,8 @@ audio_mux(struct bttv *btv, int input, int mute) btv->audio = input; /* automute */ - mute = mute || (btv->opt_automute && !signal && !btv->radio_user); + mute = mute || (btv->opt_automute && (!signal || !btv->users) + && !btv->radio_user); if (mute) gpio_val = bttv_tvcards[btv->c.type].gpiomute; @@ -1205,12 +1025,13 @@ audio_mux(struct bttv *btv, int input, int mute) if (in_interrupt()) return 0; - ctrl.id = V4L2_CID_AUDIO_MUTE; - ctrl.value = btv->mute; - bttv_call_all(btv, core, s_ctrl, &ctrl); if (btv->sd_msp34xx) { u32 in; + ctrl = v4l2_ctrl_find(btv->sd_msp34xx->ctrl_handler, V4L2_CID_AUDIO_MUTE); + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, btv->mute); + /* Note: the inputs tuner/radio/extern/intern are translated to msp routings. This assumes common behavior for all msp3400 based TV cards. When this assumption fails, then the @@ -1255,9 +1076,19 @@ audio_mux(struct bttv *btv, int input, int mute) in, MSP_OUTPUT_DEFAULT, 0); } if (btv->sd_tvaudio) { + ctrl = v4l2_ctrl_find(btv->sd_tvaudio->ctrl_handler, V4L2_CID_AUDIO_MUTE); + + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, btv->mute); v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing, input, 0, 0); } + if (btv->sd_tda7432) { + ctrl = v4l2_ctrl_find(btv->sd_tda7432->ctrl_handler, V4L2_CID_AUDIO_MUTE); + + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, btv->mute); + } return 0; } @@ -1395,8 +1226,6 @@ static void init_irqreg(struct bttv *btv) static void init_bt848(struct bttv *btv) { - int val; - if (bttv_tvcards[btv->c.type].no_video) { /* very basic init only */ init_irqreg(btv); @@ -1416,30 +1245,10 @@ static void init_bt848(struct bttv *btv) BT848_GPIO_DMA_CTL_GPINTI, BT848_GPIO_DMA_CTL); - val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0; - btwrite(val, BT848_E_SCLOOP); - btwrite(val, BT848_O_SCLOOP); - btwrite(0x20, BT848_E_VSCALE_HI); btwrite(0x20, BT848_O_VSCALE_HI); - btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0), - BT848_ADC); - btwrite(whitecrush_upper, BT848_WC_UP); - btwrite(whitecrush_lower, BT848_WC_DOWN); - - if (btv->opt_lumafilter) { - btwrite(0, BT848_E_CONTROL); - btwrite(0, BT848_O_CONTROL); - } else { - btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL); - btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL); - } - - bt848_bright(btv, btv->bright); - bt848_hue(btv, btv->hue); - bt848_contrast(btv, btv->contrast); - bt848_sat(btv, btv->saturation); + v4l2_ctrl_handler_setup(&btv->ctrl_handler); /* interrupt */ init_irqreg(btv); @@ -1461,103 +1270,26 @@ static void bttv_reinit_bt848(struct bttv *btv) set_input(btv, btv->input, btv->tvnorm); } -static int bttv_g_ctrl(struct file *file, void *priv, - struct v4l2_control *c) +static int bttv_s_ctrl(struct v4l2_ctrl *c) { - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; - - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = btv->bright; - break; - case V4L2_CID_HUE: - c->value = btv->hue; - break; - case V4L2_CID_CONTRAST: - c->value = btv->contrast; - break; - case V4L2_CID_SATURATION: - c->value = btv->saturation; - break; - case V4L2_CID_COLOR_KILLER: - c->value = btv->opt_color_killer; - break; - - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - bttv_call_all(btv, core, g_ctrl, c); - break; - - case V4L2_CID_PRIVATE_CHROMA_AGC: - c->value = btv->opt_chroma_agc; - break; - case V4L2_CID_PRIVATE_COMBFILTER: - c->value = btv->opt_combfilter; - break; - case V4L2_CID_PRIVATE_LUMAFILTER: - c->value = btv->opt_lumafilter; - break; - case V4L2_CID_PRIVATE_AUTOMUTE: - c->value = btv->opt_automute; - break; - case V4L2_CID_PRIVATE_AGC_CRUSH: - c->value = btv->opt_adc_crush; - break; - case V4L2_CID_PRIVATE_VCR_HACK: - c->value = btv->opt_vcr_hack; - break; - case V4L2_CID_PRIVATE_WHITECRUSH_UPPER: - c->value = btv->opt_whitecrush_upper; - break; - case V4L2_CID_PRIVATE_WHITECRUSH_LOWER: - c->value = btv->opt_whitecrush_lower; - break; - case V4L2_CID_PRIVATE_UV_RATIO: - c->value = btv->opt_uv_ratio; - break; - case V4L2_CID_PRIVATE_FULL_LUMA_RANGE: - c->value = btv->opt_full_luma_range; - break; - case V4L2_CID_PRIVATE_CORING: - c->value = btv->opt_coring; - break; - default: - return -EINVAL; - } - return 0; -} - -static int bttv_s_ctrl(struct file *file, void *f, - struct v4l2_control *c) -{ - int err; - struct bttv_fh *fh = f; - struct bttv *btv = fh->btv; - - err = v4l2_prio_check(&btv->prio, fh->prio); - if (0 != err) - return err; + struct bttv *btv = container_of(c->handler, struct bttv, ctrl_handler); + int val; switch (c->id) { case V4L2_CID_BRIGHTNESS: - bt848_bright(btv, c->value); + bt848_bright(btv, c->val); break; case V4L2_CID_HUE: - bt848_hue(btv, c->value); + bt848_hue(btv, c->val); break; case V4L2_CID_CONTRAST: - bt848_contrast(btv, c->value); + bt848_contrast(btv, c->val); break; case V4L2_CID_SATURATION: - bt848_sat(btv, c->value); + bt848_sat(btv, c->val); break; case V4L2_CID_COLOR_KILLER: - btv->opt_color_killer = c->value; - if (btv->opt_color_killer) { + if (c->val) { btor(BT848_SCLOOP_CKILL, BT848_E_SCLOOP); btor(BT848_SCLOOP_CKILL, BT848_O_SCLOOP); } else { @@ -1566,36 +1298,22 @@ static int bttv_s_ctrl(struct file *file, void *f, } break; case V4L2_CID_AUDIO_MUTE: - audio_mute(btv, c->value); - /* fall through */ - case V4L2_CID_AUDIO_VOLUME: - if (btv->volume_gpio) - btv->volume_gpio(btv, c->value); - - bttv_call_all(btv, core, s_ctrl, c); + audio_mute(btv, c->val); break; - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - bttv_call_all(btv, core, s_ctrl, c); + case V4L2_CID_AUDIO_VOLUME: + btv->volume_gpio(btv, c->val); break; - case V4L2_CID_PRIVATE_CHROMA_AGC: - btv->opt_chroma_agc = c->value; - if (btv->opt_chroma_agc) { - btor(BT848_SCLOOP_CAGC, BT848_E_SCLOOP); - btor(BT848_SCLOOP_CAGC, BT848_O_SCLOOP); - } else { - btand(~BT848_SCLOOP_CAGC, BT848_E_SCLOOP); - btand(~BT848_SCLOOP_CAGC, BT848_O_SCLOOP); - } + case V4L2_CID_CHROMA_AGC: + val = c->val ? BT848_SCLOOP_CAGC : 0; + btwrite(val, BT848_E_SCLOOP); + btwrite(val, BT848_O_SCLOOP); break; case V4L2_CID_PRIVATE_COMBFILTER: - btv->opt_combfilter = c->value; + btv->opt_combfilter = c->val; break; case V4L2_CID_PRIVATE_LUMAFILTER: - btv->opt_lumafilter = c->value; - if (btv->opt_lumafilter) { + if (c->val) { btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL); btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL); } else { @@ -1604,36 +1322,31 @@ static int bttv_s_ctrl(struct file *file, void *f, } break; case V4L2_CID_PRIVATE_AUTOMUTE: - btv->opt_automute = c->value; + btv->opt_automute = c->val; break; case V4L2_CID_PRIVATE_AGC_CRUSH: - btv->opt_adc_crush = c->value; btwrite(BT848_ADC_RESERVED | - (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0), + (c->val ? BT848_ADC_CRUSH : 0), BT848_ADC); break; case V4L2_CID_PRIVATE_VCR_HACK: - btv->opt_vcr_hack = c->value; + btv->opt_vcr_hack = c->val; break; case V4L2_CID_PRIVATE_WHITECRUSH_UPPER: - btv->opt_whitecrush_upper = c->value; - btwrite(c->value, BT848_WC_UP); + btwrite(c->val, BT848_WC_UP); break; case V4L2_CID_PRIVATE_WHITECRUSH_LOWER: - btv->opt_whitecrush_lower = c->value; - btwrite(c->value, BT848_WC_DOWN); + btwrite(c->val, BT848_WC_DOWN); break; case V4L2_CID_PRIVATE_UV_RATIO: - btv->opt_uv_ratio = c->value; + btv->opt_uv_ratio = c->val; bt848_sat(btv, btv->saturation); break; case V4L2_CID_PRIVATE_FULL_LUMA_RANGE: - btv->opt_full_luma_range = c->value; - btaor((c->value<<7), ~BT848_OFORM_RANGE, BT848_OFORM); + btaor((c->val << 7), ~BT848_OFORM_RANGE, BT848_OFORM); break; case V4L2_CID_PRIVATE_CORING: - btv->opt_coring = c->value; - btaor((c->value<<5), ~BT848_OFORM_CORE32, BT848_OFORM); + btaor((c->val << 5), ~BT848_OFORM_CORE32, BT848_OFORM); break; default: return -EINVAL; @@ -1641,6 +1354,121 @@ static int bttv_s_ctrl(struct file *file, void *f, return 0; } +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops bttv_ctrl_ops = { + .s_ctrl = bttv_s_ctrl, +}; + +static struct v4l2_ctrl_config bttv_ctrl_combfilter = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_COMBFILTER, + .name = "Comb Filter", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_automute = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_AUTOMUTE, + .name = "Auto Mute", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_lumafilter = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_LUMAFILTER, + .name = "Luma Decimation Filter", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_agc_crush = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_AGC_CRUSH, + .name = "AGC Crush", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_vcr_hack = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_VCR_HACK, + .name = "VCR Hack", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, + .def = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_whitecrush_lower = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER, + .name = "Whitecrush Lower", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 255, + .step = 1, + .def = 0x7f, +}; + +static struct v4l2_ctrl_config bttv_ctrl_whitecrush_upper = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER, + .name = "Whitecrush Upper", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 255, + .step = 1, + .def = 0xcf, +}; + +static struct v4l2_ctrl_config bttv_ctrl_uv_ratio = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_UV_RATIO, + .name = "UV Ratio", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 100, + .step = 1, + .def = 50, +}; + +static struct v4l2_ctrl_config bttv_ctrl_full_luma = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_FULL_LUMA_RANGE, + .name = "Full Luma Range", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, +}; + +static struct v4l2_ctrl_config bttv_ctrl_coring = { + .ops = &bttv_ctrl_ops, + .id = V4L2_CID_PRIVATE_CORING, + .name = "Coring", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 3, + .step = 1, +}; + + /* ----------------------------------------------------------------------- */ void bttv_gpio_tracking(struct bttv *btv, char *comment) @@ -2047,9 +1875,11 @@ static int bttv_s_frequency(struct file *file, void *priv, static int bttv_log_status(struct file *file, void *f) { + struct video_device *vdev = video_devdata(file); struct bttv_fh *fh = f; struct bttv *btv = fh->btv; + v4l2_ctrl_handler_log_status(vdev->ctrl_handler, btv->c.v4l2_dev.name); bttv_call_all(btv, core, log_status); return 0; } @@ -2939,30 +2769,6 @@ static int bttv_streamoff(struct file *file, void *priv, return 0; } -static int bttv_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - struct bttv_fh *fh = priv; - struct bttv *btv = fh->btv; - const struct v4l2_queryctrl *ctrl; - - if ((c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) && - (c->id < V4L2_CID_PRIVATE_BASE || - c->id >= V4L2_CID_PRIVATE_LASTP1)) - return -EINVAL; - - if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME)) - *c = no_ctl; - else { - ctrl = ctrl_by_id(c->id); - - *c = (NULL != ctrl) ? *ctrl : no_ctl; - } - - return 0; -} - static int bttv_g_parm(struct file *file, void *f, struct v4l2_streamparm *parm) { @@ -3263,6 +3069,7 @@ static int bttv_open(struct file *file) fh = kmalloc(sizeof(*fh), GFP_KERNEL); if (unlikely(!fh)) return -ENOMEM; + btv->users++; file->private_data = fh; *fh = btv->init; @@ -3287,7 +3094,6 @@ static int bttv_open(struct file *file) set_tvnorm(btv,btv->tvnorm); set_input(btv, btv->input, btv->tvnorm); - btv->users++; /* The V4L2 spec requires one global set of cropping parameters which only change on request. These are stored in btv->crop[1]. @@ -3349,7 +3155,7 @@ static int bttv_release(struct file *file) bttv_field_count(btv); if (!btv->users) - audio_mute(btv, 1); + audio_mute(btv, btv->mute); return 0; } @@ -3400,9 +3206,6 @@ static const struct v4l2_ioctl_ops bttv_ioctl_ops = { .vidioc_enum_input = bttv_enum_input, .vidioc_g_input = bttv_g_input, .vidioc_s_input = bttv_s_input, - .vidioc_queryctrl = bttv_queryctrl, - .vidioc_g_ctrl = bttv_g_ctrl, - .vidioc_s_ctrl = bttv_s_ctrl, .vidioc_streamon = bttv_streamon, .vidioc_streamoff = bttv_streamoff, .vidioc_g_tuner = bttv_g_tuner, @@ -3511,24 +3314,6 @@ static int radio_s_tuner(struct file *file, void *priv, return 0; } -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - const struct v4l2_queryctrl *ctrl; - - if (c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) - return -EINVAL; - - if (c->id == V4L2_CID_AUDIO_MUTE) { - ctrl = ctrl_by_id(c->id); - *c = *ctrl; - } else - *c = no_ctl; - - return 0; -} - static ssize_t radio_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { @@ -3570,11 +3355,9 @@ static const struct v4l2_file_operations radio_fops = static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = bttv_querycap, + .vidioc_log_status = bttv_log_status, .vidioc_g_tuner = radio_g_tuner, .vidioc_s_tuner = radio_s_tuner, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = bttv_g_ctrl, - .vidioc_s_ctrl = bttv_s_ctrl, .vidioc_g_frequency = bttv_g_frequency, .vidioc_s_frequency = bttv_s_frequency, }; @@ -4220,6 +4003,7 @@ static int bttv_register_video(struct bttv *btv) btv->radio_dev = vdev_init(btv, &radio_template, "radio"); if (NULL == btv->radio_dev) goto err; + btv->radio_dev->ctrl_handler = &btv->radio_ctrl_handler; if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO, radio_nr[btv->c.nr]) < 0) goto err; @@ -4258,6 +4042,7 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) int result; unsigned char lat; struct bttv *btv; + struct v4l2_ctrl_handler *hdl; if (bttv_num == BTTV_MAX) return -ENOMEM; @@ -4317,6 +4102,10 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr); goto fail0; } + hdl = &btv->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 20); + btv->c.v4l2_dev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(&btv->radio_ctrl_handler, 6); btv->revision = dev->revision; pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); @@ -4353,16 +4142,19 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) /* init options from insmod args */ btv->opt_combfilter = combfilter; - btv->opt_lumafilter = lumafilter; + bttv_ctrl_combfilter.def = combfilter; + bttv_ctrl_lumafilter.def = lumafilter; btv->opt_automute = automute; - btv->opt_chroma_agc = chroma_agc; - btv->opt_adc_crush = adc_crush; + bttv_ctrl_automute.def = automute; + bttv_ctrl_agc_crush.def = agc_crush; btv->opt_vcr_hack = vcr_hack; - btv->opt_whitecrush_upper = whitecrush_upper; - btv->opt_whitecrush_lower = whitecrush_lower; + bttv_ctrl_vcr_hack.def = vcr_hack; + bttv_ctrl_whitecrush_upper.def = whitecrush_upper; + bttv_ctrl_whitecrush_lower.def = whitecrush_lower; btv->opt_uv_ratio = uv_ratio; - btv->opt_full_luma_range = full_luma_range; - btv->opt_coring = coring; + bttv_ctrl_uv_ratio.def = uv_ratio; + bttv_ctrl_full_luma.def = full_luma_range; + bttv_ctrl_coring.def = coring; /* fill struct bttv with some useful defaults */ btv->init.btv = btv; @@ -4373,6 +4165,34 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) btv->init.height = 240; btv->input = 0; + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 0xff00, 0x100, 32768); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_CONTRAST, 0, 0xff80, 0x80, 0x6c00); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_SATURATION, 0, 0xff80, 0x80, 32768); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_COLOR_KILLER, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_HUE, 0, 0xff00, 0x100, 32768); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_CHROMA_AGC, 0, 1, 1, !!chroma_agc); + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + if (btv->volume_gpio) + v4l2_ctrl_new_std(hdl, &bttv_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 0xff00, 0x100, 0xff00); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_combfilter, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_automute, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_lumafilter, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_agc_crush, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_vcr_hack, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_whitecrush_lower, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_whitecrush_upper, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_uv_ratio, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_full_luma, NULL); + v4l2_ctrl_new_custom(hdl, &bttv_ctrl_coring, NULL); + /* initialize hardware */ if (bttv_gpio) bttv_gpio_tracking(btv,"pre-init"); @@ -4400,20 +4220,26 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) btv->radio_freq = 90500 * 16; /* 90.5Mhz default */ } init_irqreg(btv); + v4l2_ctrl_handler_setup(hdl); + if (hdl->error) { + result = hdl->error; + goto fail2; + } /* register video4linux + input */ if (!bttv_tvcards[btv->c.type].no_video) { - bttv_register_video(btv); - bt848_bright(btv,32768); - bt848_contrast(btv, 27648); - bt848_hue(btv,32768); - bt848_sat(btv,32768); - audio_mute(btv, 1); + v4l2_ctrl_add_handler(&btv->radio_ctrl_handler, hdl, + v4l2_ctrl_radio_filter); + if (btv->radio_ctrl_handler.error) { + result = btv->radio_ctrl_handler.error; + goto fail2; + } set_input(btv, 0, btv->tvnorm); bttv_crop_reset(&btv->crop[0], btv->tvnorm); btv->crop[1] = btv->crop[0]; /* current = default */ disclaim_vbi_lines(btv); disclaim_video_lines(btv); + bttv_register_video(btv); } /* add subdevices and autoload dvb-bt8xx if needed */ @@ -4435,6 +4261,8 @@ fail2: free_irq(btv->c.pci->irq,btv); fail1: + v4l2_ctrl_handler_free(&btv->ctrl_handler); + v4l2_ctrl_handler_free(&btv->radio_ctrl_handler); v4l2_device_unregister(&btv->c.v4l2_dev); fail0: @@ -4476,9 +4304,11 @@ static void bttv_remove(struct pci_dev *pci_dev) bttv_unregister_video(btv); /* free allocated memory */ + v4l2_ctrl_handler_free(&btv->ctrl_handler); + v4l2_ctrl_handler_free(&btv->radio_ctrl_handler); btcx_riscmem_free(btv->c.pci,&btv->main); - /* free ressources */ + /* free resources */ free_irq(btv->c.pci->irq,btv); iounmap(btv->bt848_mmio); release_mem_region(pci_resource_start(btv->c.pci,0), diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h index 528e03ec19e..c3882ef3c52 100644 --- a/drivers/media/pci/bt8xx/bttvp.h +++ b/drivers/media/pci/bt8xx/bttvp.h @@ -33,9 +33,10 @@ #include #include #include +#include #include #include -#include +#include #include #include #include @@ -393,12 +394,17 @@ struct bttv { wait_queue_head_t i2c_queue; struct v4l2_subdev *sd_msp34xx; struct v4l2_subdev *sd_tvaudio; + struct v4l2_subdev *sd_tda7432; /* video4linux (1) */ struct video_device *video_dev; struct video_device *radio_dev; struct video_device *vbi_dev; + /* controls */ + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl_handler radio_ctrl_handler; + /* infrared remote */ int has_remote; struct bttv_ir *remote; @@ -426,17 +432,9 @@ struct bttv { /* various options */ int opt_combfilter; - int opt_lumafilter; int opt_automute; - int opt_chroma_agc; - int opt_color_killer; - int opt_adc_crush; int opt_vcr_hack; - int opt_whitecrush_upper; - int opt_whitecrush_lower; int opt_uv_ratio; - int opt_full_luma_range; - int opt_coring; /* radio data/state */ int has_radio; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index dcd63745e83..1d00ca9f0ba 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -146,6 +146,11 @@ enum v4l2_colorfx { * of controls. We reserve 16 controls for this driver. */ #define V4L2_CID_USER_MEYE_BASE (V4L2_CID_USER_BASE + 0x1000) +/* The base for the bttv driver controls. + * We reserve 32 controls for this driver. */ +#define V4L2_CID_USER_BTTV_BASE (V4L2_CID_USER_BASE + 0x1010) + + /* MPEG-class control IDs */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) -- cgit v1.2.3-70-g09d2 From 7d8e0bf56a66bab08d2f316dd87e56c08cecb899 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 10:57:03 +0800 Subject: cgroup: avoid accessing modular cgroup subsys structure without locking subsys[i] is set to NULL in cgroup_unload_subsys() at modular unload, and that's protected by cgroup_mutex, and then the memory *subsys[i] resides will be freed. So this is unsafe without any locking: if (!ss || ss->module) ... v2: - add a comment for enum cgroup_subsys_id - simplify the comment in cgroup_exit() Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 17 ++++++++++++++--- kernel/cgroup.c | 28 ++++++++++++++-------------- 2 files changed, 28 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 75c6ec1ba1b..5f76829dd75 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -44,14 +44,25 @@ extern void cgroup_unload_subsys(struct cgroup_subsys *ss); extern const struct file_operations proc_cgroup_operations; -/* Define the enumeration of all builtin cgroup subsystems */ +/* + * Define the enumeration of all cgroup subsystems. + * + * We define ids for builtin subsystems and then modular ones. + */ #define SUBSYS(_x) _x ## _subsys_id, -#define IS_SUBSYS_ENABLED(option) IS_ENABLED(option) enum cgroup_subsys_id { +#define IS_SUBSYS_ENABLED(option) IS_BUILTIN(option) +#include +#undef IS_SUBSYS_ENABLED + CGROUP_BUILTIN_SUBSYS_COUNT, + + __CGROUP_SUBSYS_TEMP_PLACEHOLDER = CGROUP_BUILTIN_SUBSYS_COUNT - 1, + +#define IS_SUBSYS_ENABLED(option) IS_MODULE(option) #include +#undef IS_SUBSYS_ENABLED CGROUP_SUBSYS_COUNT, }; -#undef IS_SUBSYS_ENABLED #undef SUBSYS /* Per-subsystem/per-cgroup state maintained by the system. */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 9df799d5d31..7a6c4c72ca5 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4940,17 +4940,17 @@ void cgroup_post_fork(struct task_struct *child) * and addition to css_set. */ if (need_forkexit_callback) { - for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { + /* + * fork/exit callbacks are supported only for builtin + * subsystems, and the builtin section of the subsys + * array is immutable, so we don't need to lock the + * subsys array here. On the other hand, modular section + * of the array can be freed at module unload, so we + * can't touch that. + */ + for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) { struct cgroup_subsys *ss = subsys[i]; - /* - * fork/exit callbacks are supported only for - * builtin subsystems and we don't need further - * synchronization as they never go away. - */ - if (!ss || ss->module) - continue; - if (ss->fork) ss->fork(child); } @@ -5015,13 +5015,13 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks) tsk->cgroups = &init_css_set; if (run_callbacks && need_forkexit_callback) { - for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { + /* + * fork/exit callbacks are supported only for builtin + * subsystems, see cgroup_post_fork() for details. + */ + for (i = 0; i < CGROUP_BUILTIN_SUBSYS_COUNT; i++) { struct cgroup_subsys *ss = subsys[i]; - /* modular subsystems can't use callbacks */ - if (!ss || ss->module) - continue; - if (ss->exit) { struct cgroup *old_cgrp = rcu_dereference_raw(cg->subsys[i])->cgroup; -- cgit v1.2.3-70-g09d2 From 9259826ccb8165f797e4c2c9d17925b41af5f6ae Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 11:37:50 +0800 Subject: res_counter: remove include of cgroup.h from res_counter.h It's not needed at all. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- include/linux/res_counter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index 5ae8456d967..a83a849bf1d 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h @@ -13,7 +13,7 @@ * info about what this counter is. */ -#include +#include /* * The core object. the cgroup that wishes to account for some -- cgit v1.2.3-70-g09d2 From ff794dea52eaaa09017efea688a1d7f92ab0818e Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 11:37:56 +0800 Subject: cpuset: remove include of cgroup.h from cpuset.h We don't need to include cgroup.h in cpuset.h. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- include/linux/cpuset.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 8c8a60d2940..ccd1de8ad82 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #ifdef CONFIG_CPUSETS -- cgit v1.2.3-70-g09d2 From 53bf0f446bc387eabdd535dca080789cc74607f4 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Fri, 25 Jan 2013 06:29:56 -0300 Subject: [media] v4l: Define video buffer flag for the COPY timestamp type Define video buffer flag for the COPY timestamp. In this case the timestamp value is copied from the OUTPUT to the corresponding CAPTURE buffer. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Reviewed-by: Sylwester Nawrocki Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/io.xml | 6 ++++++ include/uapi/linux/videodev2.h | 1 + 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index e6c58559ca6..2c4c068dde8 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -1145,6 +1145,12 @@ in which case caches have not been used. same clock outside V4L2, use clock_gettime(2) . + + V4L2_BUF_FLAG_TIMESTAMP_COPY + 0x4000 + The CAPTURE buffer timestamp has been taken from the + corresponding OUTPUT buffer. This flag applies only to mem2mem devices. + diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 234d1d87091..b5f5cddcf1c 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -705,6 +705,7 @@ struct v4l2_buffer { #define V4L2_BUF_FLAG_TIMESTAMP_MASK 0xe000 #define V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN 0x0000 #define V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC 0x2000 +#define V4L2_BUF_FLAG_TIMESTAMP_COPY 0x4000 /** * struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor -- cgit v1.2.3-70-g09d2 From 6aa69f99b2ecc7f9b387fcf22d30e6601b58819f Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Fri, 25 Jan 2013 06:29:57 -0300 Subject: [media] vb2: Add support for non monotonic timestamps Not all drivers use monotonic timestamps. This patch adds a way to set the timestamp type per every queue. In addition, set proper timestamp type in drivers that I am sure that use either MONOTONIC or COPY timestamps. Other drivers will correctly report UNKNOWN timestamp type instead of assuming that all drivers use monotonic timestamps. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Reviewed-by: Sylwester Nawrocki Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/blackfin/bfin_capture.c | 1 + drivers/media/platform/davinci/vpbe_display.c | 1 + drivers/media/platform/davinci/vpif_capture.c | 1 + drivers/media/platform/davinci/vpif_display.c | 1 + drivers/media/platform/s3c-camif/camif-capture.c | 1 + drivers/media/platform/s5p-fimc/fimc-capture.c | 1 + drivers/media/platform/s5p-fimc/fimc-lite.c | 1 + drivers/media/platform/s5p-mfc/s5p_mfc.c | 2 ++ drivers/media/platform/soc_camera/atmel-isi.c | 1 + drivers/media/platform/soc_camera/mx2_camera.c | 1 + drivers/media/platform/soc_camera/mx3_camera.c | 1 + drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c | 1 + drivers/media/platform/vivi.c | 1 + drivers/media/usb/pwc/pwc-if.c | 1 + drivers/media/usb/stk1160/stk1160-v4l.c | 1 + drivers/media/usb/uvc/uvc_queue.c | 1 + drivers/media/v4l2-core/videobuf2-core.c | 8 ++++++-- include/media/videobuf2-core.h | 1 + 18 files changed, 24 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 5f209d5810d..8ffe42aabd8 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -1029,6 +1029,7 @@ static int bcap_probe(struct platform_device *pdev) q->buf_struct_size = sizeof(struct bcap_buffer); q->ops = &bcap_video_qops; q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vb2_queue_init(q); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 5e6b0cab514..9f9f2c1a073 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1404,6 +1404,7 @@ static int vpbe_display_reqbufs(struct file *file, void *priv, q->ops = &video_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpbe_disp_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) { diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 5892d2bc8ee..1943e41f386 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1035,6 +1035,7 @@ static int vpif_reqbufs(struct file *file, void *priv, q->ops = &video_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpif_cap_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) { diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index dd249c96126..5477c2cb865 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1001,6 +1001,7 @@ static int vpif_reqbufs(struct file *file, void *priv, q->ops = &video_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct vpif_disp_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) { diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index a55793c3d81..e91f350929e 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -1153,6 +1153,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct camif_buffer); q->drv_priv = vp; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index f553cc2a8ee..87b68420f77 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -1797,6 +1797,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, q->ops = &fimc_capture_qops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct fimc_vid_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index bfc4206935c..47fbf7bcf60 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -1325,6 +1325,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct flite_buffer); q->drv_priv = fimc; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret < 0) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e84703c314c..92c6bf11af7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -804,6 +804,7 @@ static int s5p_mfc_open(struct file *file) goto err_queue_init; } q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(q); if (ret) { mfc_err("Failed to initialize videobuf2 queue(capture)\n"); @@ -825,6 +826,7 @@ static int s5p_mfc_open(struct file *file) goto err_queue_init; } q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; ret = vb2_queue_init(q); if (ret) { mfc_err("Failed to initialize videobuf2 queue(output)\n"); diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 82dbf99d347..c314ff9b98d 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -514,6 +514,7 @@ static int isi_camera_init_videobuf(struct vb2_queue *q, q->buf_struct_size = sizeof(struct frame_buffer); q->ops = &isi_video_qops; q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index ffba7d91f41..048c26a27c3 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -797,6 +797,7 @@ static int mx2_camera_init_videobuf(struct vb2_queue *q, q->ops = &mx2_videobuf_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct mx2_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index f5cbb92db54..2c3bd69fb38 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -455,6 +455,7 @@ static int mx3_camera_init_videobuf(struct vb2_queue *q, q->ops = &mx3_videobuf_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct mx3_camera_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index bb08a46432f..973e72b24fa 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -2026,6 +2026,7 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, q->ops = &sh_mobile_ceu_videobuf_ops; q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer); + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; return vb2_queue_init(q); } diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 8a33a712f48..c46d2e8677a 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -1429,6 +1429,7 @@ static int __init vivi_create_instance(int inst) q->buf_struct_size = sizeof(struct vivi_buffer); q->ops = &vivi_video_qops; q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 5ec15cb1ed2..77bbf788965 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1001,6 +1001,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf); pdev->vb_queue.ops = &pwc_vb_queue_ops; pdev->vb_queue.mem_ops = &vb2_vmalloc_memops; + pdev->vb_queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; rc = vb2_queue_init(&pdev->vb_queue); if (rc < 0) { PWC_ERROR("Oops, could not initialize vb2 queue.\n"); diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 6694f9e2ca5..5307a63e378 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -687,6 +687,7 @@ int stk1160_vb2_setup(struct stk1160 *dev) q->buf_struct_size = sizeof(struct stk1160_buffer); q->ops = &stk1160_video_qops; q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; rc = vb2_queue_init(q); if (rc < 0) diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 6c233a54ce4..cd962be860c 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -149,6 +149,7 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.ops = &uvc_queue_qops; queue->queue.mem_ops = &vb2_vmalloc_memops; + queue->queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ret = vb2_queue_init(&queue->queue); if (ret) return ret; diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index db1235dcb32..be0448161c6 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -403,7 +403,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) * Clear any buffer state related flags. */ b->flags &= ~V4L2_BUFFER_MASK_FLAGS; - b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + b->flags |= q->timestamp_type; switch (vb->state) { case VB2_BUF_STATE_QUEUED: @@ -2039,9 +2039,13 @@ int vb2_queue_init(struct vb2_queue *q) WARN_ON(!q->type) || WARN_ON(!q->io_modes) || WARN_ON(!q->ops->queue_setup) || - WARN_ON(!q->ops->buf_queue)) + WARN_ON(!q->ops->buf_queue) || + WARN_ON(q->timestamp_type & ~V4L2_BUF_FLAG_TIMESTAMP_MASK)) return -EINVAL; + /* Warn that the driver should choose an appropriate timestamp type */ + WARN_ON(q->timestamp_type == V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN); + INIT_LIST_HEAD(&q->queued_list); INIT_LIST_HEAD(&q->done_list); spin_lock_init(&q->done_lock); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 9cfd4ee9e56..a2d42745078 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -326,6 +326,7 @@ struct vb2_queue { const struct vb2_mem_ops *mem_ops; void *drv_priv; unsigned int buf_struct_size; + u32 timestamp_type; /* private: internal use only */ enum v4l2_memory memory; -- cgit v1.2.3-70-g09d2 From 192f1e78cb9cbc1a2cee866f5e03a52857e648b6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 15 Feb 2013 05:51:21 -0300 Subject: [media] s2255: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/s2255/s2255drv.c | 172 ++++++++++++------------------------- include/uapi/linux/v4l2-controls.h | 4 + 2 files changed, 59 insertions(+), 117 deletions(-) (limited to 'include') diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 498c57ea5d3..2dcb29b647f 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -217,6 +218,7 @@ struct s2255_dev; struct s2255_channel { struct video_device vdev; + struct v4l2_ctrl_handler hdl; int resources; struct s2255_dmaqueue vidq; struct s2255_bufferi buffer; @@ -336,7 +338,7 @@ struct s2255_fh { */ #define S2255_V4L2_YC_ON 1 #define S2255_V4L2_YC_OFF 0 -#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_S2255_COLORFILTER (V4L2_CID_USER_S2255_BASE + 0) /* frame prefix size (sent once every frame) */ #define PREFIX_SIZE 512 @@ -810,28 +812,6 @@ static void res_free(struct s2255_fh *fh) dprintk(1, "res: put\n"); } -static int vidioc_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *qmenu) -{ - static const char *colorfilter[] = { - "Off", - "On", - NULL - }; - if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) { - int i; - const char **menu_items = colorfilter; - for (i = 0; i < qmenu->index && menu_items[i]; i++) - ; /* do nothing (from v4l2-common.c) */ - if (menu_items[i] == NULL || menu_items[i][0] == '\0') - return -EINVAL; - strlcpy(qmenu->name, menu_items[qmenu->index], - sizeof(qmenu->name)); - return 0; - } - return v4l2_ctrl_query_menu(qmenu, NULL, NULL); -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1427,109 +1407,32 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } -/* --- controls ---------------------------------------------- */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static int s2255_s_ctrl(struct v4l2_ctrl *ctrl) { - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - struct s2255_dev *dev = fh->dev; - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT); - break; - case V4L2_CID_CONTRAST: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST); - break; - case V4L2_CID_SATURATION: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION); - break; - case V4L2_CID_HUE: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE); - break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; - strlcpy(qc->name, "Color Filter", sizeof(qc->name)); - qc->type = V4L2_CTRL_TYPE_MENU; - qc->minimum = 0; - qc->maximum = 1; - qc->step = 1; - qc->default_value = 1; - qc->flags = 0; - break; - default: - return -EINVAL; - } - dprintk(4, "%s, id %d\n", __func__, qc->id); - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - struct s2255_channel *channel = fh->channel; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = channel->mode.bright; - break; - case V4L2_CID_CONTRAST: - ctrl->value = channel->mode.contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = channel->mode.saturation; - break; - case V4L2_CID_HUE: - ctrl->value = channel->mode.hue; - break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; - ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16); - break; - default: - return -EINVAL; - } - dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + struct s2255_channel *channel = + container_of(ctrl->handler, struct s2255_channel, hdl); struct s2255_mode mode; + mode = channel->mode; dprintk(4, "%s\n", __func__); + /* update the mode to the corresponding value */ switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - mode.bright = ctrl->value; + mode.bright = ctrl->val; break; case V4L2_CID_CONTRAST: - mode.contrast = ctrl->value; + mode.contrast = ctrl->val; break; case V4L2_CID_HUE: - mode.hue = ctrl->value; + mode.hue = ctrl->val; break; case V4L2_CID_SATURATION: - mode.saturation = ctrl->value; + mode.saturation = ctrl->val; break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; + case V4L2_CID_S2255_COLORFILTER: mode.color &= ~MASK_INPUT_TYPE; - mode.color |= ((ctrl->value ? 0 : 1) << 16); + mode.color |= !ctrl->val << 16; break; default: return -EINVAL; @@ -1539,7 +1442,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, some V4L programs restart stream unnecessarily after a s_crtl. */ - s2255_set_mode(fh->channel, &mode); + s2255_set_mode(channel, &mode); return 0; } @@ -1886,7 +1789,6 @@ static const struct v4l2_file_operations s2255_fops_v4l = { }; static const struct v4l2_ioctl_ops s2255_ioctl_ops = { - .vidioc_querymenu = vidioc_querymenu, .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1900,9 +1802,6 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_s_jpegcomp = vidioc_s_jpegcomp, @@ -1915,8 +1814,13 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = { static void s2255_video_device_release(struct video_device *vdev) { struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); - dprintk(4, "%s, chnls: %d \n", __func__, + struct s2255_channel *channel = + container_of(vdev, struct s2255_channel, vdev); + + v4l2_ctrl_handler_free(&channel->hdl); + dprintk(4, "%s, chnls: %d\n", __func__, atomic_read(&dev->num_channels)); + if (atomic_dec_and_test(&dev->num_channels)) s2255_destroy(dev); return; @@ -1931,6 +1835,20 @@ static struct video_device template = { .current_norm = V4L2_STD_NTSC_M, }; +static const struct v4l2_ctrl_ops s2255_ctrl_ops = { + .s_ctrl = s2255_s_ctrl, +}; + +static const struct v4l2_ctrl_config color_filter_ctrl = { + .ops = &s2255_ctrl_ops, + .name = "Color Filter", + .id = V4L2_CID_S2255_COLORFILTER, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, + .def = 1, +}; + static int s2255_probe_v4l(struct s2255_dev *dev) { int ret; @@ -1945,9 +1863,29 @@ static int s2255_probe_v4l(struct s2255_dev *dev) for (i = 0; i < MAX_CHANNELS; i++) { channel = &dev->channel[i]; INIT_LIST_HEAD(&channel->vidq.active); + + v4l2_ctrl_handler_init(&channel->hdl, 5); + v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops, + V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT); + v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST); + v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION); + v4l2_ctrl_new_std(&channel->hdl, &s2255_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, DEF_HUE); + if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER && + (dev->pid != 0x2257 || channel->idx <= 1)) + v4l2_ctrl_new_custom(&channel->hdl, &color_filter_ctrl, NULL); + if (channel->hdl.error) { + ret = channel->hdl.error; + v4l2_ctrl_handler_free(&channel->hdl); + dev_err(&dev->udev->dev, "couldn't register control\n"); + break; + } channel->vidq.dev = dev; /* register 4 video devices */ channel->vdev = template; + channel->vdev.ctrl_handler = &channel->hdl; channel->vdev.lock = &dev->lock; channel->vdev.v4l2_dev = &dev->v4l2_dev; video_set_drvdata(&channel->vdev, channel); diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 1d00ca9f0ba..7eab0b91827 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -151,6 +151,10 @@ enum v4l2_colorfx { #define V4L2_CID_USER_BTTV_BASE (V4L2_CID_USER_BASE + 0x1010) +/* The base for the s2255 driver controls. + * We reserve 8 controls for this driver. */ +#define V4L2_CID_USER_S2255_BASE (V4L2_CID_USER_BASE + 0x1010) + /* MPEG-class control IDs */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) -- cgit v1.2.3-70-g09d2 From e1fd1f490fa4213bd3060efa823a39d299538f72 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 5 Mar 2013 15:04:55 -0500 Subject: get rid of union semop in sys_semctl(2) arguments just have the bugger take unsigned long and deal with SETVAL case (when we use an int member in the union) explicitly. Signed-off-by: Al Viro --- arch/parisc/kernel/sys_parisc32.c | 15 ----- arch/parisc/kernel/syscall_table.S | 2 +- arch/sparc/kernel/sys_sparc_64.c | 2 +- include/linux/syscalls.h | 2 +- ipc/compat.c | 14 +++-- ipc/sem.c | 121 +++++++++++++++++++++++-------------- ipc/syscall.c | 6 +- 7 files changed, 91 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c index 46bdf6080fe..f517e08e7f0 100644 --- a/arch/parisc/kernel/sys_parisc32.c +++ b/arch/parisc/kernel/sys_parisc32.c @@ -60,21 +60,6 @@ asmlinkage long sys32_unimplemented(int r26, int r25, int r24, int r23, return -ENOSYS; } -asmlinkage long sys32_semctl(int semid, int semnum, int cmd, union semun arg) -{ - union semun u; - - if (cmd == SETVAL) { - /* Ugh. arg is a union of int,ptr,ptr,ptr, so is 8 bytes. - * The int should be in the first 4, but our argument - * frobbing has left it in the last 4. - */ - u.val = *((int *)&arg + 1); - return sys_semctl (semid, semnum, cmd, u); - } - return sys_semctl (semid, semnum, cmd, arg); -} - asmlinkage long compat_sys_fanotify_mark(int fan_fd, int flags, u32 mask_hi, u32 mask_lo, int fd, const char __user *pathname) diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index 30c9a3bba1c..0c9107285e6 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -282,7 +282,7 @@ ENTRY_COMP(recvmsg) ENTRY_SAME(semop) /* 185 */ ENTRY_SAME(semget) - ENTRY_DIFF(semctl) + ENTRY_COMP(semctl) ENTRY_COMP(msgsnd) ENTRY_COMP(msgrcv) ENTRY_SAME(msgget) /* 190 */ diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c index 42beb6fc4ad..2daaaa6eda2 100644 --- a/arch/sparc/kernel/sys_sparc_64.c +++ b/arch/sparc/kernel/sys_sparc_64.c @@ -353,7 +353,7 @@ SYSCALL_DEFINE6(sparc_ipc, unsigned int, call, int, first, unsigned long, second case SEMCTL: { err = sys_semctl(first, second, (int)third | IPC_64, - (union semun) ptr); + (unsigned long) ptr); goto out; } default: diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 9660a8bdcbb..65c001f7fa0 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -657,7 +657,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf); asmlinkage long sys_semget(key_t key, int nsems, int semflg); asmlinkage long sys_semop(int semid, struct sembuf __user *sops, unsigned nsops); -asmlinkage long sys_semctl(int semid, int semnum, int cmd, union semun arg); +asmlinkage long sys_semctl(int semid, int semnum, int cmd, unsigned long arg); asmlinkage long sys_semtimedop(int semid, struct sembuf __user *sops, unsigned nsops, const struct timespec __user *timeout); diff --git a/ipc/compat.c b/ipc/compat.c index 6cb6a4df86e..892f6585dd6 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -240,7 +240,7 @@ static inline int put_compat_semid_ds(struct semid64_ds *s, static long do_compat_semctl(int first, int second, int third, u32 pad) { - union semun fourth; + unsigned long fourth; int err, err2; struct semid64_ds s64; struct semid64_ds __user *up64; @@ -249,9 +249,13 @@ static long do_compat_semctl(int first, int second, int third, u32 pad) memset(&s64, 0, sizeof(s64)); if ((third & (~IPC_64)) == SETVAL) - fourth.val = (int) pad; +#ifdef __BIG_ENDIAN + fourth = (unsigned long)pad << 32; +#else + fourth = pad; +#endif else - fourth.__pad = compat_ptr(pad); + fourth = (unsigned long)compat_ptr(pad); switch (third & (~IPC_64)) { case IPC_INFO: case IPC_RMID: @@ -269,7 +273,7 @@ static long do_compat_semctl(int first, int second, int third, u32 pad) case IPC_STAT: case SEM_STAT: up64 = compat_alloc_user_space(sizeof(s64)); - fourth.__pad = up64; + fourth = (unsigned long)up64; err = sys_semctl(first, second, third, fourth); if (err < 0) break; @@ -295,7 +299,7 @@ static long do_compat_semctl(int first, int second, int third, u32 pad) if (err) break; - fourth.__pad = up64; + fourth = (unsigned long)up64; err = sys_semctl(first, second, third, fourth); break; diff --git a/ipc/sem.c b/ipc/sem.c index e7236df7a47..5b167d00efa 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -799,7 +799,7 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, } static int semctl_nolock(struct ipc_namespace *ns, int semid, - int cmd, int version, union semun arg) + int cmd, int version, void __user *p) { int err; struct sem_array *sma; @@ -834,7 +834,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, } max_id = ipc_get_maxid(&sem_ids(ns)); up_read(&sem_ids(ns).rw_mutex); - if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) + if (copy_to_user(p, &seminfo, sizeof(struct seminfo))) return -EFAULT; return (max_id < 0) ? 0: max_id; } @@ -871,7 +871,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; sem_unlock(sma); - if (copy_semid_to_user (arg.buf, &tbuf, version)) + if (copy_semid_to_user(p, &tbuf, version)) return -EFAULT; return id; } @@ -883,8 +883,67 @@ out_unlock: return err; } +static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, + unsigned long arg) +{ + struct sem_undo *un; + struct sem_array *sma; + struct sem* curr; + int err; + int nsems; + struct list_head tasks; + int val; +#if defined(CONFIG_64BIT) && defined(__BIG_ENDIAN) + /* big-endian 64bit */ + val = arg >> 32; +#else + /* 32bit or little-endian 64bit */ + val = arg; +#endif + + sma = sem_lock_check(ns, semid); + if (IS_ERR(sma)) + return PTR_ERR(sma); + + INIT_LIST_HEAD(&tasks); + nsems = sma->sem_nsems; + + err = -EACCES; + if (ipcperms(ns, &sma->sem_perm, S_IWUGO)) + goto out_unlock; + + err = security_sem_semctl(sma, SETVAL); + if (err) + goto out_unlock; + + err = -EINVAL; + if(semnum < 0 || semnum >= nsems) + goto out_unlock; + + curr = &sma->sem_base[semnum]; + + err = -ERANGE; + if (val > SEMVMX || val < 0) + goto out_unlock; + + assert_spin_locked(&sma->sem_perm.lock); + list_for_each_entry(un, &sma->list_id, list_id) + un->semadj[semnum] = 0; + + curr->semval = val; + curr->sempid = task_tgid_vnr(current); + sma->sem_ctime = get_seconds(); + /* maybe some queued-up processes were waiting for this */ + do_smart_update(sma, NULL, 0, 0, &tasks); + err = 0; +out_unlock: + sem_unlock(sma); + wake_up_sem_queue_do(&tasks); + return err; +} + static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, - int cmd, int version, union semun arg) + int cmd, void __user *p) { struct sem_array *sma; struct sem* curr; @@ -903,7 +962,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, err = -EACCES; if (ipcperms(ns, &sma->sem_perm, - (cmd == SETVAL || cmd == SETALL) ? S_IWUGO : S_IRUGO)) + cmd == SETALL ? S_IWUGO : S_IRUGO)) goto out_unlock; err = security_sem_semctl(sma, cmd); @@ -914,7 +973,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, switch (cmd) { case GETALL: { - ushort __user *array = arg.array; + ushort __user *array = p; int i; if(nsems > SEMMSL_FAST) { @@ -957,7 +1016,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, } } - if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) { + if (copy_from_user (sem_io, p, nsems*sizeof(ushort))) { sem_putref(sma); err = -EFAULT; goto out_free; @@ -991,7 +1050,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, err = 0; goto out_unlock; } - /* GETVAL, GETPID, GETNCTN, GETZCNT, SETVAL: fall-through */ + /* GETVAL, GETPID, GETNCTN, GETZCNT: fall-through */ } err = -EINVAL; if(semnum < 0 || semnum >= nsems) @@ -1012,27 +1071,6 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, case GETZCNT: err = count_semzcnt(sma,semnum); goto out_unlock; - case SETVAL: - { - int val = arg.val; - struct sem_undo *un; - - err = -ERANGE; - if (val > SEMVMX || val < 0) - goto out_unlock; - - assert_spin_locked(&sma->sem_perm.lock); - list_for_each_entry(un, &sma->list_id, list_id) - un->semadj[semnum] = 0; - - curr->semval = val; - curr->sempid = task_tgid_vnr(current); - sma->sem_ctime = get_seconds(); - /* maybe some queued-up processes were waiting for this */ - do_smart_update(sma, NULL, 0, 0, &tasks); - err = 0; - goto out_unlock; - } } out_unlock: sem_unlock(sma); @@ -1076,7 +1114,7 @@ copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version) * NOTE: no locks must be held, the rw_mutex is taken inside this function. */ static int semctl_down(struct ipc_namespace *ns, int semid, - int cmd, int version, union semun arg) + int cmd, int version, void __user *p) { struct sem_array *sma; int err; @@ -1084,7 +1122,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, struct kern_ipc_perm *ipcp; if(cmd == IPC_SET) { - if (copy_semid_from_user(&semid64, arg.buf, version)) + if (copy_semid_from_user(&semid64, p, version)) return -EFAULT; } @@ -1120,11 +1158,11 @@ out_up: return err; } -SYSCALL_DEFINE(semctl)(int semid, int semnum, int cmd, union semun arg) +SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) { - int err = -EINVAL; int version; struct ipc_namespace *ns; + void __user *p = (void __user *)arg; if (semid < 0) return -EINVAL; @@ -1137,30 +1175,23 @@ SYSCALL_DEFINE(semctl)(int semid, int semnum, int cmd, union semun arg) case SEM_INFO: case IPC_STAT: case SEM_STAT: - err = semctl_nolock(ns, semid, cmd, version, arg); - return err; + return semctl_nolock(ns, semid, cmd, version, p); case GETALL: case GETVAL: case GETPID: case GETNCNT: case GETZCNT: - case SETVAL: case SETALL: - err = semctl_main(ns,semid,semnum,cmd,version,arg); - return err; + return semctl_main(ns, semid, semnum, cmd, p); + case SETVAL: + return semctl_setval(ns, semid, semnum, arg); case IPC_RMID: case IPC_SET: - err = semctl_down(ns, semid, cmd, version, arg); - return err; + return semctl_down(ns, semid, cmd, version, p); default: return -EINVAL; } } -asmlinkage long SyS_semctl(int semid, int semnum, int cmd, union semun arg) -{ - return SYSC_semctl((int) semid, (int) semnum, (int) cmd, arg); -} -SYSCALL_ALIAS(sys_semctl, SyS_semctl); /* If the task doesn't already have a undo_list, then allocate one * here. We guarantee there is only one thread using this undo list, diff --git a/ipc/syscall.c b/ipc/syscall.c index 0d1e32ce048..52429489cde 100644 --- a/ipc/syscall.c +++ b/ipc/syscall.c @@ -33,12 +33,12 @@ SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second, case SEMGET: return sys_semget(first, second, third); case SEMCTL: { - union semun fourth; + unsigned long arg; if (!ptr) return -EINVAL; - if (get_user(fourth.__pad, (void __user * __user *) ptr)) + if (get_user(arg, (unsigned long __user *) ptr)) return -EFAULT; - return sys_semctl(first, second, third, fourth); + return sys_semctl(first, second, third, arg); } case MSGSND: -- cgit v1.2.3-70-g09d2 From 99e621f796d7f0341a51e8cdf32b81663b10b448 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 5 Mar 2013 15:36:40 -0500 Subject: syscalls.h: slightly reduce the jungles of macros a) teach __MAP(num, m, ) to take empty list (with num being 0, of course) b) fold types__... and args__... declaration and initialization into SYSCALL_METADATA(num, ...), making their use conditional on num != 0. That allows to use the SYSCALL_METADATA instead of its near-duplicate in SYSCALL_DEFINE0. c) make SYSCALL_METADATA expand to nothing in case if CONFIG_FTRACE_SYSCALLS is not defined; that allows to make SYSCALL_DEFINE0 and SYSCALL_DEFINEx definitions independent from CONFIG_FTRACE_SYSCALLS. d) kill SYSCALL_DEFINE - no users left (SYSCALL_DEFINE[0-6] is, of course, still alive and well). Signed-off-by: Al Viro --- include/linux/syscalls.h | 49 +++++++++++++++--------------------------------- 1 file changed, 15 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 65c001f7fa0..4147d700a29 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -87,6 +87,7 @@ struct sigaltstack; * of __MAP starting at the third one) is in the same format as * for SYSCALL_DEFINE/COMPAT_SYSCALL_DEFINE */ +#define __MAP0(m,...) #define __MAP1(m,t,a) m(t,a) #define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__) #define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__) @@ -139,7 +140,13 @@ extern struct trace_event_functions exit_syscall_print_funcs; __attribute__((section("_ftrace_events"))) \ *__event_exit_##sname = &event_exit_##sname; -#define SYSCALL_METADATA(sname, nb) \ +#define SYSCALL_METADATA(sname, nb, ...) \ + static const char *types_##sname[] = { \ + __MAP(nb,__SC_STR_TDECL,__VA_ARGS__) \ + }; \ + static const char *args_##sname[] = { \ + __MAP(nb,__SC_STR_ADECL,__VA_ARGS__) \ + }; \ SYSCALL_TRACE_ENTER_EVENT(sname); \ SYSCALL_TRACE_EXIT_EVENT(sname); \ static struct syscall_metadata __used \ @@ -147,8 +154,8 @@ extern struct trace_event_functions exit_syscall_print_funcs; .name = "sys"#sname, \ .syscall_nr = -1, /* Filled in at boot */ \ .nb_args = nb, \ - .types = types_##sname, \ - .args = args_##sname, \ + .types = nb ? types_##sname : NULL, \ + .args = nb ? args_##sname : NULL, \ .enter_event = &event_enter_##sname, \ .exit_event = &event_exit_##sname, \ .enter_fields = LIST_HEAD_INIT(__syscall_meta_##sname.enter_fields), \ @@ -156,26 +163,13 @@ extern struct trace_event_functions exit_syscall_print_funcs; static struct syscall_metadata __used \ __attribute__((section("__syscalls_metadata"))) \ *__p_syscall_meta_##sname = &__syscall_meta_##sname; +#else +#define SYSCALL_METADATA(sname, nb, ...) +#endif #define SYSCALL_DEFINE0(sname) \ - SYSCALL_TRACE_ENTER_EVENT(_##sname); \ - SYSCALL_TRACE_EXIT_EVENT(_##sname); \ - static struct syscall_metadata __used \ - __syscall_meta__##sname = { \ - .name = "sys_"#sname, \ - .syscall_nr = -1, /* Filled in at boot */ \ - .nb_args = 0, \ - .enter_event = &event_enter__##sname, \ - .exit_event = &event_exit__##sname, \ - .enter_fields = LIST_HEAD_INIT(__syscall_meta__##sname.enter_fields), \ - }; \ - static struct syscall_metadata __used \ - __attribute__((section("__syscalls_metadata"))) \ - *__p_syscall_meta_##sname = &__syscall_meta__##sname; \ + SYSCALL_METADATA(_##sname, 0); \ asmlinkage long sys_##sname(void) -#else -#define SYSCALL_DEFINE0(name) asmlinkage long sys_##name(void) -#endif #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) @@ -184,22 +178,9 @@ extern struct trace_event_functions exit_syscall_print_funcs; #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) -#ifdef CONFIG_FTRACE_SYSCALLS #define SYSCALL_DEFINEx(x, sname, ...) \ - static const char *types_##sname[] = { \ - __MAP(x,__SC_STR_TDECL,__VA_ARGS__) \ - }; \ - static const char *args_##sname[] = { \ - __MAP(x,__SC_STR_ADECL,__VA_ARGS__) \ - }; \ - SYSCALL_METADATA(sname, x); \ + SYSCALL_METADATA(sname, x, __VA_ARGS__) \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) -#else -#define SYSCALL_DEFINEx(x, sname, ...) \ - __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) -#endif - -#define SYSCALL_DEFINE(name) static inline long SYSC_##name #define __PROTECT(...) asmlinkage_protect(__VA_ARGS__) #define __SYSCALL_DEFINEx(x, name, ...) \ -- cgit v1.2.3-70-g09d2 From a0f155e9646d5f1c263f6f9aae880151100243bb Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 28 Feb 2013 12:33:18 +0100 Subject: KVM: Initialize irqfd from kvm_init(). Currently, eventfd introduces module_init/module_exit functions to initialize/cleanup the irqfd workqueue. This only works, however, if no other module_init/module_exit functions are built into the same module. Let's just move the initialization and cleanup to kvm_init and kvm_exit. This way, it is also clearer where kvm startup may fail. Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- include/linux/kvm_host.h | 13 +++++++++++++ virt/kvm/eventfd.c | 7 ++----- virt/kvm/kvm_main.c | 6 ++++++ 3 files changed, 21 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index ac584cc5358..d50fe173028 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -424,6 +424,19 @@ void kvm_vcpu_uninit(struct kvm_vcpu *vcpu); int __must_check vcpu_load(struct kvm_vcpu *vcpu); void vcpu_put(struct kvm_vcpu *vcpu); +#ifdef __KVM_HAVE_IOAPIC +int kvm_irqfd_init(void); +void kvm_irqfd_exit(void); +#else +static inline int kvm_irqfd_init(void) +{ + return 0; +} + +static inline void kvm_irqfd_exit(void) +{ +} +#endif int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, struct module *module); void kvm_exit(void); diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index adb17f266b2..0b6fe69bb03 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -543,7 +543,7 @@ void kvm_irq_routing_update(struct kvm *kvm, * aggregated from all vm* instances. We need our own isolated single-thread * queue to prevent deadlock against flushing the normal work-queue. */ -static int __init irqfd_module_init(void) +int kvm_irqfd_init(void) { irqfd_cleanup_wq = create_singlethread_workqueue("kvm-irqfd-cleanup"); if (!irqfd_cleanup_wq) @@ -552,13 +552,10 @@ static int __init irqfd_module_init(void) return 0; } -static void __exit irqfd_module_exit(void) +void kvm_irqfd_exit(void) { destroy_workqueue(irqfd_cleanup_wq); } - -module_init(irqfd_module_init); -module_exit(irqfd_module_exit); #endif /* diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 0e919a1d4d5..faf05bddd13 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2898,6 +2898,9 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, int r; int cpu; + r = kvm_irqfd_init(); + if (r) + goto out_irqfd; r = kvm_arch_init(opaque); if (r) goto out_fail; @@ -2978,6 +2981,8 @@ out_free_0a: out_free_0: kvm_arch_exit(); out_fail: + kvm_irqfd_exit(); +out_irqfd: return r; } EXPORT_SYMBOL_GPL(kvm_init); @@ -2994,6 +2999,7 @@ void kvm_exit(void) on_each_cpu(hardware_disable_nolock, NULL, 1); kvm_arch_hardware_unsetup(); kvm_arch_exit(); + kvm_irqfd_exit(); free_cpumask_var(cpus_hardware_enabled); } EXPORT_SYMBOL_GPL(kvm_exit); -- cgit v1.2.3-70-g09d2 From 060f0ce6ff975decd1e0ee318c08e228bccbee1e Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 28 Feb 2013 12:33:19 +0100 Subject: KVM: Introduce KVM_VIRTIO_CCW_NOTIFY_BUS. Add a new bus type for virtio-ccw devices on s390. Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- include/linux/kvm_host.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index d50fe173028..9fa13ebc338 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -149,6 +149,7 @@ struct kvm_io_bus { enum kvm_bus { KVM_MMIO_BUS, KVM_PIO_BUS, + KVM_VIRTIO_CCW_NOTIFY_BUS, KVM_NR_BUSES }; -- cgit v1.2.3-70-g09d2 From 2b83451b45d720ca38c03878ce42ff9139cad9e3 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 28 Feb 2013 12:33:20 +0100 Subject: KVM: ioeventfd for virtio-ccw devices. Enhance KVM_IOEVENTFD with a new flag that allows to attach to virtio-ccw devices on s390 via the KVM_VIRTIO_CCW_NOTIFY_BUS. Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- Documentation/virtual/kvm/api.txt | 8 ++++++++ include/uapi/linux/kvm.h | 3 +++ virt/kvm/eventfd.c | 17 +++++++++++++---- 3 files changed, 24 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 119358dfb74..c16b442556e 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1486,15 +1486,23 @@ struct kvm_ioeventfd { __u8 pad[36]; }; +For the special case of virtio-ccw devices on s390, the ioevent is matched +to a subchannel/virtqueue tuple instead. + The following flags are defined: #define KVM_IOEVENTFD_FLAG_DATAMATCH (1 << kvm_ioeventfd_flag_nr_datamatch) #define KVM_IOEVENTFD_FLAG_PIO (1 << kvm_ioeventfd_flag_nr_pio) #define KVM_IOEVENTFD_FLAG_DEASSIGN (1 << kvm_ioeventfd_flag_nr_deassign) +#define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \ + (1 << kvm_ioeventfd_flag_nr_virtio_ccw_notify) If datamatch flag is set, the event will be signaled only if the written value to the registered address is equal to datamatch in struct kvm_ioeventfd. +For virtio-ccw devices, addr contains the subchannel id and datamatch the +virtqueue index. + 4.60 KVM_DIRTY_TLB diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 3c56ba3d80c..74d0ff3dfd6 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -449,12 +449,15 @@ enum { kvm_ioeventfd_flag_nr_datamatch, kvm_ioeventfd_flag_nr_pio, kvm_ioeventfd_flag_nr_deassign, + kvm_ioeventfd_flag_nr_virtio_ccw_notify, kvm_ioeventfd_flag_nr_max, }; #define KVM_IOEVENTFD_FLAG_DATAMATCH (1 << kvm_ioeventfd_flag_nr_datamatch) #define KVM_IOEVENTFD_FLAG_PIO (1 << kvm_ioeventfd_flag_nr_pio) #define KVM_IOEVENTFD_FLAG_DEASSIGN (1 << kvm_ioeventfd_flag_nr_deassign) +#define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \ + (1 << kvm_ioeventfd_flag_nr_virtio_ccw_notify) #define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 << kvm_ioeventfd_flag_nr_max) - 1) diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 0b6fe69bb03..020522ed909 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -674,15 +674,24 @@ ioeventfd_check_collision(struct kvm *kvm, struct _ioeventfd *p) return false; } +static enum kvm_bus ioeventfd_bus_from_flags(__u32 flags) +{ + if (flags & KVM_IOEVENTFD_FLAG_PIO) + return KVM_PIO_BUS; + if (flags & KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY) + return KVM_VIRTIO_CCW_NOTIFY_BUS; + return KVM_MMIO_BUS; +} + static int kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) { - int pio = args->flags & KVM_IOEVENTFD_FLAG_PIO; - enum kvm_bus bus_idx = pio ? KVM_PIO_BUS : KVM_MMIO_BUS; + enum kvm_bus bus_idx; struct _ioeventfd *p; struct eventfd_ctx *eventfd; int ret; + bus_idx = ioeventfd_bus_from_flags(args->flags); /* must be natural-word sized */ switch (args->len) { case 1: @@ -757,12 +766,12 @@ fail: static int kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) { - int pio = args->flags & KVM_IOEVENTFD_FLAG_PIO; - enum kvm_bus bus_idx = pio ? KVM_PIO_BUS : KVM_MMIO_BUS; + enum kvm_bus bus_idx; struct _ioeventfd *p, *tmp; struct eventfd_ctx *eventfd; int ret = -ENOENT; + bus_idx = ioeventfd_bus_from_flags(args->flags); eventfd = eventfd_ctx_fdget(args->fd); if (IS_ERR(eventfd)) return PTR_ERR(eventfd); -- cgit v1.2.3-70-g09d2 From 82dc3c63c692b1e1d59378ecee948ac88e034aad Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 5 Mar 2013 15:57:22 +0000 Subject: net: introduce NAPI_POLL_WEIGHT Some drivers use a too big NAPI poll weight. This patch adds a NAPI_POLL_WEIGHT default value and issues an error message if a driver attempts to use a bigger weight. Signed-off-by: Eric Dumazet Cc: Eilon Greenstein Signed-off-by: David S. Miller --- include/linux/netdevice.h | 5 +++++ net/core/dev.c | 3 +++ 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b3d00fa4b31..896eb4985f9 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1475,6 +1475,11 @@ static inline void *netdev_priv(const struct net_device *dev) */ #define SET_NETDEV_DEVTYPE(net, devtype) ((net)->dev.type = (devtype)) +/* Default NAPI poll() weight + * Device drivers are strongly advised to not use bigger value + */ +#define NAPI_POLL_WEIGHT 64 + /** * netif_napi_add - initialize a napi context * @dev: network device diff --git a/net/core/dev.c b/net/core/dev.c index a06a7a58dd1..96103894ad6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4057,6 +4057,9 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi, napi->gro_list = NULL; napi->skb = NULL; napi->poll = poll; + if (weight > NAPI_POLL_WEIGHT) + pr_err_once("netif_napi_add() called with weight %d on device %s\n", + weight, dev->name); napi->weight = weight; list_add(&napi->dev_list, &dev->napi_list); napi->dev = dev; -- cgit v1.2.3-70-g09d2 From a947b0a93efa9a25c012aa88848f4cf8d9b41280 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Fri, 22 Feb 2013 10:54:54 +0100 Subject: xfrm: allow to avoid copying DSCP during encapsulation By default, DSCP is copying during encapsulation. Copying the DSCP in IPsec tunneling may be a bit dangerous because packets with different DSCP may get reordered relative to each other in the network and then dropped by the remote IPsec GW if the reordering becomes too big compared to the replay window. It is possible to avoid this copy with netfilter rules, but it's very convenient to be able to configure it for each SA directly. This patch adds a toogle for this purpose. By default, it's not set to maintain backward compatibility. Field flags in struct xfrm_usersa_info is full, hence I add a new attribute. Signed-off-by: Nicolas Dichtel Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 1 + include/uapi/linux/xfrm.h | 3 +++ net/ipv4/ipcomp.c | 1 + net/ipv4/xfrm4_mode_tunnel.c | 8 ++++++-- net/ipv6/xfrm6_mode_tunnel.c | 7 +++++-- net/xfrm/xfrm_state.c | 1 + net/xfrm/xfrm_user.c | 13 +++++++++++++ 7 files changed, 30 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 24c8886fd96..ae16531d0d3 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -162,6 +162,7 @@ struct xfrm_state { xfrm_address_t saddr; int header_len; int trailer_len; + u32 extra_flags; } props; struct xfrm_lifetime_cfg lft; diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index 28e493b5b94..a8cd6a4a297 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -297,6 +297,7 @@ enum xfrm_attr_type_t { XFRMA_MARK, /* struct xfrm_mark */ XFRMA_TFCPAD, /* __u32 */ XFRMA_REPLAY_ESN_VAL, /* struct xfrm_replay_esn */ + XFRMA_SA_EXTRA_FLAGS, /* __u32 */ __XFRMA_MAX #define XFRMA_MAX (__XFRMA_MAX - 1) @@ -367,6 +368,8 @@ struct xfrm_usersa_info { #define XFRM_STATE_ESN 128 }; +#define XFRM_SA_XFLAG_DONT_ENCAP_DSCP 1 + struct xfrm_usersa_id { xfrm_address_t daddr; __be32 spi; diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index f01d1b1aff7..59cb8c76905 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c @@ -75,6 +75,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x) t->props.mode = x->props.mode; t->props.saddr.a4 = x->props.saddr.a4; t->props.flags = x->props.flags; + t->props.extra_flags = x->props.extra_flags; memcpy(&t->mark, &x->mark, sizeof(t->mark)); if (xfrm_init_state(t)) diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index fe5189e2e11..eb1dd4d643f 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -103,8 +103,12 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family); - /* DS disclosed */ - top_iph->tos = INET_ECN_encapsulate(XFRM_MODE_SKB_CB(skb)->tos, + /* DS disclosing depends on XFRM_SA_XFLAG_DONT_ENCAP_DSCP */ + if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP) + top_iph->tos = 0; + else + top_iph->tos = XFRM_MODE_SKB_CB(skb)->tos; + top_iph->tos = INET_ECN_encapsulate(top_iph->tos, XFRM_MODE_SKB_CB(skb)->tos); flags = x->props.flags; diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c index 9bf6a74a71d..4770d515c2c 100644 --- a/net/ipv6/xfrm6_mode_tunnel.c +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -49,8 +49,11 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) sizeof(top_iph->flow_lbl)); top_iph->nexthdr = xfrm_af2proto(skb_dst(skb)->ops->family); - dsfield = XFRM_MODE_SKB_CB(skb)->tos; - dsfield = INET_ECN_encapsulate(dsfield, dsfield); + if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP) + dsfield = 0; + else + dsfield = XFRM_MODE_SKB_CB(skb)->tos; + dsfield = INET_ECN_encapsulate(dsfield, XFRM_MODE_SKB_CB(skb)->tos); if (x->props.flags & XFRM_STATE_NOECN) dsfield &= ~INET_ECN_MASK; ipv6_change_dsfield(top_iph, 0, dsfield); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 2c341bdaf47..78f66fa9244 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1187,6 +1187,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp) goto error; x->props.flags = orig->props.flags; + x->props.extra_flags = orig->props.extra_flags; x->curlft.add_time = orig->curlft.add_time; x->km.state = orig->km.state; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index fbd9e6cd0fd..204cba192af 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -515,6 +515,9 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, copy_from_user_state(x, p); + if (attrs[XFRMA_SA_EXTRA_FLAGS]) + x->props.extra_flags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]); + if ((err = attach_aead(&x->aead, &x->props.ealgo, attrs[XFRMA_ALG_AEAD]))) goto error; @@ -779,6 +782,13 @@ static int copy_to_user_state_extra(struct xfrm_state *x, copy_to_user_state(x, p); + if (x->props.extra_flags) { + ret = nla_put_u32(skb, XFRMA_SA_EXTRA_FLAGS, + x->props.extra_flags); + if (ret) + goto out; + } + if (x->coaddr) { ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr); if (ret) @@ -2302,6 +2312,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { [XFRMA_MARK] = { .len = sizeof(struct xfrm_mark) }, [XFRMA_TFCPAD] = { .type = NLA_U32 }, [XFRMA_REPLAY_ESN_VAL] = { .len = sizeof(struct xfrm_replay_state_esn) }, + [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, }; static struct xfrm_link { @@ -2495,6 +2506,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x) x->security->ctx_len); if (x->coaddr) l += nla_total_size(sizeof(*x->coaddr)); + if (x->props.extra_flags) + l += nla_total_size(sizeof(x->props.extra_flags)); /* Must count x->lastused as it may become non-zero behind our back. */ l += nla_total_size(sizeof(u64)); -- cgit v1.2.3-70-g09d2 From 1fd9c467b4f7e08beee41f9771396f39265f4c08 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Mar 2013 11:51:55 +0800 Subject: mfd: arizona: Define additional FLL control registers Signed-off-by: Mark Brown --- include/linux/mfd/arizona/registers.h | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 34035513606..a61ce90ecd3 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -85,12 +85,14 @@ #define ARIZONA_FLL1_CONTROL_6 0x176 #define ARIZONA_FLL1_LOOP_FILTER_TEST_1 0x177 #define ARIZONA_FLL1_NCO_TEST_0 0x178 +#define ARIZONA_FLL1_CONTROL_7 0x179 #define ARIZONA_FLL1_SYNCHRONISER_1 0x181 #define ARIZONA_FLL1_SYNCHRONISER_2 0x182 #define ARIZONA_FLL1_SYNCHRONISER_3 0x183 #define ARIZONA_FLL1_SYNCHRONISER_4 0x184 #define ARIZONA_FLL1_SYNCHRONISER_5 0x185 #define ARIZONA_FLL1_SYNCHRONISER_6 0x186 +#define ARIZONA_FLL1_SYNCHRONISER_7 0x187 #define ARIZONA_FLL1_SPREAD_SPECTRUM 0x189 #define ARIZONA_FLL1_GPIO_CLOCK 0x18A #define ARIZONA_FLL2_CONTROL_1 0x191 @@ -101,12 +103,14 @@ #define ARIZONA_FLL2_CONTROL_6 0x196 #define ARIZONA_FLL2_LOOP_FILTER_TEST_1 0x197 #define ARIZONA_FLL2_NCO_TEST_0 0x198 +#define ARIZONA_FLL2_CONTROL_7 0x199 #define ARIZONA_FLL2_SYNCHRONISER_1 0x1A1 #define ARIZONA_FLL2_SYNCHRONISER_2 0x1A2 #define ARIZONA_FLL2_SYNCHRONISER_3 0x1A3 #define ARIZONA_FLL2_SYNCHRONISER_4 0x1A4 #define ARIZONA_FLL2_SYNCHRONISER_5 0x1A5 #define ARIZONA_FLL2_SYNCHRONISER_6 0x1A6 +#define ARIZONA_FLL2_SYNCHRONISER_7 0x1A7 #define ARIZONA_FLL2_SPREAD_SPECTRUM 0x1A9 #define ARIZONA_FLL2_GPIO_CLOCK 0x1AA #define ARIZONA_MIC_CHARGE_PUMP_1 0x200 @@ -1677,6 +1681,13 @@ #define ARIZONA_FLL1_FRC_INTEG_VAL_SHIFT 0 /* FLL1_FRC_INTEG_VAL - [11:0] */ #define ARIZONA_FLL1_FRC_INTEG_VAL_WIDTH 12 /* FLL1_FRC_INTEG_VAL - [11:0] */ +/* + * R377 (0x179) - FLL1 Control 7 + */ +#define ARIZONA_FLL1_GAIN_MASK 0x003c /* FLL1_GAIN */ +#define ARIZONA_FLL1_GAIN_SHIFT 2 /* FLL1_GAIN */ +#define ARIZONA_FLL1_GAIN_WIDTH 4 /* FLL1_GAIN */ + /* * R385 (0x181) - FLL1 Synchroniser 1 */ @@ -1723,6 +1734,17 @@ #define ARIZONA_FLL1_CLK_SYNC_SRC_SHIFT 0 /* FLL1_CLK_SYNC_SRC - [3:0] */ #define ARIZONA_FLL1_CLK_SYNC_SRC_WIDTH 4 /* FLL1_CLK_SYNC_SRC - [3:0] */ +/* + * R391 (0x187) - FLL1 Synchroniser 7 + */ +#define ARIZONA_FLL1_SYNC_GAIN_MASK 0x003c /* FLL1_SYNC_GAIN */ +#define ARIZONA_FLL1_SYNC_GAIN_SHIFT 2 /* FLL1_SYNC_GAIN */ +#define ARIZONA_FLL1_SYNC_GAIN_WIDTH 4 /* FLL1_SYNC_GAIN */ +#define ARIZONA_FLL1_SYNC_BW 0x0001 /* FLL1_SYNC_BW */ +#define ARIZONA_FLL1_SYNC_BW_MASK 0x0001 /* FLL1_SYNC_BW */ +#define ARIZONA_FLL1_SYNC_BW_SHIFT 0 /* FLL1_SYNC_BW */ +#define ARIZONA_FLL1_SYNC_BW_WIDTH 1 /* FLL1_SYNC_BW */ + /* * R393 (0x189) - FLL1 Spread Spectrum */ @@ -1815,6 +1837,13 @@ #define ARIZONA_FLL2_FRC_INTEG_VAL_SHIFT 0 /* FLL2_FRC_INTEG_VAL - [11:0] */ #define ARIZONA_FLL2_FRC_INTEG_VAL_WIDTH 12 /* FLL2_FRC_INTEG_VAL - [11:0] */ +/* + * R409 (0x199) - FLL2 Control 7 + */ +#define ARIZONA_FLL2_GAIN_MASK 0x003c /* FLL2_GAIN */ +#define ARIZONA_FLL2_GAIN_SHIFT 2 /* FLL2_GAIN */ +#define ARIZONA_FLL2_GAIN_WIDTH 4 /* FLL2_GAIN */ + /* * R417 (0x1A1) - FLL2 Synchroniser 1 */ @@ -1861,6 +1890,17 @@ #define ARIZONA_FLL2_CLK_SYNC_SRC_SHIFT 0 /* FLL2_CLK_SYNC_SRC - [3:0] */ #define ARIZONA_FLL2_CLK_SYNC_SRC_WIDTH 4 /* FLL2_CLK_SYNC_SRC - [3:0] */ +/* + * R423 (0x1A7) - FLL2 Synchroniser 7 + */ +#define ARIZONA_FLL2_SYNC_GAIN_MASK 0x003c /* FLL2_SYNC_GAIN */ +#define ARIZONA_FLL2_SYNC_GAIN_SHIFT 2 /* FLL2_SYNC_GAIN */ +#define ARIZONA_FLL2_SYNC_GAIN_WIDTH 4 /* FLL2_SYNC_GAIN */ +#define ARIZONA_FLL2_SYNC_BW_MASK 0x0001 /* FLL2_SYNC_BW */ +#define ARIZONA_FLL2_SYNC_BW_MASK 0x0001 /* FLL2_SYNC_BW */ +#define ARIZONA_FLL2_SYNC_BW_SHIFT 0 /* FLL2_SYNC_BW */ +#define ARIZONA_FLL2_SYNC_BW_WIDTH 1 /* FLL2_SYNC_BW */ + /* * R425 (0x1A9) - FLL2 Spread Spectrum */ -- cgit v1.2.3-70-g09d2 From 19a37d1cd5465c10d669a296a2ea24b4c985363b Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 16:05:28 +0800 Subject: sched: Remove some dummy functions No one will call those functions if CONFIG_SCHED_DEBUG=n. Signed-off-by: Li Zefan Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5135A748.3050206@huawei.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index d35d2b6ddbf..2715fbb9ea8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -127,18 +127,6 @@ extern void proc_sched_show_task(struct task_struct *p, struct seq_file *m); extern void proc_sched_set_task(struct task_struct *p); extern void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq); -#else -static inline void -proc_sched_show_task(struct task_struct *p, struct seq_file *m) -{ -} -static inline void proc_sched_set_task(struct task_struct *p) -{ -} -static inline void -print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) -{ -} #endif /* -- cgit v1.2.3-70-g09d2 From 090b582f27ac7b6714661020033160130e5297bd Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 16:05:51 +0800 Subject: sched: Remove test_sd_parent() It's unused. Signed-off-by: Li Zefan Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5135A75F.4070202@huawei.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 2715fbb9ea8..e880d7d115e 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -959,15 +959,6 @@ extern void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], cpumask_var_t *alloc_sched_domains(unsigned int ndoms); void free_sched_domains(cpumask_var_t doms[], unsigned int ndoms); -/* Test a flag in parent sched domain */ -static inline int test_sd_parent(struct sched_domain *sd, int flag) -{ - if (sd->parent && (sd->parent->flags & flag)) - return 1; - - return 0; -} - unsigned long default_scale_freq_power(struct sched_domain *sd, int cpu); unsigned long default_scale_smt_power(struct sched_domain *sd, int cpu); -- cgit v1.2.3-70-g09d2 From cc1f4b1f3faed9f2040eff2a75f510b424b3cf18 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 16:06:09 +0800 Subject: sched: Move SCHED_LOAD_SHIFT macros to kernel/sched/sched.h They are used internally only. Signed-off-by: Li Zefan Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5135A771.4070104@huawei.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 25 ------------------------- kernel/sched/sched.h | 26 +++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index e880d7d115e..f8826d04fb1 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -755,31 +755,6 @@ enum cpu_idle_type { CPU_MAX_IDLE_TYPES }; -/* - * Increase resolution of nice-level calculations for 64-bit architectures. - * The extra resolution improves shares distribution and load balancing of - * low-weight task groups (eg. nice +19 on an autogroup), deeper taskgroup - * hierarchies, especially on larger systems. This is not a user-visible change - * and does not change the user-interface for setting shares/weights. - * - * We increase resolution only if we have enough bits to allow this increased - * resolution (i.e. BITS_PER_LONG > 32). The costs for increasing resolution - * when BITS_PER_LONG <= 32 are pretty high and the returns do not justify the - * increased costs. - */ -#if 0 /* BITS_PER_LONG > 32 -- currently broken: it increases power usage under light load */ -# define SCHED_LOAD_RESOLUTION 10 -# define scale_load(w) ((w) << SCHED_LOAD_RESOLUTION) -# define scale_load_down(w) ((w) >> SCHED_LOAD_RESOLUTION) -#else -# define SCHED_LOAD_RESOLUTION 0 -# define scale_load(w) (w) -# define scale_load_down(w) (w) -#endif - -#define SCHED_LOAD_SHIFT (10 + SCHED_LOAD_RESOLUTION) -#define SCHED_LOAD_SCALE (1L << SCHED_LOAD_SHIFT) - /* * Increase resolution of cpu_power calculations */ diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index cc03cfdf469..709a30cdfd8 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -33,6 +33,31 @@ extern __read_mostly int scheduler_running; */ #define NS_TO_JIFFIES(TIME) ((unsigned long)(TIME) / (NSEC_PER_SEC / HZ)) +/* + * Increase resolution of nice-level calculations for 64-bit architectures. + * The extra resolution improves shares distribution and load balancing of + * low-weight task groups (eg. nice +19 on an autogroup), deeper taskgroup + * hierarchies, especially on larger systems. This is not a user-visible change + * and does not change the user-interface for setting shares/weights. + * + * We increase resolution only if we have enough bits to allow this increased + * resolution (i.e. BITS_PER_LONG > 32). The costs for increasing resolution + * when BITS_PER_LONG <= 32 are pretty high and the returns do not justify the + * increased costs. + */ +#if 0 /* BITS_PER_LONG > 32 -- currently broken: it increases power usage under light load */ +# define SCHED_LOAD_RESOLUTION 10 +# define scale_load(w) ((w) << SCHED_LOAD_RESOLUTION) +# define scale_load_down(w) ((w) >> SCHED_LOAD_RESOLUTION) +#else +# define SCHED_LOAD_RESOLUTION 0 +# define scale_load(w) (w) +# define scale_load_down(w) (w) +#endif + +#define SCHED_LOAD_SHIFT (10 + SCHED_LOAD_RESOLUTION) +#define SCHED_LOAD_SCALE (1L << SCHED_LOAD_SHIFT) + #define NICE_0_LOAD SCHED_LOAD_SCALE #define NICE_0_SHIFT SCHED_LOAD_SHIFT @@ -784,7 +809,6 @@ static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev) } #endif /* __ARCH_WANT_UNLOCKED_CTXSW */ - static inline void update_load_add(struct load_weight *lw, unsigned long inc) { lw->weight += inc; -- cgit v1.2.3-70-g09d2 From 5e6521eaa1ee581a13b904f35b80c5efeb2baccb Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 16:06:23 +0800 Subject: sched: Move struct sched_group to kernel/sched/sched.h Move struct sched_group_power and sched_group and related inline functions to kernel/sched/sched.h, as they are used internally only. Signed-off-by: Li Zefan Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5135A77F.2010705@huawei.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 58 ++------------------------------------------------- kernel/sched/sched.h | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index f8826d04fb1..0d641304c0f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -780,62 +780,6 @@ enum cpu_idle_type { extern int __weak arch_sd_sibiling_asym_packing(void); -struct sched_group_power { - atomic_t ref; - /* - * CPU power of this group, SCHED_LOAD_SCALE being max power for a - * single CPU. - */ - unsigned int power, power_orig; - unsigned long next_update; - /* - * Number of busy cpus in this group. - */ - atomic_t nr_busy_cpus; - - unsigned long cpumask[0]; /* iteration mask */ -}; - -struct sched_group { - struct sched_group *next; /* Must be a circular list */ - atomic_t ref; - - unsigned int group_weight; - struct sched_group_power *sgp; - - /* - * The CPUs this group covers. - * - * NOTE: this field is variable length. (Allocated dynamically - * by attaching extra space to the end of the structure, - * depending on how many CPUs the kernel has booted up with) - */ - unsigned long cpumask[0]; -}; - -static inline struct cpumask *sched_group_cpus(struct sched_group *sg) -{ - return to_cpumask(sg->cpumask); -} - -/* - * cpumask masking which cpus in the group are allowed to iterate up the domain - * tree. - */ -static inline struct cpumask *sched_group_mask(struct sched_group *sg) -{ - return to_cpumask(sg->sgp->cpumask); -} - -/** - * group_first_cpu - Returns the first cpu in the cpumask of a sched_group. - * @group: The group whose first cpu is to be returned. - */ -static inline unsigned int group_first_cpu(struct sched_group *group) -{ - return cpumask_first(sched_group_cpus(group)); -} - struct sched_domain_attr { int relax_domain_level; }; @@ -846,6 +790,8 @@ struct sched_domain_attr { extern int sched_domain_level_max; +struct sched_group; + struct sched_domain { /* These fields must be setup */ struct sched_domain *parent; /* top domain must be null terminated */ diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 709a30cdfd8..1a4a2b19c2f 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -572,6 +572,62 @@ static inline struct sched_domain *highest_flag_domain(int cpu, int flag) DECLARE_PER_CPU(struct sched_domain *, sd_llc); DECLARE_PER_CPU(int, sd_llc_id); +struct sched_group_power { + atomic_t ref; + /* + * CPU power of this group, SCHED_LOAD_SCALE being max power for a + * single CPU. + */ + unsigned int power, power_orig; + unsigned long next_update; + /* + * Number of busy cpus in this group. + */ + atomic_t nr_busy_cpus; + + unsigned long cpumask[0]; /* iteration mask */ +}; + +struct sched_group { + struct sched_group *next; /* Must be a circular list */ + atomic_t ref; + + unsigned int group_weight; + struct sched_group_power *sgp; + + /* + * The CPUs this group covers. + * + * NOTE: this field is variable length. (Allocated dynamically + * by attaching extra space to the end of the structure, + * depending on how many CPUs the kernel has booted up with) + */ + unsigned long cpumask[0]; +}; + +static inline struct cpumask *sched_group_cpus(struct sched_group *sg) +{ + return to_cpumask(sg->cpumask); +} + +/* + * cpumask masking which cpus in the group are allowed to iterate up the domain + * tree. + */ +static inline struct cpumask *sched_group_mask(struct sched_group *sg) +{ + return to_cpumask(sg->sgp->cpumask); +} + +/** + * group_first_cpu - Returns the first cpu in the cpumask of a sched_group. + * @group: The group whose first cpu is to be returned. + */ +static inline unsigned int group_first_cpu(struct sched_group *group) +{ + return cpumask_first(sched_group_cpus(group)); +} + extern int group_balance_cpu(struct sched_group *sg); #endif /* CONFIG_SMP */ -- cgit v1.2.3-70-g09d2 From b13095f07f25464de65f5ce5ea94e16813d67488 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 16:06:38 +0800 Subject: sched: Move wake flags to kernel/sched/sched.h They are used internally only. Signed-off-by: Li Zefan Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5135A78E.7040609@huawei.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 7 ------- kernel/sched/sched.h | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 0d641304c0f..863b505ac48 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -920,13 +920,6 @@ struct uts_namespace; struct rq; struct sched_domain; -/* - * wake flags - */ -#define WF_SYNC 0x01 /* waker goes to sleep after wakup */ -#define WF_FORK 0x02 /* child wakeup after fork */ -#define WF_MIGRATED 0x04 /* internal use, task got migrated */ - #define ENQUEUE_WAKEUP 1 #define ENQUEUE_HEAD 2 #ifdef CONFIG_SMP diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 1a4a2b19c2f..4e5c2afdac9 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -865,6 +865,13 @@ static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev) } #endif /* __ARCH_WANT_UNLOCKED_CTXSW */ +/* + * wake flags + */ +#define WF_SYNC 0x01 /* waker goes to sleep after wakeup */ +#define WF_FORK 0x02 /* child wakeup after fork */ +#define WF_MIGRATED 0x4 /* internal use, task got migrated */ + static inline void update_load_add(struct load_weight *lw, unsigned long inc) { lw->weight += inc; -- cgit v1.2.3-70-g09d2 From c82ba9fa7588dfd02d4dc99ad1af486304bc424c Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 16:06:55 +0800 Subject: sched: Move struct sched_class to kernel/sched/sched.h It's used internally only. Signed-off-by: Li Zefan Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5135A79F.8090502@huawei.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 59 --------------------------------------------------- kernel/sched/sched.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 863b505ac48..04b834fa14b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -917,65 +917,6 @@ struct mempolicy; struct pipe_inode_info; struct uts_namespace; -struct rq; -struct sched_domain; - -#define ENQUEUE_WAKEUP 1 -#define ENQUEUE_HEAD 2 -#ifdef CONFIG_SMP -#define ENQUEUE_WAKING 4 /* sched_class::task_waking was called */ -#else -#define ENQUEUE_WAKING 0 -#endif - -#define DEQUEUE_SLEEP 1 - -struct sched_class { - const struct sched_class *next; - - void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags); - void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags); - void (*yield_task) (struct rq *rq); - bool (*yield_to_task) (struct rq *rq, struct task_struct *p, bool preempt); - - void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int flags); - - struct task_struct * (*pick_next_task) (struct rq *rq); - void (*put_prev_task) (struct rq *rq, struct task_struct *p); - -#ifdef CONFIG_SMP - int (*select_task_rq)(struct task_struct *p, int sd_flag, int flags); - void (*migrate_task_rq)(struct task_struct *p, int next_cpu); - - void (*pre_schedule) (struct rq *this_rq, struct task_struct *task); - void (*post_schedule) (struct rq *this_rq); - void (*task_waking) (struct task_struct *task); - void (*task_woken) (struct rq *this_rq, struct task_struct *task); - - void (*set_cpus_allowed)(struct task_struct *p, - const struct cpumask *newmask); - - void (*rq_online)(struct rq *rq); - void (*rq_offline)(struct rq *rq); -#endif - - void (*set_curr_task) (struct rq *rq); - void (*task_tick) (struct rq *rq, struct task_struct *p, int queued); - void (*task_fork) (struct task_struct *p); - - void (*switched_from) (struct rq *this_rq, struct task_struct *task); - void (*switched_to) (struct rq *this_rq, struct task_struct *task); - void (*prio_changed) (struct rq *this_rq, struct task_struct *task, - int oldprio); - - unsigned int (*get_rr_interval) (struct rq *rq, - struct task_struct *task); - -#ifdef CONFIG_FAIR_GROUP_SCHED - void (*task_move_group) (struct task_struct *p, int on_rq); -#endif -}; - struct load_weight { unsigned long weight, inv_weight; }; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 4e5c2afdac9..eca526d7afb 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -951,6 +951,61 @@ enum cpuacct_stat_index { CPUACCT_STAT_NSTATS, }; +#define ENQUEUE_WAKEUP 1 +#define ENQUEUE_HEAD 2 +#ifdef CONFIG_SMP +#define ENQUEUE_WAKING 4 /* sched_class::task_waking was called */ +#else +#define ENQUEUE_WAKING 0 +#endif + +#define DEQUEUE_SLEEP 1 + +struct sched_class { + const struct sched_class *next; + + void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags); + void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags); + void (*yield_task) (struct rq *rq); + bool (*yield_to_task) (struct rq *rq, struct task_struct *p, bool preempt); + + void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int flags); + + struct task_struct * (*pick_next_task) (struct rq *rq); + void (*put_prev_task) (struct rq *rq, struct task_struct *p); + +#ifdef CONFIG_SMP + int (*select_task_rq)(struct task_struct *p, int sd_flag, int flags); + void (*migrate_task_rq)(struct task_struct *p, int next_cpu); + + void (*pre_schedule) (struct rq *this_rq, struct task_struct *task); + void (*post_schedule) (struct rq *this_rq); + void (*task_waking) (struct task_struct *task); + void (*task_woken) (struct rq *this_rq, struct task_struct *task); + + void (*set_cpus_allowed)(struct task_struct *p, + const struct cpumask *newmask); + + void (*rq_online)(struct rq *rq); + void (*rq_offline)(struct rq *rq); +#endif + + void (*set_curr_task) (struct rq *rq); + void (*task_tick) (struct rq *rq, struct task_struct *p, int queued); + void (*task_fork) (struct task_struct *p); + + void (*switched_from) (struct rq *this_rq, struct task_struct *task); + void (*switched_to) (struct rq *this_rq, struct task_struct *task); + void (*prio_changed) (struct rq *this_rq, struct task_struct *task, + int oldprio); + + unsigned int (*get_rr_interval) (struct rq *rq, + struct task_struct *task); + +#ifdef CONFIG_FAIR_GROUP_SCHED + void (*task_move_group) (struct task_struct *p, int on_rq); +#endif +}; #define sched_class_highest (&stop_sched_class) #define for_each_class(class) \ -- cgit v1.2.3-70-g09d2 From 15f803c94bd92b17708aad9e74226fd0b2c9130c Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 16:07:11 +0800 Subject: sched: Make default_scale_freq_power() static As default_scale_{freq,smt}_power() and update_rt_power() are used in kernel/sched/fair.c only, annotate them as static functions. Signed-off-by: Li Zefan Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5135A7AF.8010900@huawei.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 3 --- kernel/sched/fair.c | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 04b834fa14b..eadd113e1eb 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -880,9 +880,6 @@ extern void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], cpumask_var_t *alloc_sched_domains(unsigned int ndoms); void free_sched_domains(cpumask_var_t doms[], unsigned int ndoms); -unsigned long default_scale_freq_power(struct sched_domain *sd, int cpu); -unsigned long default_scale_smt_power(struct sched_domain *sd, int cpu); - bool cpus_share_cache(int this_cpu, int that_cpu); #else /* CONFIG_SMP */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7a33e5986fc..9f2311256ae 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -4245,7 +4245,7 @@ static inline int get_sd_load_idx(struct sched_domain *sd, return load_idx; } -unsigned long default_scale_freq_power(struct sched_domain *sd, int cpu) +static unsigned long default_scale_freq_power(struct sched_domain *sd, int cpu) { return SCHED_POWER_SCALE; } @@ -4255,7 +4255,7 @@ unsigned long __weak arch_scale_freq_power(struct sched_domain *sd, int cpu) return default_scale_freq_power(sd, cpu); } -unsigned long default_scale_smt_power(struct sched_domain *sd, int cpu) +static unsigned long default_scale_smt_power(struct sched_domain *sd, int cpu) { unsigned long weight = sd->span_weight; unsigned long smt_gain = sd->smt_gain; @@ -4270,7 +4270,7 @@ unsigned long __weak arch_scale_smt_power(struct sched_domain *sd, int cpu) return default_scale_smt_power(sd, cpu); } -unsigned long scale_rt_power(int cpu) +static unsigned long scale_rt_power(int cpu) { struct rq *rq = cpu_rq(cpu); u64 total, available, age_stamp, avg; -- cgit v1.2.3-70-g09d2 From 25cc7da7e6336d3bb6a5bad3d3fa96fce9a81d5b Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 16:07:33 +0800 Subject: sched: Move group scheduling functions out of include/linux/sched.h - Make sched_group_{set_,}runtime(), sched_group_{set_,}period() and sched_rt_can_attach() static. - Move sched_{create,destroy,online,offline}_group() to kernel/sched/sched.h. - Remove declaration of sched_group_shares(). Signed-off-by: Li Zefan Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5135A7C5.3000708@huawei.com Signed-off-by: Ingo Molnar --- include/linux/sched.h | 21 --------------------- kernel/sched/core.c | 10 +++++----- kernel/sched/sched.h | 12 ++++++++++++ 3 files changed, 17 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index eadd113e1eb..fc039ceccbe 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2512,28 +2512,7 @@ extern long sched_setaffinity(pid_t pid, const struct cpumask *new_mask); extern long sched_getaffinity(pid_t pid, struct cpumask *mask); #ifdef CONFIG_CGROUP_SCHED - extern struct task_group root_task_group; - -extern struct task_group *sched_create_group(struct task_group *parent); -extern void sched_online_group(struct task_group *tg, - struct task_group *parent); -extern void sched_destroy_group(struct task_group *tg); -extern void sched_offline_group(struct task_group *tg); -extern void sched_move_task(struct task_struct *tsk); -#ifdef CONFIG_FAIR_GROUP_SCHED -extern int sched_group_set_shares(struct task_group *tg, unsigned long shares); -extern unsigned long sched_group_shares(struct task_group *tg); -#endif -#ifdef CONFIG_RT_GROUP_SCHED -extern int sched_group_set_rt_runtime(struct task_group *tg, - long rt_runtime_us); -extern long sched_group_rt_runtime(struct task_group *tg); -extern int sched_group_set_rt_period(struct task_group *tg, - long rt_period_us); -extern long sched_group_rt_period(struct task_group *tg); -extern int sched_rt_can_attach(struct task_group *tg, struct task_struct *tsk); -#endif #endif /* CONFIG_CGROUP_SCHED */ extern int task_can_switch_user(struct user_struct *up, diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 7f12624a393..9ad26c98644 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7455,7 +7455,7 @@ unlock: return err; } -int sched_group_set_rt_runtime(struct task_group *tg, long rt_runtime_us) +static int sched_group_set_rt_runtime(struct task_group *tg, long rt_runtime_us) { u64 rt_runtime, rt_period; @@ -7467,7 +7467,7 @@ int sched_group_set_rt_runtime(struct task_group *tg, long rt_runtime_us) return tg_set_rt_bandwidth(tg, rt_period, rt_runtime); } -long sched_group_rt_runtime(struct task_group *tg) +static long sched_group_rt_runtime(struct task_group *tg) { u64 rt_runtime_us; @@ -7479,7 +7479,7 @@ long sched_group_rt_runtime(struct task_group *tg) return rt_runtime_us; } -int sched_group_set_rt_period(struct task_group *tg, long rt_period_us) +static int sched_group_set_rt_period(struct task_group *tg, long rt_period_us) { u64 rt_runtime, rt_period; @@ -7492,7 +7492,7 @@ int sched_group_set_rt_period(struct task_group *tg, long rt_period_us) return tg_set_rt_bandwidth(tg, rt_period, rt_runtime); } -long sched_group_rt_period(struct task_group *tg) +static long sched_group_rt_period(struct task_group *tg) { u64 rt_period_us; @@ -7527,7 +7527,7 @@ static int sched_rt_global_constraints(void) return ret; } -int sched_rt_can_attach(struct task_group *tg, struct task_struct *tsk) +static int sched_rt_can_attach(struct task_group *tg, struct task_struct *tsk) { /* Don't accept realtime tasks when there is no way for them to run */ if (rt_task(tsk) && tg->rt_bandwidth.rt_runtime == 0) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index eca526d7afb..304fc1c7714 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -221,6 +221,18 @@ extern void init_tg_rt_entry(struct task_group *tg, struct rt_rq *rt_rq, struct sched_rt_entity *rt_se, int cpu, struct sched_rt_entity *parent); +extern struct task_group *sched_create_group(struct task_group *parent); +extern void sched_online_group(struct task_group *tg, + struct task_group *parent); +extern void sched_destroy_group(struct task_group *tg); +extern void sched_offline_group(struct task_group *tg); + +extern void sched_move_task(struct task_struct *tsk); + +#ifdef CONFIG_FAIR_GROUP_SCHED +extern int sched_group_set_shares(struct task_group *tg, unsigned long shares); +#endif + #else /* CONFIG_CGROUP_SCHED */ struct cfs_bandwidth { }; -- cgit v1.2.3-70-g09d2 From 877c685607925238e302cd3aa38788dca6c1b226 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 5 Mar 2013 11:38:08 +0800 Subject: perf: Remove include of cgroup.h from perf_event.h Move struct perf_cgroup_info and perf_cgroup to kernel/perf/core.c, and then we can remove include of cgroup.h. Signed-off-by: Li Zefan Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Tejun Heo Link: http://lkml.kernel.org/r/513568A0.6020804@huawei.com Signed-off-by: Ingo Molnar --- include/linux/perf_event.h | 18 +----------------- kernel/events/core.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index e47ee462c2f..8737e1cee8b 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -21,7 +21,6 @@ */ #ifdef CONFIG_PERF_EVENTS -# include # include # include #endif @@ -299,22 +298,7 @@ struct swevent_hlist { #define PERF_ATTACH_GROUP 0x02 #define PERF_ATTACH_TASK 0x04 -#ifdef CONFIG_CGROUP_PERF -/* - * perf_cgroup_info keeps track of time_enabled for a cgroup. - * This is a per-cpu dynamically allocated data structure. - */ -struct perf_cgroup_info { - u64 time; - u64 timestamp; -}; - -struct perf_cgroup { - struct cgroup_subsys_state css; - struct perf_cgroup_info *info; /* timing info, one per cpu */ -}; -#endif - +struct perf_cgroup; struct ring_buffer; /** diff --git a/kernel/events/core.c b/kernel/events/core.c index b0cd86501c3..5976a2a6b4c 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "internal.h" @@ -233,6 +234,20 @@ static void perf_ctx_unlock(struct perf_cpu_context *cpuctx, #ifdef CONFIG_CGROUP_PERF +/* + * perf_cgroup_info keeps track of time_enabled for a cgroup. + * This is a per-cpu dynamically allocated data structure. + */ +struct perf_cgroup_info { + u64 time; + u64 timestamp; +}; + +struct perf_cgroup { + struct cgroup_subsys_state css; + struct perf_cgroup_info *info; +}; + /* * Must ensure cgroup is pinned (css_get) before calling * this function. In other words, we cannot call this function -- cgit v1.2.3-70-g09d2 From f8bacc210408f7a2a182f184a9fa1475b8a67440 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 14 Feb 2013 23:27:01 +0100 Subject: cfg80211: clean up mesh plink station change API Make the ability to leave the plink_state unchanged not use a magic -1 variable that isn't in the enum, but an explicit change flag; reject invalid plink states or actions and move the needed constants for plink actions to the right header file. Also reject plink_state changes for non-mesh interfaces. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 15 ++------------- include/uapi/linux/nl80211.h | 20 +++++++++++++++++++- net/mac80211/cfg.c | 14 +++++++++++--- net/wireless/nl80211.c | 29 ++++++++++++++++++++++------- 4 files changed, 54 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d581c6de5d6..9b54574c3df 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -610,23 +610,11 @@ struct cfg80211_ap_settings { bool radar_required; }; -/** - * enum plink_action - actions to perform in mesh peers - * - * @PLINK_ACTION_INVALID: action 0 is reserved - * @PLINK_ACTION_OPEN: start mesh peer link establishment - * @PLINK_ACTION_BLOCK: block traffic from this mesh peer - */ -enum plink_actions { - PLINK_ACTION_INVALID, - PLINK_ACTION_OPEN, - PLINK_ACTION_BLOCK, -}; - /** * enum station_parameters_apply_mask - station parameter values to apply * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp) * @STATION_PARAM_APPLY_CAPABILITY: apply new capability + * @STATION_PARAM_APPLY_PLINK_STATE: apply new plink state * * Not all station parameters have in-band "no change" signalling, * for those that don't these flags will are used. @@ -634,6 +622,7 @@ enum plink_actions { enum station_parameters_apply_mask { STATION_PARAM_APPLY_UAPSD = BIT(0), STATION_PARAM_APPLY_CAPABILITY = BIT(1), + STATION_PARAM_APPLY_PLINK_STATE = BIT(2), }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c46bb016f4e..7dcc69f73d2 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -884,7 +884,8 @@ enum nl80211_commands { * consisting of a nested array. * * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). - * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link + * (see &enum nl80211_plink_action). * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path * info given for %NL80211_CMD_GET_MPATH, nested attribute described at @@ -3307,6 +3308,23 @@ enum nl80211_plink_state { MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 }; +/** + * enum nl80211_plink_action - actions to perform in mesh peers + * + * @NL80211_PLINK_ACTION_NO_ACTION: perform no action + * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment + * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer + * @NUM_NL80211_PLINK_ACTIONS: number of possible actions + */ +enum plink_actions { + NL80211_PLINK_ACTION_NO_ACTION, + NL80211_PLINK_ACTION_OPEN, + NL80211_PLINK_ACTION_BLOCK, + + NUM_NL80211_PLINK_ACTIONS, +}; + + #define NL80211_KCK_LEN 16 #define NL80211_KEK_LEN 16 #define NL80211_REPLAY_CTR_LEN 8 diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index fb306814576..ca28405d5f6 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1261,7 +1261,9 @@ static int sta_apply_parameters(struct ieee80211_local *local, if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH u32 changed = 0; - if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) { + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED && + (params->sta_modify_mask & + STATION_PARAM_APPLY_PLINK_STATE)) { switch (params->plink_state) { case NL80211_PLINK_ESTAB: if (sta->plink_state != NL80211_PLINK_ESTAB) @@ -1292,12 +1294,18 @@ static int sta_apply_parameters(struct ieee80211_local *local, /* nothing */ break; } + } else if (params->sta_modify_mask & + STATION_PARAM_APPLY_PLINK_STATE) { + return -EINVAL; } else { switch (params->plink_action) { - case PLINK_ACTION_OPEN: + case NL80211_PLINK_ACTION_NO_ACTION: + /* nothing */ + break; + case NL80211_PLINK_ACTION_OPEN: changed |= mesh_plink_open(sta); break; - case PLINK_ACTION_BLOCK: + case NL80211_PLINK_ACTION_BLOCK: changed |= mesh_plink_block(sta); break; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d44ab216c0e..9e7ece0e5e5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3412,7 +3412,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) memset(¶ms, 0, sizeof(params)); params.listen_interval = -1; - params.plink_state = -1; if (info->attrs[NL80211_ATTR_STA_AID]) return -EINVAL; @@ -3451,13 +3450,20 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms)) return -EINVAL; - if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) + if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) { params.plink_action = - nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS) + return -EINVAL; + } - if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) + if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) { params.plink_state = - nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); + nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); + if (params.plink_state >= NUM_NL80211_PLINK_STATES) + return -EINVAL; + params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE; + } if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) { enum nl80211_mesh_power_mode pm = nla_get_u32( @@ -3479,6 +3485,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; if (params.local_pm) return -EINVAL; + if (params.sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) + return -EINVAL; /* TDLS can't be set, ... */ if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) @@ -3542,6 +3550,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; if (params.local_pm) return -EINVAL; + if (params.sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) + return -EINVAL; /* reject any changes other than AUTHORIZED or WME (for TDLS) */ if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | BIT(NL80211_STA_FLAG_WME))) @@ -3553,6 +3563,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; if (params.local_pm) return -EINVAL; + if (params.sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) + return -EINVAL; if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || info->attrs[NL80211_ATTR_VHT_CAPABILITY]) return -EINVAL; @@ -3652,9 +3664,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.vht_capa = nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); - if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) + if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) { params.plink_action = - nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); + if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS) + return -EINVAL; + } if (!rdev->ops->add_station) return -EOPNOTSUPP; -- cgit v1.2.3-70-g09d2 From 2c1aabf33d1832befc5291a14c870cd09dc2182d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 14 Feb 2013 23:33:40 +0100 Subject: cfg80211: constify station parameter pointers All the pointers point right into the skb data and not to anything that would be useful to change, so make them const. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9b54574c3df..7ca321d2b59 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -658,7 +658,7 @@ enum station_parameters_apply_mask { * @ext_capab_len: number of extended capabilities */ struct station_parameters { - u8 *supported_rates; + const u8 *supported_rates; struct net_device *vlan; u32 sta_flags_mask, sta_flags_set; u32 sta_modify_mask; @@ -667,13 +667,13 @@ struct station_parameters { u8 supported_rates_len; u8 plink_action; u8 plink_state; - struct ieee80211_ht_cap *ht_capa; - struct ieee80211_vht_cap *vht_capa; + const struct ieee80211_ht_cap *ht_capa; + const struct ieee80211_vht_cap *vht_capa; u8 uapsd_queues; u8 max_sp; enum nl80211_mesh_power_mode local_pm; u16 capability; - u8 *ext_capab; + const u8 *ext_capab; u8 ext_capab_len; }; -- cgit v1.2.3-70-g09d2 From 77ee7c891a04c3d254711ddf1bde5d7381339fb3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Feb 2013 00:48:33 +0100 Subject: cfg80211: comprehensively check station changes The station change API isn't being checked properly before drivers are called, and as a result it is difficult to see what should be allowed and what not. In order to comprehensively check the API parameters parse everything first, and then have the driver call a function (cfg80211_check_station_change()) with the additionally information about the kind of station that is being changed; this allows the function to make better decisions than the old code could. While at it, also add a few checks, particularly in mesh and clarify the TDLS station lifetime in documentation. To be able to reduce a few checks, ignore any flag set bits when the mask isn't set, they shouldn't be applied then. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 8 +- include/net/cfg80211.h | 48 ++++- include/uapi/linux/nl80211.h | 16 +- net/mac80211/cfg.c | 125 ++++++++----- net/wireless/nl80211.c | 288 +++++++++++++++++------------ 5 files changed, 311 insertions(+), 174 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 752ffc4f416..28c413f861a 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2990,13 +2990,15 @@ static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); + int err; if (vif->nw_type != AP_NETWORK) return -EOPNOTSUPP; - /* Use this only for authorizing/unauthorizing a station */ - if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) - return -EOPNOTSUPP; + err = cfg80211_check_station_change(wiphy, params, + CFG80211_STA_AP_MLME_CLIENT); + if (err) + return err; if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7ca321d2b59..ed2b08da3b9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -677,6 +677,49 @@ struct station_parameters { u8 ext_capab_len; }; +/** + * enum cfg80211_station_type - the type of station being modified + * @CFG80211_STA_AP_CLIENT: client of an AP interface + * @CFG80211_STA_AP_MLME_CLIENT: client of an AP interface that has + * the AP MLME in the device + * @CFG80211_STA_AP_STA: AP station on managed interface + * @CFG80211_STA_IBSS: IBSS station + * @CFG80211_STA_TDLS_PEER_SETUP: TDLS peer on managed interface (dummy entry + * while TDLS setup is in progress, it moves out of this state when + * being marked authorized; use this only if TDLS with external setup is + * supported/used) + * @CFG80211_STA_TDLS_PEER_ACTIVE: TDLS peer on managed interface (active + * entry that is operating, has been marked authorized by userspace) + * @CFG80211_STA_MESH_PEER_NONSEC: peer on mesh interface (non-secured) + * @CFG80211_STA_MESH_PEER_SECURE: peer on mesh interface (secured) + */ +enum cfg80211_station_type { + CFG80211_STA_AP_CLIENT, + CFG80211_STA_AP_MLME_CLIENT, + CFG80211_STA_AP_STA, + CFG80211_STA_IBSS, + CFG80211_STA_TDLS_PEER_SETUP, + CFG80211_STA_TDLS_PEER_ACTIVE, + CFG80211_STA_MESH_PEER_NONSEC, + CFG80211_STA_MESH_PEER_SECURE, +}; + +/** + * cfg80211_check_station_change - validate parameter changes + * @wiphy: the wiphy this operates on + * @params: the new parameters for a station + * @statype: the type of station being modified + * + * Utility function for the @change_station driver method. Call this function + * with the appropriate station type looking up the station (and checking that + * it exists). It will verify whether the station change is acceptable, and if + * not will return an error code. Note that it may modify the parameters for + * backward compatibility reasons, so don't use them before calling this. + */ +int cfg80211_check_station_change(struct wiphy *wiphy, + struct station_parameters *params, + enum cfg80211_station_type statype); + /** * enum station_info_flags - station information flags * @@ -1770,9 +1813,8 @@ struct cfg80211_gtk_rekey_data { * @change_station: Modify a given station. Note that flags changes are not much * validated in cfg80211, in particular the auth/assoc/authorized flags * might come to the driver in invalid combinations -- make sure to check - * them, also against the existing state! Also, supported_rates changes are - * not checked in station mode -- drivers need to reject (or ignore) them - * for anything but TDLS peers. + * them, also against the existing state! Drivers must call + * cfg80211_check_station_change() to validate the information. * @get_station: get station information for the station identified by @mac * @dump_station: dump station callback -- resume dump at index @idx * diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 7dcc69f73d2..523ed3d65b4 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -36,7 +36,21 @@ * The station is still assumed to belong to the AP interface it was added * to. * - * TODO: need more info? + * Station handling varies per interface type and depending on the driver's + * capabilities. + * + * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS + * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows: + * - a setup station entry is added, not yet authorized, without any rate + * or capability information, this just exists to avoid race conditions + * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid + * to add rate and capability information to the station and at the same + * time mark it authorized. + * - %NL80211_TDLS_ENABLE_LINK is then used + * - after this, the only valid operation is to remove it by tearing down + * the TDLS link (%NL80211_TDLS_DISABLE_LINK) + * + * TODO: need more info for other interface types */ /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ca28405d5f6..c115f82c037 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1177,6 +1177,18 @@ static int sta_apply_parameters(struct ieee80211_local *local, mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) set |= BIT(NL80211_STA_FLAG_ASSOCIATED); + } else if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { + /* + * TDLS -- everything follows authorized, but + * only becoming authorized is possible, not + * going back + */ + if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) { + set |= BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED); + mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED); + } } ret = sta_apply_auth_flags(local, sta, mask, set); @@ -1261,9 +1273,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH u32 changed = 0; - if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED && - (params->sta_modify_mask & - STATION_PARAM_APPLY_PLINK_STATE)) { + + if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) { switch (params->plink_state) { case NL80211_PLINK_ESTAB: if (sta->plink_state != NL80211_PLINK_ESTAB) @@ -1294,21 +1305,18 @@ static int sta_apply_parameters(struct ieee80211_local *local, /* nothing */ break; } - } else if (params->sta_modify_mask & - STATION_PARAM_APPLY_PLINK_STATE) { - return -EINVAL; - } else { - switch (params->plink_action) { - case NL80211_PLINK_ACTION_NO_ACTION: - /* nothing */ - break; - case NL80211_PLINK_ACTION_OPEN: - changed |= mesh_plink_open(sta); - break; - case NL80211_PLINK_ACTION_BLOCK: - changed |= mesh_plink_block(sta); - break; - } + } + + switch (params->plink_action) { + case NL80211_PLINK_ACTION_NO_ACTION: + /* nothing */ + break; + case NL80211_PLINK_ACTION_OPEN: + changed |= mesh_plink_open(sta); + break; + case NL80211_PLINK_ACTION_BLOCK: + changed |= mesh_plink_block(sta); + break; } if (params->local_pm) @@ -1354,8 +1362,10 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, * defaults -- if userspace wants something else we'll * change it accordingly in sta_apply_parameters() */ - sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); - sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) { + sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); + sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); + } err = sta_apply_parameters(local, sta, params); if (err) { @@ -1364,8 +1374,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, } /* - * for TDLS, rate control should be initialized only when supported - * rates are known. + * for TDLS, rate control should be initialized only when + * rates are known and station is marked authorized */ if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) rate_control_rate_init(sta); @@ -1402,50 +1412,67 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_change_station(struct wiphy *wiphy, - struct net_device *dev, - u8 *mac, + struct net_device *dev, u8 *mac, struct station_parameters *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wiphy_priv(wiphy); struct sta_info *sta; struct ieee80211_sub_if_data *vlansdata; + enum cfg80211_station_type statype; int err; mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mac); if (!sta) { - mutex_unlock(&local->sta_mtx); - return -ENOENT; + err = -ENOENT; + goto out_err; } - /* in station mode, some updates are only valid with TDLS */ - if (sdata->vif.type == NL80211_IFTYPE_STATION && - (params->supported_rates || params->ht_capa || params->vht_capa || - params->sta_modify_mask || - (params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) && - !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { - mutex_unlock(&local->sta_mtx); - return -EINVAL; + switch (sdata->vif.type) { + case NL80211_IFTYPE_MESH_POINT: + if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) + statype = CFG80211_STA_MESH_PEER_SECURE; + else + statype = CFG80211_STA_MESH_PEER_NONSEC; + break; + case NL80211_IFTYPE_ADHOC: + statype = CFG80211_STA_IBSS; + break; + case NL80211_IFTYPE_STATION: + if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { + statype = CFG80211_STA_AP_STA; + break; + } + if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + statype = CFG80211_STA_TDLS_PEER_ACTIVE; + else + statype = CFG80211_STA_TDLS_PEER_SETUP; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + statype = CFG80211_STA_AP_CLIENT; + break; + default: + err = -EOPNOTSUPP; + goto out_err; } + err = cfg80211_check_station_change(wiphy, params, statype); + if (err) + goto out_err; + if (params->vlan && params->vlan != sta->sdata->dev) { bool prev_4addr = false; bool new_4addr = false; vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); - if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN && - vlansdata->vif.type != NL80211_IFTYPE_AP) { - mutex_unlock(&local->sta_mtx); - return -EINVAL; - } - if (params->vlan->ieee80211_ptr->use_4addr) { if (vlansdata->u.vlan.sta) { - mutex_unlock(&local->sta_mtx); - return -EBUSY; + err = -EBUSY; + goto out_err; } rcu_assign_pointer(vlansdata->u.vlan.sta, sta); @@ -1472,12 +1499,12 @@ static int ieee80211_change_station(struct wiphy *wiphy, } err = sta_apply_parameters(local, sta, params); - if (err) { - mutex_unlock(&local->sta_mtx); - return err; - } + if (err) + goto out_err; - if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates) + /* When peer becomes authorized, init rate control as well */ + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && + test_sta_flag(sta, WLAN_STA_AUTHORIZED)) rate_control_rate_init(sta); mutex_unlock(&local->sta_mtx); @@ -1487,7 +1514,11 @@ static int ieee80211_change_station(struct wiphy *wiphy, ieee80211_recalc_ps(local, -1); ieee80211_recalc_ps_vif(sdata); } + return 0; +out_err: + mutex_unlock(&local->sta_mtx); + return err; } #ifdef CONFIG_MAC80211_MESH diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9e7c10420da..83151a50e5a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2967,6 +2967,7 @@ static int parse_station_flags(struct genl_info *info, sta_flags = nla_data(nla); params->sta_flags_mask = sta_flags->mask; params->sta_flags_set = sta_flags->set; + params->sta_flags_set &= params->sta_flags_mask; if ((params->sta_flags_mask | params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) return -EINVAL; @@ -3320,6 +3321,136 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) return genlmsg_reply(msg, info); } +int cfg80211_check_station_change(struct wiphy *wiphy, + struct station_parameters *params, + enum cfg80211_station_type statype) +{ + if (params->listen_interval != -1) + return -EINVAL; + if (params->aid) + return -EINVAL; + + /* When you run into this, adjust the code below for the new flag */ + BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7); + + switch (statype) { + case CFG80211_STA_MESH_PEER_NONSEC: + case CFG80211_STA_MESH_PEER_SECURE: + /* + * No ignoring the TDLS flag here -- the userspace mesh + * code doesn't have the bug of including TDLS in the + * mask everywhere. + */ + if (params->sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_MFP) | + BIT(NL80211_STA_FLAG_AUTHORIZED))) + return -EINVAL; + break; + case CFG80211_STA_TDLS_PEER_SETUP: + case CFG80211_STA_TDLS_PEER_ACTIVE: + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -EINVAL; + /* ignore since it can't change */ + params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); + break; + default: + /* disallow mesh-specific things */ + if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION) + return -EINVAL; + if (params->local_pm) + return -EINVAL; + if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) + return -EINVAL; + } + + if (statype != CFG80211_STA_TDLS_PEER_SETUP && + statype != CFG80211_STA_TDLS_PEER_ACTIVE) { + /* TDLS can't be set, ... */ + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) + return -EINVAL; + /* + * ... but don't bother the driver with it. This works around + * a hostapd/wpa_supplicant issue -- it always includes the + * TLDS_PEER flag in the mask even for AP mode. + */ + params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); + } + + if (statype != CFG80211_STA_TDLS_PEER_SETUP) { + /* reject other things that can't change */ + if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) + return -EINVAL; + if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY) + return -EINVAL; + if (params->supported_rates) + return -EINVAL; + if (params->ext_capab || params->ht_capa || params->vht_capa) + return -EINVAL; + } + + if (statype != CFG80211_STA_AP_CLIENT) { + if (params->vlan) + return -EINVAL; + } + + switch (statype) { + case CFG80211_STA_AP_MLME_CLIENT: + /* Use this only for authorizing/unauthorizing a station */ + if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) + return -EOPNOTSUPP; + break; + case CFG80211_STA_AP_CLIENT: + /* accept only the listed bits */ + if (params->sta_flags_mask & + ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | + BIT(NL80211_STA_FLAG_WME) | + BIT(NL80211_STA_FLAG_MFP))) + return -EINVAL; + + /* but authenticated/associated only if driver handles it */ + if (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) && + params->sta_flags_mask & + (BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED))) + return -EINVAL; + break; + case CFG80211_STA_IBSS: + case CFG80211_STA_AP_STA: + /* reject any changes other than AUTHORIZED */ + if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) + return -EINVAL; + break; + case CFG80211_STA_TDLS_PEER_SETUP: + /* reject any changes other than AUTHORIZED or WME */ + if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | + BIT(NL80211_STA_FLAG_WME))) + return -EINVAL; + /* force (at least) rates when authorizing */ + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) && + !params->supported_rates) + return -EINVAL; + break; + case CFG80211_STA_TDLS_PEER_ACTIVE: + /* reject any changes */ + return -EINVAL; + case CFG80211_STA_MESH_PEER_NONSEC: + if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) + return -EINVAL; + break; + case CFG80211_STA_MESH_PEER_SECURE: + if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION) + return -EINVAL; + break; + } + + return 0; +} +EXPORT_SYMBOL(cfg80211_check_station_change); + /* * Get vlan interface making sure it is running and on the right wiphy. */ @@ -3342,6 +3473,13 @@ static struct net_device *get_vlan(struct genl_info *info, goto error; } + if (v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + v->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { + ret = -EINVAL; + goto error; + } + if (!netif_running(v)) { ret = -ENETDOWN; goto error; @@ -3410,15 +3548,18 @@ static int nl80211_set_station_tdls(struct genl_info *info, static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; struct net_device *dev = info->user_ptr[1]; struct station_parameters params; - u8 *mac_addr = NULL; + u8 *mac_addr; + int err; memset(¶ms, 0, sizeof(params)); params.listen_interval = -1; + if (!rdev->ops->change_station) + return -EOPNOTSUPP; + if (info->attrs[NL80211_ATTR_STA_AID]) return -EINVAL; @@ -3450,9 +3591,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) return -EINVAL; - if (!rdev->ops->change_station) - return -EOPNOTSUPP; - if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms)) return -EINVAL; @@ -3482,133 +3620,33 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) params.local_pm = pm; } + /* Include parameters for TDLS peer (will check later) */ + err = nl80211_set_station_tdls(info, ¶ms); + if (err) + return err; + + params.vlan = get_vlan(info, rdev); + if (IS_ERR(params.vlan)) + return PTR_ERR(params.vlan); + switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: - /* disallow mesh-specific things */ - if (params.plink_action) - return -EINVAL; - if (params.local_pm) - return -EINVAL; - if (params.sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) - return -EINVAL; - - /* TDLS can't be set, ... */ - if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) - return -EINVAL; - /* - * ... but don't bother the driver with it. This works around - * a hostapd/wpa_supplicant issue -- it always includes the - * TLDS_PEER flag in the mask even for AP mode. - */ - params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); - - /* accept only the listed bits */ - if (params.sta_flags_mask & - ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | - BIT(NL80211_STA_FLAG_AUTHENTICATED) | - BIT(NL80211_STA_FLAG_ASSOCIATED) | - BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | - BIT(NL80211_STA_FLAG_WME) | - BIT(NL80211_STA_FLAG_MFP))) - return -EINVAL; - - /* but authenticated/associated only if driver handles it */ - if (!(rdev->wiphy.features & - NL80211_FEATURE_FULL_AP_CLIENT_STATE) && - params.sta_flags_mask & - (BIT(NL80211_STA_FLAG_AUTHENTICATED) | - BIT(NL80211_STA_FLAG_ASSOCIATED))) - return -EINVAL; - - /* reject other things that can't change */ - if (params.supported_rates) - return -EINVAL; - if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) - return -EINVAL; - if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) - return -EINVAL; - if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || - info->attrs[NL80211_ATTR_VHT_CAPABILITY]) - return -EINVAL; - - /* must be last in here for error handling */ - params.vlan = get_vlan(info, rdev); - if (IS_ERR(params.vlan)) - return PTR_ERR(params.vlan); - break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: - /* - * Don't allow userspace to change the TDLS_PEER flag, - * but silently ignore attempts to change it since we - * don't have state here to verify that it doesn't try - * to change the flag. - */ - params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); - /* Include parameters for TDLS peer (driver will check) */ - err = nl80211_set_station_tdls(info, ¶ms); - if (err) - return err; - /* disallow things sta doesn't support */ - if (params.plink_action) - return -EINVAL; - if (params.local_pm) - return -EINVAL; - if (params.sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) - return -EINVAL; - /* reject any changes other than AUTHORIZED or WME (for TDLS) */ - if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | - BIT(NL80211_STA_FLAG_WME))) - return -EINVAL; - break; case NL80211_IFTYPE_ADHOC: - /* disallow things sta doesn't support */ - if (params.plink_action) - return -EINVAL; - if (params.local_pm) - return -EINVAL; - if (params.sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) - return -EINVAL; - if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || - info->attrs[NL80211_ATTR_VHT_CAPABILITY]) - return -EINVAL; - /* reject any changes other than AUTHORIZED */ - if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) - return -EINVAL; - break; case NL80211_IFTYPE_MESH_POINT: - /* disallow things mesh doesn't support */ - if (params.vlan) - return -EINVAL; - if (params.supported_rates) - return -EINVAL; - if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) - return -EINVAL; - if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) - return -EINVAL; - if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || - info->attrs[NL80211_ATTR_VHT_CAPABILITY]) - return -EINVAL; - /* - * No special handling for TDLS here -- the userspace - * mesh code doesn't have this bug. - */ - if (params.sta_flags_mask & - ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | - BIT(NL80211_STA_FLAG_MFP) | - BIT(NL80211_STA_FLAG_AUTHORIZED))) - return -EINVAL; break; default: - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + goto out_put_vlan; } - /* be aware of params.vlan when changing code here */ - + /* driver will call cfg80211_check_station_change() */ err = rdev_change_station(rdev, dev, mac_addr, ¶ms); + out_put_vlan: if (params.vlan) dev_put(params.vlan); @@ -3687,6 +3725,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms)) return -EINVAL; + /* When you run into this, adjust the code below for the new flag */ + BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7); + switch (dev->ieee80211_ptr->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: @@ -3730,8 +3771,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) /* ignore uAPSD data */ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; - /* associated is disallowed */ - if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) + /* these are disallowed */ + if (params.sta_flags_mask & + (BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_AUTHENTICATED))) return -EINVAL; /* Only TDLS peers can be added */ if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) @@ -3742,6 +3785,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) /* ... with external setup is supported */ if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) return -EOPNOTSUPP; + /* + * Older wpa_supplicant versions always mark the TDLS peer + * as authorized, but it shouldn't yet be. + */ + params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED); break; default: return -EOPNOTSUPP; -- cgit v1.2.3-70-g09d2 From 3713b4e364effef4b170c97d54528b1cdb16aa6b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 14 Feb 2013 16:19:38 +0100 Subject: nl80211: allow splitting wiphy information in dumps The per-wiphy information is getting large, to the point where with more than the typical number of channels it's too large and overflows, and userspace can't get any of the information at all. To address this (in a way that doesn't require making all messages bigger) allow userspace to specify that it can deal with wiphy information split across multiple parts of the dump, and if it can split up the data. This also splits up each channel separately so an arbitrary number of channels can be supported. Additionally, since GET_WIPHY has the same problem, add support for filtering the wiphy dump and get information for a single wiphy only, this allows userspace apps to use dump in this case to retrieve all data from a single device. As userspace needs to know if all this this is supported, add a global nl80211 feature set and include a bit for this behaviour in it. Cc: Dennis H Jensen Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 28 ++ net/wireless/nl80211.c | 933 ++++++++++++++++++++++++++----------------- 2 files changed, 599 insertions(+), 362 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 523ed3d65b4..9844c10a299 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -625,6 +625,10 @@ * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the * event. * + * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features, + * i.e. features for the nl80211 protocol rather than device features. + * Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -779,6 +783,8 @@ enum nl80211_commands { NL80211_CMD_RADAR_DETECT, + NL80211_CMD_GET_PROTOCOL_FEATURES, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1383,6 +1389,13 @@ enum nl80211_commands { * advertised to the driver, e.g., to enable TDLS off channel operations * and PU-APSD. * + * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see + * &enum nl80211_protocol_features, the attribute is a u32. + * + * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports + * receiving the data for a single wiphy split across multiple + * messages, given with wiphy dump message + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1669,6 +1682,9 @@ enum nl80211_attrs { NL80211_ATTR_STA_CAPABILITY, NL80211_ATTR_STA_EXT_CAPABILITY, + NL80211_ATTR_PROTOCOL_FEATURES, + NL80211_ATTR_SPLIT_WIPHY_DUMP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3619,4 +3635,16 @@ enum nl80211_dfs_state { NL80211_DFS_AVAILABLE, }; +/** + * enum enum nl80211_protocol_features - nl80211 protocol features + * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting + * wiphy dumps (if requested by the application with the attribute + * %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the + * wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or + * %NL80211_ATTR_WDEV. + */ +enum nl80211_protocol_features { + NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 83151a50e5a..f187a920ec7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -370,6 +370,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 }, [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, }, + [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, }, }; /* policy for the key attributes */ @@ -892,412 +893,545 @@ nla_put_failure: return -ENOBUFS; } -static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags, - struct cfg80211_registered_device *dev) +#ifdef CONFIG_PM +static int nl80211_send_wowlan(struct sk_buff *msg, + struct cfg80211_registered_device *dev) { - void *hdr; - struct nlattr *nl_bands, *nl_band; - struct nlattr *nl_freqs, *nl_freq; - struct nlattr *nl_rates, *nl_rate; - struct nlattr *nl_cmds; - enum ieee80211_band band; - struct ieee80211_channel *chan; - struct ieee80211_rate *rate; - int i; - const struct ieee80211_txrx_stypes *mgmt_stypes = - dev->wiphy.mgmt_stypes; + struct nlattr *nl_wowlan; - hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY); - if (!hdr) - return -1; + if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns) + return 0; - if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) || - nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)) || - nla_put_u32(msg, NL80211_ATTR_GENERATION, - cfg80211_rdev_list_generation) || - nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, - dev->wiphy.retry_short) || - nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, - dev->wiphy.retry_long) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, - dev->wiphy.frag_threshold) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, - dev->wiphy.rts_threshold) || - nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, - dev->wiphy.coverage_class) || - nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, - dev->wiphy.max_scan_ssids) || - nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, - dev->wiphy.max_sched_scan_ssids) || - nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, - dev->wiphy.max_scan_ie_len) || - nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, - dev->wiphy.max_sched_scan_ie_len) || - nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS, - dev->wiphy.max_match_sets)) - goto nla_put_failure; + nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); + if (!nl_wowlan) + return -ENOBUFS; - if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) && - nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN)) - goto nla_put_failure; - if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && - nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH)) - goto nla_put_failure; - if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && - nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD)) - goto nla_put_failure; - if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) && - nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT)) - goto nla_put_failure; - if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && - nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT)) - goto nla_put_failure; - if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) && - nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP)) - goto nla_put_failure; + if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) || + ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) || + ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) || + ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) || + ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || + ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || + ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || + ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) + return -ENOBUFS; - if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES, - sizeof(u32) * dev->wiphy.n_cipher_suites, - dev->wiphy.cipher_suites)) - goto nla_put_failure; + if (dev->wiphy.wowlan.n_patterns) { + struct nl80211_wowlan_pattern_support pat = { + .max_patterns = dev->wiphy.wowlan.n_patterns, + .min_pattern_len = dev->wiphy.wowlan.pattern_min_len, + .max_pattern_len = dev->wiphy.wowlan.pattern_max_len, + .max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset, + }; - if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS, - dev->wiphy.max_num_pmkids)) - goto nla_put_failure; + if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, + sizeof(pat), &pat)) + return -ENOBUFS; + } - if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && - nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE)) - goto nla_put_failure; + nla_nest_end(msg, nl_wowlan); - if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, - dev->wiphy.available_antennas_tx) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, - dev->wiphy.available_antennas_rx)) - goto nla_put_failure; + return 0; +} +#endif - if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) && - nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD, - dev->wiphy.probe_resp_offload)) - goto nla_put_failure; +static int nl80211_send_band_rateinfo(struct sk_buff *msg, + struct ieee80211_supported_band *sband) +{ + struct nlattr *nl_rates, *nl_rate; + struct ieee80211_rate *rate; + int i; - if ((dev->wiphy.available_antennas_tx || - dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) { - u32 tx_ant = 0, rx_ant = 0; - int res; - res = rdev_get_antenna(dev, &tx_ant, &rx_ant); - if (!res) { - if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, - tx_ant) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, - rx_ant)) - goto nla_put_failure; - } - } + /* add HT info */ + if (sband->ht_cap.ht_supported && + (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET, + sizeof(sband->ht_cap.mcs), + &sband->ht_cap.mcs) || + nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA, + sband->ht_cap.cap) || + nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR, + sband->ht_cap.ampdu_factor) || + nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + sband->ht_cap.ampdu_density))) + return -ENOBUFS; - if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, - dev->wiphy.interface_modes)) - goto nla_put_failure; + /* add VHT info */ + if (sband->vht_cap.vht_supported && + (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET, + sizeof(sband->vht_cap.vht_mcs), + &sband->vht_cap.vht_mcs) || + nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA, + sband->vht_cap.cap))) + return -ENOBUFS; - nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); - if (!nl_bands) - goto nla_put_failure; + /* add bitrates */ + nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES); + if (!nl_rates) + return -ENOBUFS; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!dev->wiphy.bands[band]) - continue; + for (i = 0; i < sband->n_bitrates; i++) { + nl_rate = nla_nest_start(msg, i); + if (!nl_rate) + return -ENOBUFS; - nl_band = nla_nest_start(msg, band); - if (!nl_band) - goto nla_put_failure; + rate = &sband->bitrates[i]; + if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE, + rate->bitrate)) + return -ENOBUFS; + if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) && + nla_put_flag(msg, + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE)) + return -ENOBUFS; - /* add HT info */ - if (dev->wiphy.bands[band]->ht_cap.ht_supported && - (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET, - sizeof(dev->wiphy.bands[band]->ht_cap.mcs), - &dev->wiphy.bands[band]->ht_cap.mcs) || - nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA, - dev->wiphy.bands[band]->ht_cap.cap) || - nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR, - dev->wiphy.bands[band]->ht_cap.ampdu_factor) || - nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, - dev->wiphy.bands[band]->ht_cap.ampdu_density))) - goto nla_put_failure; + nla_nest_end(msg, nl_rate); + } - /* add VHT info */ - if (dev->wiphy.bands[band]->vht_cap.vht_supported && - (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET, - sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs), - &dev->wiphy.bands[band]->vht_cap.vht_mcs) || - nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA, - dev->wiphy.bands[band]->vht_cap.cap))) - goto nla_put_failure; + nla_nest_end(msg, nl_rates); - /* add frequencies */ - nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); - if (!nl_freqs) - goto nla_put_failure; + return 0; +} - for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) { - nl_freq = nla_nest_start(msg, i); - if (!nl_freq) - goto nla_put_failure; +static int +nl80211_send_mgmt_stypes(struct sk_buff *msg, + const struct ieee80211_txrx_stypes *mgmt_stypes) +{ + u16 stypes; + struct nlattr *nl_ftypes, *nl_ifs; + enum nl80211_iftype ift; + int i; - chan = &dev->wiphy.bands[band]->channels[i]; + if (!mgmt_stypes) + return 0; - if (nl80211_msg_put_channel(msg, chan)) - goto nla_put_failure; + nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES); + if (!nl_ifs) + return -ENOBUFS; - nla_nest_end(msg, nl_freq); + for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { + nl_ftypes = nla_nest_start(msg, ift); + if (!nl_ftypes) + return -ENOBUFS; + i = 0; + stypes = mgmt_stypes[ift].tx; + while (stypes) { + if ((stypes & 1) && + nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, + (i << 4) | IEEE80211_FTYPE_MGMT)) + return -ENOBUFS; + stypes >>= 1; + i++; } + nla_nest_end(msg, nl_ftypes); + } - nla_nest_end(msg, nl_freqs); + nla_nest_end(msg, nl_ifs); - /* add bitrates */ - nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES); - if (!nl_rates) - goto nla_put_failure; + nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES); + if (!nl_ifs) + return -ENOBUFS; - for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) { - nl_rate = nla_nest_start(msg, i); - if (!nl_rate) - goto nla_put_failure; + for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { + nl_ftypes = nla_nest_start(msg, ift); + if (!nl_ftypes) + return -ENOBUFS; + i = 0; + stypes = mgmt_stypes[ift].rx; + while (stypes) { + if ((stypes & 1) && + nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, + (i << 4) | IEEE80211_FTYPE_MGMT)) + return -ENOBUFS; + stypes >>= 1; + i++; + } + nla_nest_end(msg, nl_ftypes); + } + nla_nest_end(msg, nl_ifs); - rate = &dev->wiphy.bands[band]->bitrates[i]; - if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE, - rate->bitrate)) - goto nla_put_failure; - if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) && - nla_put_flag(msg, - NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE)) - goto nla_put_failure; + return 0; +} - nla_nest_end(msg, nl_rate); - } +static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, + struct sk_buff *msg, u32 portid, u32 seq, + int flags, bool split, long *split_start, + long *band_start, long *chan_start) +{ + void *hdr; + struct nlattr *nl_bands, *nl_band; + struct nlattr *nl_freqs, *nl_freq; + struct nlattr *nl_cmds; + enum ieee80211_band band; + struct ieee80211_channel *chan; + int i; + const struct ieee80211_txrx_stypes *mgmt_stypes = + dev->wiphy.mgmt_stypes; + long start = 0, start_chan = 0, start_band = 0; - nla_nest_end(msg, nl_rates); + hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY); + if (!hdr) + return -ENOBUFS; - nla_nest_end(msg, nl_band); + /* allow always using the variables */ + if (!split) { + split_start = &start; + band_start = &start_band; + chan_start = &start_chan; } - nla_nest_end(msg, nl_bands); - nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS); - if (!nl_cmds) - goto nla_put_failure; + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) || + nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, + wiphy_name(&dev->wiphy)) || + nla_put_u32(msg, NL80211_ATTR_GENERATION, + cfg80211_rdev_list_generation)) + goto nla_put_failure; + + switch (*split_start) { + case 0: + if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, + dev->wiphy.retry_short) || + nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, + dev->wiphy.retry_long) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + dev->wiphy.frag_threshold) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, + dev->wiphy.rts_threshold) || + nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, + dev->wiphy.coverage_class) || + nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + dev->wiphy.max_scan_ssids) || + nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + dev->wiphy.max_sched_scan_ssids) || + nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, + dev->wiphy.max_scan_ie_len) || + nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + dev->wiphy.max_sched_scan_ie_len) || + nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS, + dev->wiphy.max_match_sets)) + goto nla_put_failure; - i = 0; -#define CMD(op, n) \ - do { \ - if (dev->ops->op) { \ - i++; \ - if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \ - goto nla_put_failure; \ - } \ - } while (0) - - CMD(add_virtual_intf, NEW_INTERFACE); - CMD(change_virtual_intf, SET_INTERFACE); - CMD(add_key, NEW_KEY); - CMD(start_ap, START_AP); - CMD(add_station, NEW_STATION); - CMD(add_mpath, NEW_MPATH); - CMD(update_mesh_config, SET_MESH_CONFIG); - CMD(change_bss, SET_BSS); - CMD(auth, AUTHENTICATE); - CMD(assoc, ASSOCIATE); - CMD(deauth, DEAUTHENTICATE); - CMD(disassoc, DISASSOCIATE); - CMD(join_ibss, JOIN_IBSS); - CMD(join_mesh, JOIN_MESH); - CMD(set_pmksa, SET_PMKSA); - CMD(del_pmksa, DEL_PMKSA); - CMD(flush_pmksa, FLUSH_PMKSA); - if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) - CMD(remain_on_channel, REMAIN_ON_CHANNEL); - CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); - CMD(mgmt_tx, FRAME); - CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); - if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { - i++; - if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS)) + if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) && + nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN)) goto nla_put_failure; - } - if (dev->ops->set_monitor_channel || dev->ops->start_ap || - dev->ops->join_mesh) { - i++; - if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) + if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && + nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH)) goto nla_put_failure; - } - CMD(set_wds_peer, SET_WDS_PEER); - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { - CMD(tdls_mgmt, TDLS_MGMT); - CMD(tdls_oper, TDLS_OPER); - } - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) - CMD(sched_scan_start, START_SCHED_SCAN); - CMD(probe_client, PROBE_CLIENT); - CMD(set_noack_map, SET_NOACK_MAP); - if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { - i++; - if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS)) + if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && + nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD)) + goto nla_put_failure; + if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) && + nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT)) + goto nla_put_failure; + if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && + nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT)) + goto nla_put_failure; + if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) && + nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP)) goto nla_put_failure; - } - CMD(start_p2p_device, START_P2P_DEVICE); - CMD(set_mcast_rate, SET_MCAST_RATE); -#ifdef CONFIG_NL80211_TESTMODE - CMD(testmode_cmd, TESTMODE); -#endif + (*split_start)++; + if (split) + break; + case 1: + if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES, + sizeof(u32) * dev->wiphy.n_cipher_suites, + dev->wiphy.cipher_suites)) + goto nla_put_failure; -#undef CMD + if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS, + dev->wiphy.max_num_pmkids)) + goto nla_put_failure; - if (dev->ops->connect || dev->ops->auth) { - i++; - if (nla_put_u32(msg, i, NL80211_CMD_CONNECT)) + if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && + nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE)) goto nla_put_failure; - } - if (dev->ops->disconnect || dev->ops->deauth) { - i++; - if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT)) + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, + dev->wiphy.available_antennas_tx) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + dev->wiphy.available_antennas_rx)) goto nla_put_failure; - } - nla_nest_end(msg, nl_cmds); + if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) && + nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD, + dev->wiphy.probe_resp_offload)) + goto nla_put_failure; - if (dev->ops->remain_on_channel && - (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) && - nla_put_u32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, - dev->wiphy.max_remain_on_channel_duration)) - goto nla_put_failure; + if ((dev->wiphy.available_antennas_tx || + dev->wiphy.available_antennas_rx) && + dev->ops->get_antenna) { + u32 tx_ant = 0, rx_ant = 0; + int res; + res = rdev_get_antenna(dev, &tx_ant, &rx_ant); + if (!res) { + if (nla_put_u32(msg, + NL80211_ATTR_WIPHY_ANTENNA_TX, + tx_ant) || + nla_put_u32(msg, + NL80211_ATTR_WIPHY_ANTENNA_RX, + rx_ant)) + goto nla_put_failure; + } + } - if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) && - nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) - goto nla_put_failure; + (*split_start)++; + if (split) + break; + case 2: + if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, + dev->wiphy.interface_modes)) + goto nla_put_failure; + (*split_start)++; + if (split) + break; + case 3: + nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); + if (!nl_bands) + goto nla_put_failure; - if (mgmt_stypes) { - u16 stypes; - struct nlattr *nl_ftypes, *nl_ifs; - enum nl80211_iftype ift; + for (band = *band_start; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband; - nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES); - if (!nl_ifs) - goto nla_put_failure; + sband = dev->wiphy.bands[band]; - for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { - nl_ftypes = nla_nest_start(msg, ift); - if (!nl_ftypes) + if (!sband) + continue; + + nl_band = nla_nest_start(msg, band); + if (!nl_band) goto nla_put_failure; - i = 0; - stypes = mgmt_stypes[ift].tx; - while (stypes) { - if ((stypes & 1) && - nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, - (i << 4) | IEEE80211_FTYPE_MGMT)) + + switch (*chan_start) { + case 0: + if (nl80211_send_band_rateinfo(msg, sband)) goto nla_put_failure; - stypes >>= 1; - i++; + (*chan_start)++; + if (split) + break; + default: + /* add frequencies */ + nl_freqs = nla_nest_start( + msg, NL80211_BAND_ATTR_FREQS); + if (!nl_freqs) + goto nla_put_failure; + + for (i = *chan_start - 1; + i < sband->n_channels; + i++) { + nl_freq = nla_nest_start(msg, i); + if (!nl_freq) + goto nla_put_failure; + + chan = &sband->channels[i]; + + if (nl80211_msg_put_channel(msg, chan)) + goto nla_put_failure; + + nla_nest_end(msg, nl_freq); + if (split) + break; + } + if (i < sband->n_channels) + *chan_start = i + 2; + else + *chan_start = 0; + nla_nest_end(msg, nl_freqs); + } + + nla_nest_end(msg, nl_band); + + if (split) { + /* start again here */ + if (*chan_start) + band--; + break; } - nla_nest_end(msg, nl_ftypes); } + nla_nest_end(msg, nl_bands); - nla_nest_end(msg, nl_ifs); + if (band < IEEE80211_NUM_BANDS) + *band_start = band + 1; + else + *band_start = 0; - nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES); - if (!nl_ifs) + /* if bands & channels are done, continue outside */ + if (*band_start == 0 && *chan_start == 0) + (*split_start)++; + if (split) + break; + case 4: + nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS); + if (!nl_cmds) goto nla_put_failure; - for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { - nl_ftypes = nla_nest_start(msg, ift); - if (!nl_ftypes) + i = 0; +#define CMD(op, n) \ + do { \ + if (dev->ops->op) { \ + i++; \ + if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \ + goto nla_put_failure; \ + } \ + } while (0) + + CMD(add_virtual_intf, NEW_INTERFACE); + CMD(change_virtual_intf, SET_INTERFACE); + CMD(add_key, NEW_KEY); + CMD(start_ap, START_AP); + CMD(add_station, NEW_STATION); + CMD(add_mpath, NEW_MPATH); + CMD(update_mesh_config, SET_MESH_CONFIG); + CMD(change_bss, SET_BSS); + CMD(auth, AUTHENTICATE); + CMD(assoc, ASSOCIATE); + CMD(deauth, DEAUTHENTICATE); + CMD(disassoc, DISASSOCIATE); + CMD(join_ibss, JOIN_IBSS); + CMD(join_mesh, JOIN_MESH); + CMD(set_pmksa, SET_PMKSA); + CMD(del_pmksa, DEL_PMKSA); + CMD(flush_pmksa, FLUSH_PMKSA); + if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) + CMD(remain_on_channel, REMAIN_ON_CHANNEL); + CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); + CMD(mgmt_tx, FRAME); + CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); + if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS)) goto nla_put_failure; - i = 0; - stypes = mgmt_stypes[ift].rx; - while (stypes) { - if ((stypes & 1) && - nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, - (i << 4) | IEEE80211_FTYPE_MGMT)) - goto nla_put_failure; - stypes >>= 1; - i++; - } - nla_nest_end(msg, nl_ftypes); } - nla_nest_end(msg, nl_ifs); - } + if (dev->ops->set_monitor_channel || dev->ops->start_ap || + dev->ops->join_mesh) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) + goto nla_put_failure; + } + CMD(set_wds_peer, SET_WDS_PEER); + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { + CMD(tdls_mgmt, TDLS_MGMT); + CMD(tdls_oper, TDLS_OPER); + } + if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) + CMD(sched_scan_start, START_SCHED_SCAN); + CMD(probe_client, PROBE_CLIENT); + CMD(set_noack_map, SET_NOACK_MAP); + if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS)) + goto nla_put_failure; + } + CMD(start_p2p_device, START_P2P_DEVICE); + CMD(set_mcast_rate, SET_MCAST_RATE); -#ifdef CONFIG_PM - if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { - struct nlattr *nl_wowlan; +#ifdef CONFIG_NL80211_TESTMODE + CMD(testmode_cmd, TESTMODE); +#endif - nl_wowlan = nla_nest_start(msg, - NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); - if (!nl_wowlan) - goto nla_put_failure; +#undef CMD - if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) && - nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) || - ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) && - nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) || - ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) && - nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) || - ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) && - nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) || - ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && - nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || - ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) && - nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || - ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) && - nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || - ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) && - nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) - goto nla_put_failure; - if (dev->wiphy.wowlan.n_patterns) { - struct nl80211_wowlan_pattern_support pat = { - .max_patterns = dev->wiphy.wowlan.n_patterns, - .min_pattern_len = - dev->wiphy.wowlan.pattern_min_len, - .max_pattern_len = - dev->wiphy.wowlan.pattern_max_len, - .max_pkt_offset = - dev->wiphy.wowlan.max_pkt_offset, - }; - if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, - sizeof(pat), &pat)) + if (dev->ops->connect || dev->ops->auth) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_CONNECT)) goto nla_put_failure; } - nla_nest_end(msg, nl_wowlan); - } + if (dev->ops->disconnect || dev->ops->deauth) { + i++; + if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT)) + goto nla_put_failure; + } + + nla_nest_end(msg, nl_cmds); + (*split_start)++; + if (split) + break; + case 5: + if (dev->ops->remain_on_channel && + (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) && + nla_put_u32(msg, + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, + dev->wiphy.max_remain_on_channel_duration)) + goto nla_put_failure; + + if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) && + nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) + goto nla_put_failure; + + if (nl80211_send_mgmt_stypes(msg, mgmt_stypes)) + goto nla_put_failure; + (*split_start)++; + if (split) + break; + case 6: +#ifdef CONFIG_PM + if (nl80211_send_wowlan(msg, dev)) + goto nla_put_failure; + (*split_start)++; + if (split) + break; +#else + (*split_start)++; #endif + case 7: + if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, + dev->wiphy.software_iftypes)) + goto nla_put_failure; - if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, - dev->wiphy.software_iftypes)) - goto nla_put_failure; + if (nl80211_put_iface_combinations(&dev->wiphy, msg)) + goto nla_put_failure; - if (nl80211_put_iface_combinations(&dev->wiphy, msg)) - goto nla_put_failure; + (*split_start)++; + if (split) + break; + case 8: + if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) && + nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME, + dev->wiphy.ap_sme_capa)) + goto nla_put_failure; - if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) && - nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME, - dev->wiphy.ap_sme_capa)) - goto nla_put_failure; + if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, + dev->wiphy.features)) + goto nla_put_failure; - if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, - dev->wiphy.features)) - goto nla_put_failure; + if (dev->wiphy.ht_capa_mod_mask && + nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, + sizeof(*dev->wiphy.ht_capa_mod_mask), + dev->wiphy.ht_capa_mod_mask)) + goto nla_put_failure; - if (dev->wiphy.ht_capa_mod_mask && - nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, - sizeof(*dev->wiphy.ht_capa_mod_mask), - dev->wiphy.ht_capa_mod_mask)) - goto nla_put_failure; + if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME && + dev->wiphy.max_acl_mac_addrs && + nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX, + dev->wiphy.max_acl_mac_addrs)) + goto nla_put_failure; - if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME && - dev->wiphy.max_acl_mac_addrs && - nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX, - dev->wiphy.max_acl_mac_addrs)) - goto nla_put_failure; + /* + * Any information below this point is only available to + * applications that can deal with it being split. This + * helps ensure that newly added capabilities don't break + * older tools by overrunning their buffers. + * + * We still increment split_start so that in the split + * case we'll continue with more data in the next round, + * but break unconditionally so unsplit data stops here. + */ + (*split_start)++; + break; + case 9: + /* placeholder */ + /* done */ + *split_start = 0; + break; + } return genlmsg_end(msg, hdr); nla_put_failure: @@ -1310,39 +1444,80 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) int idx = 0, ret; int start = cb->args[0]; struct cfg80211_registered_device *dev; + s64 filter_wiphy = -1; + bool split = false; + struct nlattr **tb = nl80211_fam.attrbuf; + int res; mutex_lock(&cfg80211_mutex); + res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, + tb, nl80211_fam.maxattr, nl80211_policy); + if (res == 0) { + split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP]; + if (tb[NL80211_ATTR_WIPHY]) + filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + if (tb[NL80211_ATTR_WDEV]) + filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32; + if (tb[NL80211_ATTR_IFINDEX]) { + struct net_device *netdev; + int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + + netdev = dev_get_by_index(sock_net(skb->sk), ifidx); + if (!netdev) { + mutex_unlock(&cfg80211_mutex); + return -ENODEV; + } + if (netdev->ieee80211_ptr) { + dev = wiphy_to_dev( + netdev->ieee80211_ptr->wiphy); + filter_wiphy = dev->wiphy_idx; + } + dev_put(netdev); + } + } + list_for_each_entry(dev, &cfg80211_rdev_list, list) { if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) continue; if (++idx <= start) continue; - ret = nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - dev); - if (ret < 0) { - /* - * If sending the wiphy data didn't fit (ENOBUFS or - * EMSGSIZE returned), this SKB is still empty (so - * it's not too big because another wiphy dataset is - * already in the skb) and we've not tried to adjust - * the dump allocation yet ... then adjust the alloc - * size to be bigger, and return 1 but with the empty - * skb. This results in an empty message being RX'ed - * in userspace, but that is ignored. - * - * We can then retry with the larger buffer. - */ - if ((ret == -ENOBUFS || ret == -EMSGSIZE) && - !skb->len && - cb->min_dump_alloc < 4096) { - cb->min_dump_alloc = 4096; - mutex_unlock(&cfg80211_mutex); - return 1; + if (filter_wiphy != -1 && dev->wiphy_idx != filter_wiphy) + continue; + /* attempt to fit multiple wiphy data chunks into the skb */ + do { + ret = nl80211_send_wiphy(dev, skb, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NLM_F_MULTI, + split, &cb->args[1], + &cb->args[2], + &cb->args[3]); + if (ret < 0) { + /* + * If sending the wiphy data didn't fit (ENOBUFS + * or EMSGSIZE returned), this SKB is still + * empty (so it's not too big because another + * wiphy dataset is already in the skb) and + * we've not tried to adjust the dump allocation + * yet ... then adjust the alloc size to be + * bigger, and return 1 but with the empty skb. + * This results in an empty message being RX'ed + * in userspace, but that is ignored. + * + * We can then retry with the larger buffer. + */ + if ((ret == -ENOBUFS || ret == -EMSGSIZE) && + !skb->len && + cb->min_dump_alloc < 4096) { + cb->min_dump_alloc = 4096; + mutex_unlock(&cfg80211_mutex); + return 1; + } + idx--; + break; } - idx--; - break; - } + } while (cb->args[1] > 0); + break; } mutex_unlock(&cfg80211_mutex); @@ -1360,7 +1535,8 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) if (!msg) return -ENOMEM; - if (nl80211_send_wiphy(msg, info->snd_portid, info->snd_seq, 0, dev) < 0) { + if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0, + false, NULL, NULL, NULL) < 0) { nlmsg_free(msg); return -ENOBUFS; } @@ -7821,6 +7997,33 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info) return 0; } +static int nl80211_get_protocol_features(struct sk_buff *skb, + struct genl_info *info) +{ + void *hdr; + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + NL80211_CMD_GET_PROTOCOL_FEATURES); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_PROTOCOL_FEATURES, + NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + + nla_put_failure: + kfree_skb(msg); + return -ENOBUFS; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -8497,6 +8700,11 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES, + .doit = nl80211_get_protocol_features, + .policy = nl80211_policy, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -8524,7 +8732,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) if (!msg) return; - if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) { + if (nl80211_send_wiphy(rdev, msg, 0, 0, 0, + false, NULL, NULL, NULL) < 0) { nlmsg_free(msg); return; } -- cgit v1.2.3-70-g09d2 From 9a886586c82aa02cb49f8c85e961595716884545 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Feb 2013 19:25:00 +0100 Subject: wireless: move sequence number arithmetic to ieee80211.h Move the sequence number arithmetic code from mac80211 to ieee80211.h so others can use it. Also rename the functions from _seq to _sn, they operate on the sequence number, not the sequence_control field. Also move macros to convert the sequence control to/from the sequence number value from various drivers. Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlegacy/3945.h | 4 --- drivers/net/wireless/iwlegacy/4965-mac.c | 13 ++++---- drivers/net/wireless/iwlegacy/common.h | 4 --- drivers/net/wireless/iwlwifi/dvm/tx.c | 11 ++++--- drivers/net/wireless/iwlwifi/iwl-trans.h | 3 -- drivers/net/wireless/iwlwifi/mvm/sta.c | 4 +-- drivers/net/wireless/iwlwifi/mvm/tx.c | 2 +- drivers/net/wireless/iwlwifi/pcie/tx.c | 2 +- drivers/net/wireless/rtlwifi/wifi.h | 3 -- include/linux/ieee80211.h | 28 +++++++++++++++++ net/mac80211/rx.c | 53 +++++++++++++------------------- 11 files changed, 66 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/iwlegacy/3945.h b/drivers/net/wireless/iwlegacy/3945.h index 1d45075e0d5..9a8703def0b 100644 --- a/drivers/net/wireless/iwlegacy/3945.h +++ b/drivers/net/wireless/iwlegacy/3945.h @@ -150,10 +150,6 @@ struct il3945_frame { struct list_head list; }; -#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) -#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) -#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) - #define SUP_RATE_11A_MAX_NUM_CHANNELS 8 #define SUP_RATE_11B_MAX_NUM_CHANNELS 4 #define SUP_RATE_11G_MAX_NUM_CHANNELS 12 diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 7941eb3a016..c092fcbbe96 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -2258,7 +2258,7 @@ il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif, spin_lock_irqsave(&il->sta_lock, flags); tid_data = &il->stations[sta_id].tid[tid]; - *ssn = SEQ_TO_SN(tid_data->seq_number); + *ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); tid_data->agg.txq_id = txq_id; il_set_swq_id(&il->txq[txq_id], il4965_get_ac_from_tid(tid), txq_id); spin_unlock_irqrestore(&il->sta_lock, flags); @@ -2408,7 +2408,7 @@ il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id) /* aggregated HW queue */ if (txq_id == tid_data->agg.txq_id && q->read_ptr == q->write_ptr) { - u16 ssn = SEQ_TO_SN(tid_data->seq_number); + u16 ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); int tx_fifo = il4965_get_fifo_from_tid(tid); D_HT("HW queue empty: continue DELBA flow\n"); il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo); @@ -2627,7 +2627,8 @@ il4965_get_ra_sta_id(struct il_priv *il, struct ieee80211_hdr *hdr) static inline u32 il4965_get_scd_ssn(struct il4965_tx_resp *tx_resp) { - return le32_to_cpup(&tx_resp->u.status + tx_resp->frame_count) & MAX_SN; + return le32_to_cpup(&tx_resp->u.status + + tx_resp->frame_count) & IEEE80211_MAX_SN; } static inline u32 @@ -2717,15 +2718,15 @@ il4965_tx_status_reply_tx(struct il_priv *il, struct il_ht_agg *agg, hdr = (struct ieee80211_hdr *) skb->data; sc = le16_to_cpu(hdr->seq_ctrl); - if (idx != (SEQ_TO_SN(sc) & 0xff)) { + if (idx != (IEEE80211_SEQ_TO_SN(sc) & 0xff)) { IL_ERR("BUG_ON idx doesn't match seq control" " idx=%d, seq_idx=%d, seq=%d\n", idx, - SEQ_TO_SN(sc), hdr->seq_ctrl); + IEEE80211_SEQ_TO_SN(sc), hdr->seq_ctrl); return -1; } D_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n", i, idx, - SEQ_TO_SN(sc)); + IEEE80211_SEQ_TO_SN(sc)); sh = idx - start; if (sh > 64) { diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index 96f2025d936..73bd3ef316c 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -541,10 +541,6 @@ struct il_frame { struct list_head list; }; -#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) -#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) -#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) - enum { CMD_SYNC = 0, CMD_SIZE_NORMAL = 0, diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 6aec2df3bb2..d499a0366fa 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -418,7 +418,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv, " Tx flags = 0x%08x, agg.state = %d", info->flags, tid_data->agg.state); IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d", - sta_id, tid, SEQ_TO_SN(tid_data->seq_number)); + sta_id, tid, + IEEE80211_SEQ_TO_SN(tid_data->seq_number)); goto drop_unlock_sta; } @@ -569,7 +570,7 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, return 0; } - tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); + tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); /* There are still packets for this RA / TID in the HW */ if (!test_bit(txq_id, priv->agg_q_alloc)) { @@ -651,7 +652,7 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, spin_lock_bh(&priv->sta_lock); tid_data = &priv->tid_data[sta_id][tid]; - tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); + tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); tid_data->agg.txq_id = txq_id; *ssn = tid_data->agg.ssn; @@ -911,7 +912,7 @@ static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) { return le32_to_cpup((__le32 *)&tx_resp->status + - tx_resp->frame_count) & MAX_SN; + tx_resp->frame_count) & IEEE80211_MAX_SN; } static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, @@ -1148,7 +1149,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (tx_resp->frame_count == 1) { u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); - next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10); + next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10); if (is_agg) { /* If this is an aggregation queue, we can rely on the diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 8c7bec6b9a0..00bdc5b00af 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -114,9 +114,6 @@ * completely agnostic to these differences. * The transport does provide helper functionnality (i.e. SYNC / ASYNC mode), */ -#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) -#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) -#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) #define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f) #define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8) #define SEQ_TO_INDEX(s) ((s) & 0xff) diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 861a7f9f8e7..52aecf20d0d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -686,7 +686,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, spin_lock_bh(&mvmsta->lock); tid_data = &mvmsta->tid_data[tid]; - tid_data->ssn = SEQ_TO_SN(tid_data->seq_number); + tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); tid_data->txq_id = txq_id; *ssn = tid_data->ssn; @@ -779,7 +779,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, switch (tid_data->state) { case IWL_AGG_ON: - tid_data->ssn = SEQ_TO_SN(tid_data->seq_number); + tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); IWL_DEBUG_TX_QUEUES(mvm, "ssn = %d, next_recl = %d\n", diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 6b67ce3f679..56df249b215 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -641,7 +641,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, next_reclaimed = ssn; } else { /* The next packet to be reclaimed is the one after this one */ - next_reclaimed = SEQ_TO_SN(seq_ctl + 0x10); + next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10); } IWL_DEBUG_TX_REPLY(mvm, diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 8e9e3212fe7..ad7441dfa6f 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1581,7 +1581,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, * Check here that the packets are in the right place on the ring. */ #ifdef CONFIG_IWLWIFI_DEBUG - wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); + wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) && ((wifi_seq & 0xff) != q->write_ptr), "Q: %d WiFi Seq %d tfdNum %d", diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index f13258a8d99..c3eff32acf6 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -2127,9 +2127,6 @@ value to host byte ordering.*/ #define WLAN_FC_GET_TYPE(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) #define WLAN_FC_GET_STYPE(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) #define WLAN_FC_MORE_DATA(fc) (le16_to_cpu(fc) & IEEE80211_FCTL_MOREDATA) -#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) -#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) -#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) #define RT_RF_OFF_LEVL_ASPM BIT(0) /*PCI ASPM */ #define RT_RF_OFF_LEVL_CLK_REQ BIT(1) /*PCI clock request */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 7e24fe0cfbc..a0c550fb65a 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -113,6 +113,34 @@ #define IEEE80211_CTL_EXT_SSW_FBACK 0x9000 #define IEEE80211_CTL_EXT_SSW_ACK 0xa000 + +#define IEEE80211_SN_MASK ((IEEE80211_SCTL_SEQ) >> 4) +#define IEEE80211_MAX_SN IEEE80211_SN_MASK +#define IEEE80211_SN_MODULO (IEEE80211_MAX_SN + 1) + +static inline int ieee80211_sn_less(u16 sn1, u16 sn2) +{ + return ((sn1 - sn2) & IEEE80211_SN_MASK) > (IEEE80211_SN_MODULO >> 1); +} + +static inline u16 ieee80211_sn_add(u16 sn1, u16 sn2) +{ + return (sn1 + sn2) & IEEE80211_SN_MASK; +} + +static inline u16 ieee80211_sn_inc(u16 sn) +{ + return ieee80211_sn_add(sn, 1); +} + +static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2) +{ + return (sn1 - sn2) & IEEE80211_SN_MASK; +} + +#define IEEE80211_SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) +#define IEEE80211_SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) + /* miscellaneous IEEE 802.11 constants */ #define IEEE80211_MAX_FRAG_THRESHOLD 2352 #define IEEE80211_MAX_RTS_THRESHOLD 2353 diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index acf006f2d61..1f940e2b6f2 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -648,24 +648,6 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) return RX_CONTINUE; } -#define SEQ_MODULO 0x1000 -#define SEQ_MASK 0xfff - -static inline int seq_less(u16 sq1, u16 sq2) -{ - return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1); -} - -static inline u16 seq_inc(u16 sq) -{ - return (sq + 1) & SEQ_MASK; -} - -static inline u16 seq_sub(u16 sq1, u16 sq2) -{ - return (sq1 - sq2) & SEQ_MASK; -} - static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, int index, @@ -687,7 +669,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, __skb_queue_tail(frames, skb); no_frame: - tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); + tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num); } static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata, @@ -699,8 +681,9 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata lockdep_assert_held(&tid_agg_rx->reorder_lock); - while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) { - index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % + while (ieee80211_sn_less(tid_agg_rx->head_seq_num, head_seq_num)) { + index = ieee80211_sn_sub(tid_agg_rx->head_seq_num, + tid_agg_rx->ssn) % tid_agg_rx->buf_size; ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, frames); @@ -727,8 +710,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, lockdep_assert_held(&tid_agg_rx->reorder_lock); /* release the buffer until next missing frame */ - index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % - tid_agg_rx->buf_size; + index = ieee80211_sn_sub(tid_agg_rx->head_seq_num, + tid_agg_rx->ssn) % tid_agg_rx->buf_size; if (!tid_agg_rx->reorder_buf[index] && tid_agg_rx->stored_mpdu_num) { /* @@ -756,19 +739,22 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, * Increment the head seq# also for the skipped slots. */ tid_agg_rx->head_seq_num = - (tid_agg_rx->head_seq_num + skipped) & SEQ_MASK; + (tid_agg_rx->head_seq_num + + skipped) & IEEE80211_SN_MASK; skipped = 0; } } else while (tid_agg_rx->reorder_buf[index]) { ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, frames); - index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % + index = ieee80211_sn_sub(tid_agg_rx->head_seq_num, + tid_agg_rx->ssn) % tid_agg_rx->buf_size; } if (tid_agg_rx->stored_mpdu_num) { - j = index = seq_sub(tid_agg_rx->head_seq_num, - tid_agg_rx->ssn) % tid_agg_rx->buf_size; + j = index = ieee80211_sn_sub(tid_agg_rx->head_seq_num, + tid_agg_rx->ssn) % + tid_agg_rx->buf_size; for (; j != (index - 1) % tid_agg_rx->buf_size; j = (j + 1) % tid_agg_rx->buf_size) { @@ -809,7 +795,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata head_seq_num = tid_agg_rx->head_seq_num; /* frame with out of date sequence number */ - if (seq_less(mpdu_seq_num, head_seq_num)) { + if (ieee80211_sn_less(mpdu_seq_num, head_seq_num)) { dev_kfree_skb(skb); goto out; } @@ -818,8 +804,9 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata * If frame the sequence number exceeds our buffering window * size release some previous frames to make room for this one. */ - if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) { - head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size)); + if (!ieee80211_sn_less(mpdu_seq_num, head_seq_num + buf_size)) { + head_seq_num = ieee80211_sn_inc( + ieee80211_sn_sub(mpdu_seq_num, buf_size)); /* release stored frames up to new head to stack */ ieee80211_release_reorder_frames(sdata, tid_agg_rx, head_seq_num, frames); @@ -827,7 +814,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata /* Now the new frame is always in the range of the reordering buffer */ - index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size; + index = ieee80211_sn_sub(mpdu_seq_num, + tid_agg_rx->ssn) % tid_agg_rx->buf_size; /* check if we already stored this frame */ if (tid_agg_rx->reorder_buf[index]) { @@ -843,7 +831,8 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata */ if (mpdu_seq_num == tid_agg_rx->head_seq_num && tid_agg_rx->stored_mpdu_num == 0) { - tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num); + tid_agg_rx->head_seq_num = + ieee80211_sn_inc(tid_agg_rx->head_seq_num); ret = false; goto out; } -- cgit v1.2.3-70-g09d2 From b8a31c9a5afff257cc5dd637cda5fef03e12d67b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 22 Feb 2013 17:28:49 +0100 Subject: ieee80211: mark 802.11 related structs as being 2-byte aligned Regardless of what header features they use, or if they align the IP header or not, 802.11 packets from all drivers guarantee a 2-byte alignment (and there's a debug WARN_ON in case they don't). Annotate packet structs with __aligned(2) to allow the compiler to use 16-bit load/store operations on platforms with extremely inefficient unaligned access (e.g. MIPS). This reduces code size and improves performance on affected platforms and causes no binary code change on others. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index a0c550fb65a..6e352c31fee 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -213,7 +213,7 @@ struct ieee80211_hdr { u8 addr3[6]; __le16 seq_ctrl; u8 addr4[6]; -} __packed; +} __packed __aligned(2); struct ieee80211_hdr_3addr { __le16 frame_control; @@ -222,7 +222,7 @@ struct ieee80211_hdr_3addr { u8 addr2[6]; u8 addr3[6]; __le16 seq_ctrl; -} __packed; +} __packed __aligned(2); struct ieee80211_qos_hdr { __le16 frame_control; @@ -232,7 +232,7 @@ struct ieee80211_qos_hdr { u8 addr3[6]; __le16 seq_ctrl; __le16 qos_ctrl; -} __packed; +} __packed __aligned(2); /** * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set @@ -609,7 +609,7 @@ struct ieee80211s_hdr { __le32 seqnum; u8 eaddr1[6]; u8 eaddr2[6]; -} __packed; +} __packed __aligned(2); /* Mesh flags */ #define MESH_FLAGS_AE_A4 0x1 @@ -903,7 +903,7 @@ struct ieee80211_mgmt { } u; } __packed action; } u; -} __packed; +} __packed __aligned(2); /* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */ #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 @@ -934,20 +934,20 @@ struct ieee80211_rts { __le16 duration; u8 ra[6]; u8 ta[6]; -} __packed; +} __packed __aligned(2); struct ieee80211_cts { __le16 frame_control; __le16 duration; u8 ra[6]; -} __packed; +} __packed __aligned(2); struct ieee80211_pspoll { __le16 frame_control; __le16 aid; u8 bssid[6]; u8 ta[6]; -} __packed; +} __packed __aligned(2); /* TDLS */ -- cgit v1.2.3-70-g09d2 From c8bb93f5f5d478a01db66127844d1d2dd30abec7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 21 Feb 2013 17:26:44 +0100 Subject: wireless: remove unused VHT MCS defines There's an enum with the same values (but slightly different names except for NOT_SUPPORTED) that is actually used, so remove the defines. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 6e352c31fee..35c1f96d936 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1318,11 +1318,6 @@ struct ieee80211_vht_operation { } __packed; -#define IEEE80211_VHT_MCS_ZERO_TO_SEVEN_SUPPORT 0 -#define IEEE80211_VHT_MCS_ZERO_TO_EIGHT_SUPPORT 1 -#define IEEE80211_VHT_MCS_ZERO_TO_NINE_SUPPORT 2 -#define IEEE80211_VHT_MCS_NOT_SUPPORTED 3 - /* 802.11ac VHT Capabilities */ #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 0x00000000 #define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 0x00000001 -- cgit v1.2.3-70-g09d2 From ee2aca343c9aa64d277a75a5df043299dc84cfd9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 21 Feb 2013 17:36:01 +0100 Subject: cfg80211: add ability to override VHT capabilities For testing it's sometimes useful to be able to override certain VHT capability advertisement, add the ability to do that in cfg80211. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 12 +++++++++++ include/uapi/linux/nl80211.h | 3 +++ net/wireless/core.h | 10 ++++++++-- net/wireless/mlme.c | 35 ++++++++++++++++++++++++++++++--- net/wireless/nl80211.c | 47 ++++++++++++++++++++++++++++++++++++++++++-- net/wireless/sme.c | 4 +++- 6 files changed, 103 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ed2b08da3b9..73a523901c7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1430,9 +1430,11 @@ struct cfg80211_auth_request { * enum cfg80211_assoc_req_flags - Over-ride default behaviour in association. * * @ASSOC_REQ_DISABLE_HT: Disable HT (802.11n) + * @ASSOC_REQ_DISABLE_VHT: Disable VHT */ enum cfg80211_assoc_req_flags { ASSOC_REQ_DISABLE_HT = BIT(0), + ASSOC_REQ_DISABLE_VHT = BIT(1), }; /** @@ -1454,6 +1456,8 @@ enum cfg80211_assoc_req_flags { * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask * will be used in ht_capa. Un-supported values will be ignored. * @ht_capa_mask: The bits of ht_capa which are to be used. + * @vht_capa: VHT capability override + * @vht_capa_mask: VHT capability mask indicating which fields to use */ struct cfg80211_assoc_request { struct cfg80211_bss *bss; @@ -1464,6 +1468,7 @@ struct cfg80211_assoc_request { u32 flags; struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; + struct ieee80211_vht_cap vht_capa, vht_capa_mask; }; /** @@ -1574,6 +1579,8 @@ struct cfg80211_ibss_params { * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask * will be used in ht_capa. Un-supported values will be ignored. * @ht_capa_mask: The bits of ht_capa which are to be used. + * @vht_capa: VHT Capability overrides + * @vht_capa_mask: The bits of vht_capa which are to be used. */ struct cfg80211_connect_params { struct ieee80211_channel *channel; @@ -1592,6 +1599,8 @@ struct cfg80211_connect_params { int bg_scan_period; struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; + struct ieee80211_vht_cap vht_capa; + struct ieee80211_vht_cap vht_capa_mask; }; /** @@ -2516,6 +2525,8 @@ struct wiphy_wowlan_support { * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features. * @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden. * If null, then none can be over-ridden. + * @vht_capa_mod_mask: Specify what VHT capabilities can be over-ridden. + * If null, then none can be over-ridden. * * @max_acl_mac_addrs: Maximum number of MAC addresses that the device * supports for ACL. @@ -2624,6 +2635,7 @@ struct wiphy { struct dentry *debugfsdir; const struct ieee80211_ht_cap *ht_capa_mod_mask; + const struct ieee80211_vht_cap *vht_capa_mod_mask; #ifdef CONFIG_NET_NS /* the network namespace this phy lives in currently */ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9844c10a299..2c3e8836003 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1685,6 +1685,9 @@ enum nl80211_attrs { NL80211_ATTR_PROTOCOL_FEATURES, NL80211_ATTR_SPLIT_WIPHY_DUMP, + NL80211_ATTR_DISABLE_VHT, + NL80211_ATTR_VHT_CAPABILITY_MASK, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/core.h b/net/wireless/core.h index 3aec0e429d8..c2f94f22bb2 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -335,7 +335,9 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt, u32 assoc_flags, struct ieee80211_ht_cap *ht_capa, - struct ieee80211_ht_cap *ht_capa_mask); + struct ieee80211_ht_cap *ht_capa_mask, + struct ieee80211_vht_cap *vht_capa, + struct ieee80211_vht_cap *vht_capa_mask); int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, const u8 *bssid, const u8 *prev_bssid, @@ -343,7 +345,9 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt, u32 assoc_flags, struct ieee80211_ht_cap *ht_capa, - struct ieee80211_ht_cap *ht_capa_mask); + struct ieee80211_ht_cap *ht_capa_mask, + struct ieee80211_vht_cap *vht_capa, + struct ieee80211_vht_cap *vht_capa_mask); int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason, @@ -375,6 +379,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, bool no_cck, bool dont_wait_for_ack, u64 *cookie); void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, const struct ieee80211_ht_cap *ht_capa_mask); +void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, + const struct ieee80211_vht_cap *vht_capa_mask); /* SME */ int __cfg80211_connect(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 5a97ce6d283..c82adfee769 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -343,6 +343,23 @@ void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, p1[i] &= p2[i]; } +/* Do a logical ht_capa &= ht_capa_mask. */ +void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, + const struct ieee80211_vht_cap *vht_capa_mask) +{ + int i; + u8 *p1, *p2; + if (!vht_capa_mask) { + memset(vht_capa, 0, sizeof(*vht_capa)); + return; + } + + p1 = (u8*)(vht_capa); + p2 = (u8*)(vht_capa_mask); + for (i = 0; i < sizeof(*vht_capa); i++) + p1[i] &= p2[i]; +} + int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, @@ -351,7 +368,9 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt, u32 assoc_flags, struct ieee80211_ht_cap *ht_capa, - struct ieee80211_ht_cap *ht_capa_mask) + struct ieee80211_ht_cap *ht_capa_mask, + struct ieee80211_vht_cap *vht_capa, + struct ieee80211_vht_cap *vht_capa_mask) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_assoc_request req; @@ -388,6 +407,13 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, sizeof(req.ht_capa_mask)); cfg80211_oper_and_ht_capa(&req.ht_capa_mask, rdev->wiphy.ht_capa_mod_mask); + if (vht_capa) + memcpy(&req.vht_capa, vht_capa, sizeof(req.vht_capa)); + if (vht_capa_mask) + memcpy(&req.vht_capa_mask, vht_capa_mask, + sizeof(req.vht_capa_mask)); + cfg80211_oper_and_vht_capa(&req.vht_capa_mask, + rdev->wiphy.vht_capa_mod_mask); req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); @@ -422,7 +448,9 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, const u8 *ie, int ie_len, bool use_mfp, struct cfg80211_crypto_settings *crypt, u32 assoc_flags, struct ieee80211_ht_cap *ht_capa, - struct ieee80211_ht_cap *ht_capa_mask) + struct ieee80211_ht_cap *ht_capa_mask, + struct ieee80211_vht_cap *vht_capa, + struct ieee80211_vht_cap *vht_capa_mask) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -431,7 +459,8 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, wdev_lock(wdev); err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, ssid, ssid_len, ie, ie_len, use_mfp, crypt, - assoc_flags, ht_capa, ht_capa_mask); + assoc_flags, ht_capa, ht_capa_mask, + vht_capa, vht_capa_mask); wdev_unlock(wdev); mutex_unlock(&rdev->devlist_mtx); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0e5176784b4..6a5893f5e48 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -371,6 +371,10 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 }, [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, }, [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, }, + [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG }, + [NL80211_ATTR_VHT_CAPABILITY_MASK] = { + .len = NL80211_VHT_CAPABILITY_LEN, + }, }; /* policy for the key attributes */ @@ -1522,6 +1526,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, dev->wiphy.extended_capabilities_mask))) goto nla_put_failure; + if (dev->wiphy.vht_capa_mod_mask && + nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, + sizeof(*dev->wiphy.vht_capa_mod_mask), + dev->wiphy.vht_capa_mod_mask)) + goto nla_put_failure; + /* done */ *split_start = 0; break; @@ -5982,6 +5992,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) u32 flags = 0; struct ieee80211_ht_cap *ht_capa = NULL; struct ieee80211_ht_cap *ht_capa_mask = NULL; + struct ieee80211_vht_cap *vht_capa = NULL; + struct ieee80211_vht_cap *vht_capa_mask = NULL; if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; @@ -6038,12 +6050,25 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); } + if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT])) + flags |= ASSOC_REQ_DISABLE_VHT; + + if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) + vht_capa_mask = + nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]); + + if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) { + if (!vht_capa_mask) + return -EINVAL; + vht_capa = nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); + } + err = nl80211_crypto_settings(rdev, info, &crypto, 1); if (!err) err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, ssid, ssid_len, ie, ie_len, use_mfp, - &crypto, flags, ht_capa, - ht_capa_mask); + &crypto, flags, ht_capa, ht_capa_mask, + vht_capa, vht_capa_mask); return err; } @@ -6623,6 +6648,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) sizeof(connect.ht_capa)); } + if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT])) + connect.flags |= ASSOC_REQ_DISABLE_VHT; + + if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) + memcpy(&connect.vht_capa_mask, + nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]), + sizeof(connect.vht_capa_mask)); + + if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) { + if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) { + kfree(connkeys); + return -EINVAL; + } + memcpy(&connect.vht_capa, + nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]), + sizeof(connect.vht_capa)); + } + err = cfg80211_connect(rdev, dev, &connect, connkeys); if (err) kfree(connkeys); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index f432bd3755b..7da118c034f 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -195,7 +195,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) params->mfp != NL80211_MFP_NO, ¶ms->crypto, params->flags, ¶ms->ht_capa, - ¶ms->ht_capa_mask); + ¶ms->ht_capa_mask, + ¶ms->vht_capa, + ¶ms->vht_capa_mask); if (err) __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, NULL, 0, -- cgit v1.2.3-70-g09d2 From d339d5ca8eee34f3c70386cf2545edc53e546a13 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 12 Feb 2013 09:34:13 +0200 Subject: mac80211: Allow drivers to differentiate between ROC types Some devices can handle remain on channel requests differently based on the request type/priority. Add support to differentiate between different ROC types, i.e., indicate that the ROC is required for sending managment frames. Signed-off-by: Ilan Peer Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 3 ++- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 7 ++++--- drivers/net/wireless/mac80211_hwsim.c | 3 ++- drivers/net/wireless/ti/wlcore/main.c | 3 ++- include/net/mac80211.h | 21 ++++++++++++++++++++- net/mac80211/cfg.c | 21 +++++++++++++++------ net/mac80211/driver-ops.h | 7 ++++--- net/mac80211/ieee80211_i.h | 1 + net/mac80211/offchannel.c | 2 +- net/mac80211/trace.h | 11 +++++++---- 10 files changed, 58 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 323e4a33fca..c7cd2dffa5c 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1137,7 +1137,8 @@ done: static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *channel, - int duration) + int duration, + enum ieee80211_roc_type type) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index e8264e11b12..23460f4a4c7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1081,7 +1081,8 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, static int iwl_mvm_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *channel, - int duration) + int duration, + enum ieee80211_roc_type type) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct cfg80211_chan_def chandef; @@ -1092,8 +1093,8 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, return -EINVAL; } - IWL_DEBUG_MAC80211(mvm, "enter (%d, %d)\n", channel->hw_value, - duration); + IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, + duration, type); mutex_lock(&mvm->mutex); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index cffdf4fbf16..7490c4fc717 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1535,7 +1535,8 @@ static void hw_roc_done(struct work_struct *work) static int mac80211_hwsim_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *chan, - int duration) + int duration, + enum ieee80211_roc_type type) { struct mac80211_hwsim_data *hwsim = hw->priv; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 2c2ff3e1f84..d7e306333f6 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4956,7 +4956,8 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop) static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *chan, - int duration) + int duration, + enum ieee80211_roc_type type) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl1271 *wl = hw->priv; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f7eba1300d8..9b0d342c067 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2134,6 +2134,24 @@ enum ieee80211_rate_control_changed { IEEE80211_RC_NSS_CHANGED = BIT(3), }; +/** + * enum ieee80211_roc_type - remain on channel type + * + * With the support for multi channel contexts and multi channel operations, + * remain on channel operations might be limited/deferred/aborted by other + * flows/operations which have higher priority (and vise versa). + * Specifying the ROC type can be used by devices to prioritize the ROC + * operations compared to other operations/flows. + * + * @IEEE80211_ROC_TYPE_NORMAL: There are no special requirements for this ROC. + * @IEEE80211_ROC_TYPE_MGMT_TX: The remain on channel request is required + * for sending managment frames offchannel. + */ +enum ieee80211_roc_type { + IEEE80211_ROC_TYPE_NORMAL = 0, + IEEE80211_ROC_TYPE_MGMT_TX, +}; + /** * struct ieee80211_ops - callbacks from mac80211 to the driver * @@ -2687,7 +2705,8 @@ struct ieee80211_ops { int (*remain_on_channel)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel *chan, - int duration); + int duration, + enum ieee80211_roc_type type); int (*cancel_remain_on_channel)(struct ieee80211_hw *hw); int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx); void (*get_ringparam)(struct ieee80211_hw *hw, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c115f82c037..f9cbdc29946 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2410,7 +2410,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel, unsigned int duration, u64 *cookie, - struct sk_buff *txskb) + struct sk_buff *txskb, + enum ieee80211_roc_type type) { struct ieee80211_roc_work *roc, *tmp; bool queued = false; @@ -2429,6 +2430,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, roc->duration = duration; roc->req_duration = duration; roc->frame = txskb; + roc->type = type; roc->mgmt_tx_cookie = (unsigned long)txskb; roc->sdata = sdata; INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work); @@ -2459,7 +2461,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, if (!duration) duration = 10; - ret = drv_remain_on_channel(local, sdata, channel, duration); + ret = drv_remain_on_channel(local, sdata, channel, duration, type); if (ret) { kfree(roc); return ret; @@ -2478,10 +2480,13 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, * * If it hasn't started yet, just increase the duration * and add the new one to the list of dependents. + * If the type of the new ROC has higher priority, modify the + * type of the previous one to match that of the new one. */ if (!tmp->started) { list_add_tail(&roc->list, &tmp->dependents); tmp->duration = max(tmp->duration, roc->duration); + tmp->type = max(tmp->type, roc->type); queued = true; break; } @@ -2493,16 +2498,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, /* * In the offloaded ROC case, if it hasn't begun, add * this new one to the dependent list to be handled - * when the the master one begins. If it has begun, + * when the master one begins. If it has begun, * check that there's still a minimum time left and * if so, start this one, transmitting the frame, but - * add it to the list directly after this one with a + * add it to the list directly after this one with * a reduced time so we'll ask the driver to execute * it right after finishing the previous one, in the * hope that it'll also be executed right afterwards, * effectively extending the old one. * If there's no minimum time left, just add it to the * normal list. + * TODO: the ROC type is ignored here, assuming that it + * is better to immediately use the current ROC. */ if (!tmp->hw_begun) { list_add_tail(&roc->list, &tmp->dependents); @@ -2596,7 +2603,8 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy, mutex_lock(&local->mtx); ret = ieee80211_start_roc_work(local, sdata, chan, - duration, cookie, NULL); + duration, cookie, NULL, + IEEE80211_ROC_TYPE_NORMAL); mutex_unlock(&local->mtx); return ret; @@ -2829,7 +2837,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* This will handle all kinds of coalescing and immediate TX */ ret = ieee80211_start_roc_work(local, sdata, chan, - wait, cookie, skb); + wait, cookie, skb, + IEEE80211_ROC_TYPE_MGMT_TX); if (ret) kfree_skb(skb); out_unlock: diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index ee56d0779d8..832acea4a5c 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -787,15 +787,16 @@ static inline int drv_get_antenna(struct ieee80211_local *local, static inline int drv_remain_on_channel(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *chan, - unsigned int duration) + unsigned int duration, + enum ieee80211_roc_type type) { int ret; might_sleep(); - trace_drv_remain_on_channel(local, sdata, chan, duration); + trace_drv_remain_on_channel(local, sdata, chan, duration, type); ret = local->ops->remain_on_channel(&local->hw, &sdata->vif, - chan, duration); + chan, duration, type); trace_drv_return_int(local, ret); return ret; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8da53a06730..2518f0429b8 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -315,6 +315,7 @@ struct ieee80211_roc_work { u32 duration, req_duration; struct sk_buff *frame; u64 cookie, mgmt_tx_cookie; + enum ieee80211_roc_type type; }; /* flags used in struct ieee80211_if_managed.flags */ diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index cc79b4a2e82..db547fceaeb 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -277,7 +277,7 @@ void ieee80211_start_next_roc(struct ieee80211_local *local) duration = 10; ret = drv_remain_on_channel(local, roc->sdata, roc->chan, - duration); + duration, roc->type); roc->started = true; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 3d7cd2a0582..e7db2b804e0 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1042,15 +1042,17 @@ TRACE_EVENT(drv_remain_on_channel, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *chan, - unsigned int duration), + unsigned int duration, + enum ieee80211_roc_type type), - TP_ARGS(local, sdata, chan, duration), + TP_ARGS(local, sdata, chan, duration, type), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY __field(int, center_freq) __field(unsigned int, duration) + __field(u32, type) ), TP_fast_assign( @@ -1058,12 +1060,13 @@ TRACE_EVENT(drv_remain_on_channel, VIF_ASSIGN; __entry->center_freq = chan->center_freq; __entry->duration = duration; + __entry->type = type; ), TP_printk( - LOCAL_PR_FMT VIF_PR_FMT " freq:%dMHz duration:%dms", + LOCAL_PR_FMT VIF_PR_FMT " freq:%dMHz duration:%dms type=%d", LOCAL_PR_ARG, VIF_PR_ARG, - __entry->center_freq, __entry->duration + __entry->center_freq, __entry->duration, __entry->type ) ); -- cgit v1.2.3-70-g09d2 From 355199e02b831fd4f652c34d6c7673d973da1369 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 27 Feb 2013 17:14:27 +0200 Subject: cfg80211: Extend support for IEEE 802.11r Fast BSS Transition Add NL80211_CMD_UPDATE_FT_IES to support update of FT IEs to the WLAN driver and NL80211_CMD_FT_EVENT to send FT events from the WLAN driver. This will carry the target AP's MAC address along with the relevant Information Elements. This event is used to report received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE). These changes allow FT to be supported with drivers that use an internal SME instead of user space option (like FT implementation in wpa_supplicant with mac80211-based drivers). Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 41 ++++++++++++++++++++++++ include/uapi/linux/nl80211.h | 19 +++++++++++ net/wireless/nl80211.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 13 ++++++++ net/wireless/trace.h | 46 +++++++++++++++++++++++++++ 5 files changed, 195 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 73a523901c7..dfef0d5b5d3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1762,6 +1762,21 @@ struct cfg80211_gtk_rekey_data { u8 replay_ctr[NL80211_REPLAY_CTR_LEN]; }; +/** + * struct cfg80211_update_ft_ies_params - FT IE Information + * + * This structure provides information needed to update the fast transition IE + * + * @md: The Mobility Domain ID, 2 Octet value + * @ie: Fast Transition IEs + * @ie_len: Length of ft_ie in octets + */ +struct cfg80211_update_ft_ies_params { + u16 md; + const u8 *ie; + size_t ie_len; +}; + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -2208,6 +2223,8 @@ struct cfg80211_ops { int (*start_radar_detection)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_chan_def *chandef); + int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_update_ft_ies_params *ftie); }; /* @@ -4044,6 +4061,30 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate); */ void cfg80211_unregister_wdev(struct wireless_dev *wdev); +/** + * struct cfg80211_ft_event - FT Information Elements + * @ies: FT IEs + * @ies_len: length of the FT IE in bytes + * @target_ap: target AP's MAC address + * @ric_ies: RIC IE + * @ric_ies_len: length of the RIC IE in bytes + */ +struct cfg80211_ft_event_params { + const u8 *ies; + size_t ies_len; + const u8 *target_ap; + const u8 *ric_ies; + size_t ric_ies_len; +}; + +/** + * cfg80211_ft_event - notify userspace about FT IE and RIC IE + * @netdev: network device + * @ft_event: IE information + */ +void cfg80211_ft_event(struct net_device *netdev, + struct cfg80211_ft_event_params *ft_event); + /** * cfg80211_get_p2p_attr - find and copy a P2P attribute from IE buffer * @ies: the input IE buffer diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 2c3e8836003..2d0cff57ff8 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -629,6 +629,14 @@ * i.e. features for the nl80211 protocol rather than device features. * Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap. * + * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition + * Information Element to the WLAN driver + * + * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver + * to the supplicant. This will carry the target AP's MAC address along + * with the relevant Information Elements. This event is used to report + * received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE). + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -785,6 +793,9 @@ enum nl80211_commands { NL80211_CMD_GET_PROTOCOL_FEATURES, + NL80211_CMD_UPDATE_FT_IES, + NL80211_CMD_FT_EVENT, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1396,6 +1407,11 @@ enum nl80211_commands { * receiving the data for a single wiphy split across multiple * messages, given with wiphy dump message * + * @NL80211_ATTR_MDID: Mobility Domain Identifier + * + * @NL80211_ATTR_IE_RIC: Resource Information Container Information + * Element + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1688,6 +1704,9 @@ enum nl80211_attrs { NL80211_ATTR_DISABLE_VHT, NL80211_ATTR_VHT_CAPABILITY_MASK, + NL80211_ATTR_MDID, + NL80211_ATTR_IE_RIC, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a8bd453d22b..08de0c6035f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -375,6 +375,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_VHT_CAPABILITY_MASK] = { .len = NL80211_VHT_CAPABILITY_LEN, }, + [NL80211_ATTR_MDID] = { .type = NLA_U16 }, + [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, }; /* policy for the key attributes */ @@ -8160,6 +8163,27 @@ static int nl80211_get_protocol_features(struct sk_buff *skb, return -ENOBUFS; } +static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_update_ft_ies_params ft_params; + struct net_device *dev = info->user_ptr[1]; + + if (!rdev->ops->update_ft_ies) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_MDID] || + !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) + return -EINVAL; + + memset(&ft_params, 0, sizeof(ft_params)); + ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]); + ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + + return rdev_update_ft_ies(rdev, dev, &ft_params); +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -8841,6 +8865,14 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_get_protocol_features, .policy = nl80211_policy, }, + { + .cmd = NL80211_CMD_UPDATE_FT_IES, + .doit = nl80211_update_ft_ies, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -10542,6 +10574,50 @@ static struct notifier_block nl80211_netlink_notifier = { .notifier_call = nl80211_netlink_notify, }; +void cfg80211_ft_event(struct net_device *netdev, + struct cfg80211_ft_event_params *ft_event) +{ + struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct sk_buff *msg; + void *hdr; + int err; + + trace_cfg80211_ft_event(wiphy, netdev, ft_event); + + if (!ft_event->target_ap) + return; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap); + if (ft_event->ies) + nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies); + if (ft_event->ric_ies) + nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len, + ft_event->ric_ies); + + err = genlmsg_end(msg, hdr); + if (err < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, GFP_KERNEL); +} +EXPORT_SYMBOL(cfg80211_ft_event); + /* initialisation/exit functions */ int nl80211_init(void) diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 422d38291d6..8c8b26f574e 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -887,4 +887,17 @@ static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev, trace_rdev_return_int(&rdev->wiphy, ret); return ret; } + +static inline int rdev_update_ft_ies(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_update_ft_ies_params *ftie) +{ + int ret; + + trace_rdev_update_ft_ies(&rdev->wiphy, dev, ftie); + ret = rdev->ops->update_ft_ies(&rdev->wiphy, dev, ftie); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index b7a531380e1..ccadef2106a 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1785,6 +1785,26 @@ TRACE_EVENT(rdev_set_mac_acl, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy) ); +TRACE_EVENT(rdev_update_ft_ies, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_update_ft_ies_params *ftie), + TP_ARGS(wiphy, netdev, ftie), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u16, md) + __dynamic_array(u8, ie, ftie->ie_len) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->md = ftie->md; + memcpy(__get_dynamic_array(ie), ftie->ie, ftie->ie_len); + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", md: 0x%x", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->md) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ @@ -2413,6 +2433,32 @@ TRACE_EVENT(cfg80211_report_wowlan_wakeup, TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) ); +TRACE_EVENT(cfg80211_ft_event, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_ft_event_params *ft_event), + TP_ARGS(wiphy, netdev, ft_event), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __dynamic_array(u8, ies, ft_event->ies_len) + MAC_ENTRY(target_ap) + __dynamic_array(u8, ric_ies, ft_event->ric_ies_len) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + if (ft_event->ies) + memcpy(__get_dynamic_array(ies), ft_event->ies, + ft_event->ies_len); + MAC_ASSIGN(target_ap, ft_event->target_ap); + if (ft_event->ric_ies) + memcpy(__get_dynamic_array(ric_ies), ft_event->ric_ies, + ft_event->ric_ies_len); + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", target_ap: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap)) +); + #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH -- cgit v1.2.3-70-g09d2 From a87121051ce80831a302c67286119013104f7a5a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 23 Feb 2013 00:22:44 +0100 Subject: mac80211: remove IEEE80211_KEY_FLAG_WMM_STA There's no driver using this flag, so it seems that all drivers support HW crypto with WMM or don't support it at all. Remove the flag and code setting it. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 3 --- net/mac80211/key.c | 26 -------------------------- 2 files changed, 29 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9b0d342c067..421c3ac8c52 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1101,8 +1101,6 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) * These flags are used for communication about keys between the driver * and mac80211, with the @flags parameter of &struct ieee80211_key_conf. * - * @IEEE80211_KEY_FLAG_WMM_STA: Set by mac80211, this flag indicates - * that the STA this key will be used with could be using QoS. * @IEEE80211_KEY_FLAG_GENERATE_IV: This flag should be set by the * driver to indicate that it requires IV generation for this * particular key. @@ -1127,7 +1125,6 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) * %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW. */ enum ieee80211_key_flags { - IEEE80211_KEY_FLAG_WMM_STA = 1<<0, IEEE80211_KEY_FLAG_GENERATE_IV = 1<<1, IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2, IEEE80211_KEY_FLAG_PAIRWISE = 1<<3, diff --git a/net/mac80211/key.c b/net/mac80211/key.c index df81b178c59..6eb4888a70e 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -440,32 +440,6 @@ int ieee80211_key_link(struct ieee80211_key *key, key->sdata = sdata; key->sta = sta; - if (sta) { - /* - * some hardware cannot handle TKIP with QoS, so - * we indicate whether QoS could be in use. - */ - if (test_sta_flag(sta, WLAN_STA_WME)) - key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; - } else { - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - struct sta_info *ap; - - /* - * We're getting a sta pointer in, so must be under - * appropriate locking for sta_info_get(). - */ - - /* same here, the AP could be using QoS */ - ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid); - if (ap) { - if (test_sta_flag(ap, WLAN_STA_WME)) - key->conf.flags |= - IEEE80211_KEY_FLAG_WMM_STA; - } - } - } - mutex_lock(&sdata->local->key_mtx); if (sta && pairwise) -- cgit v1.2.3-70-g09d2 From 55d942f4246c79a8f3f17f92c224e641c5c26125 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 1 Mar 2013 13:07:48 +0100 Subject: mac80211: restrict peer's VHT capabilities to own Implement restricting peer VHT capabilities to the device's own capabilities. This is useful when a single driver supports more than one device and the devices have different capabilities (often they will differ in the number of spatial streams), but in particular is also necessary for VHT capability overrides to work correctly -- otherwise it'd be possible to e.g. advertise, due to overrides, that TX-STBC is not supported, but then still use it to TX to the AP because it supports RX-STBC. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 3 +- include/net/mac80211.h | 5 +- net/mac80211/vht.c | 114 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 117 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 35c1f96d936..4cf0c9e4dd9 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1333,10 +1333,11 @@ struct ieee80211_vht_operation { #define IEEE80211_VHT_CAP_RXSTBC_2 0x00000200 #define IEEE80211_VHT_CAP_RXSTBC_3 0x00000300 #define IEEE80211_VHT_CAP_RXSTBC_4 0x00000400 +#define IEEE80211_VHT_CAP_RXSTBC_MASK 0x00000700 #define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800 #define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000 #define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX 0x00006000 -#define IEEE80211_VHT_CAP_SOUNDING_DIMENTION_MAX 0x00030000 +#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX 0x00030000 #define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000 #define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000 #define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000 diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 421c3ac8c52..cdd7cea1fd4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1228,9 +1228,8 @@ enum ieee80211_sta_rx_bandwidth { * @addr: MAC address * @aid: AID we assigned to the station if we're an AP * @supp_rates: Bitmap of supported rates (per band) - * @ht_cap: HT capabilities of this STA; restricted to our own TX capabilities - * @vht_cap: VHT capabilities of this STA; Not restricting any capabilities - * of remote STA. Taking as is. + * @ht_cap: HT capabilities of this STA; restricted to our own capabilities + * @vht_cap: VHT capabilities of this STA; restricted to our own capabilities * @wme: indicates whether the STA supports WME. Only valid during AP-mode. * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *), size is determined in hw information. diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index cacc1c74556..171344d4eb7 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -118,6 +118,8 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct sta_info *sta) { struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; + struct ieee80211_sta_vht_cap own_cap; + u32 cap_info, i; memset(vht_cap, 0, sizeof(*vht_cap)); @@ -133,12 +135,122 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, vht_cap->vht_supported = true; - vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info); + own_cap = sband->vht_cap; + /* + * If user has specified capability overrides, take care + * of that if the station we're setting up is the AP that + * we advertised a restricted capability set to. Override + * our own capabilities and then use those below. + */ + if (sdata->vif.type == NL80211_IFTYPE_STATION && + !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + ieee80211_apply_vhtcap_overrides(sdata, &own_cap); + + /* take some capabilities as-is */ + cap_info = le32_to_cpu(vht_cap_ie->vht_cap_info); + vht_cap->cap = cap_info; + vht_cap->cap &= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 | + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 | + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_RXLDPC | + IEEE80211_VHT_CAP_VHT_TXOP_PS | + IEEE80211_VHT_CAP_HTC_VHT | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | + IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB | + IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB | + IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | + IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; + + /* and some based on our own capabilities */ + switch (own_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { + case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: + vht_cap->cap |= cap_info & + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + break; + case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: + vht_cap->cap |= cap_info & + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; + break; + default: + /* nothing */ + break; + } + + /* symmetric capabilities */ + vht_cap->cap |= cap_info & own_cap.cap & + (IEEE80211_VHT_CAP_SHORT_GI_80 | + IEEE80211_VHT_CAP_SHORT_GI_160); + + /* remaining ones */ + if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) { + vht_cap->cap |= cap_info & + (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX | + IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX); + } + + if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) + vht_cap->cap |= cap_info & + IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; + + if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) + vht_cap->cap |= cap_info & + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + + if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) + vht_cap->cap |= cap_info & + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; + + if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC) + vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK; + + if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) + vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC; /* Copy peer MCS info, the driver might need them. */ memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, sizeof(struct ieee80211_vht_mcs_info)); + /* but also restrict MCSes */ + for (i = 0; i < 8; i++) { + u16 own_rx, own_tx, peer_rx, peer_tx; + + own_rx = le16_to_cpu(own_cap.vht_mcs.rx_mcs_map); + own_rx = (own_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + + own_tx = le16_to_cpu(own_cap.vht_mcs.tx_mcs_map); + own_tx = (own_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + + peer_rx = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); + peer_rx = (peer_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + + peer_tx = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); + peer_tx = (peer_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; + + if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { + if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED) + peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED; + else if (own_rx < peer_tx) + peer_tx = own_rx; + } + + if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { + if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED) + peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED; + else if (own_tx < peer_rx) + peer_rx = own_tx; + } + + vht_cap->vht_mcs.rx_mcs_map &= + ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); + vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2); + + vht_cap->vht_mcs.tx_mcs_map &= + ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); + vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2); + } + + /* finally set up the bandwidth */ switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: -- cgit v1.2.3-70-g09d2 From bb2798d45fc0575f5d08c0bb7baf4d5d5e8cc0c3 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 4 Mar 2013 13:06:10 -0800 Subject: nl80211: explicit userspace MPM Secure mesh had the implicit requirement that the Mesh Peering Management entity be in userspace. However userspace might want to implement an open MPM as well, so specify a mesh setup parameter to indicate this. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 25 ++++++++++++++++++------- net/wireless/mesh.c | 1 + net/wireless/nl80211.c | 8 ++++++++ 4 files changed, 29 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index dfef0d5b5d3..69b2b2631b9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1151,6 +1151,7 @@ struct mesh_config { * @ie_len: length of vendor information elements * @is_authenticated: this mesh requires authentication * @is_secure: this mesh uses security + * @user_mpm: userspace handles all MPM functions * @dtim_period: DTIM period to use * @beacon_interval: beacon interval to use * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a] @@ -1168,6 +1169,7 @@ struct mesh_setup { u8 ie_len; bool is_authenticated; bool is_secure; + bool user_mpm; u8 dtim_period; u16 beacon_interval; int mcast_rate[IEEE80211_NUM_BANDS]; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 2d0cff57ff8..8134c6a96f5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -513,9 +513,11 @@ * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a * beacon or probe response from a compatible mesh peer. This is only * sent while no station information (sta_info) exists for the new peer - * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set. On - * reception of this notification, userspace may decide to create a new - * station (@NL80211_CMD_NEW_STATION). To stop this notification from + * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH, + * @NL80211_MESH_SETUP_USERSPACE_AMPE, or + * @NL80211_MESH_SETUP_USERSPACE_MPM is set. On reception of this + * notification, userspace may decide to create a new station + * (@NL80211_CMD_NEW_STATION). To stop this notification from * reoccurring, the userspace authentication daemon may want to create the * new station with the AUTHENTICATED flag unset and maybe change it later * depending on the authentication result. @@ -1199,10 +1201,10 @@ enum nl80211_commands { * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver * allows auth frames in a mesh to be passed to userspace for processing via * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. - * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as - * defined in &enum nl80211_plink_state. Used when userspace is - * driving the peer link management state machine. - * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. + * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in + * &enum nl80211_plink_state. Used when userspace is driving the peer link + * management state machine. @NL80211_MESH_SETUP_USERSPACE_AMPE or + * @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled. * * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy * capabilities, the supported WoWLAN triggers @@ -2612,6 +2614,9 @@ enum nl80211_meshconf_params { * vendor specific synchronization method or disable it to use the default * neighbor offset synchronization * + * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will + * implement an MPM which handles peer allocation and state. + * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number * * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use @@ -2624,6 +2629,7 @@ enum nl80211_mesh_setup_params { NL80211_MESH_SETUP_USERSPACE_AUTH, NL80211_MESH_SETUP_USERSPACE_AMPE, NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, + NL80211_MESH_SETUP_USERSPACE_MPM, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, @@ -3526,6 +3532,10 @@ enum nl80211_ap_sme_features { * stations the authenticated/associated bits have to be set in the mask. * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits * (HT40, VHT 80/160 MHz) if this flag is set + * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh + * Peering Management entity which may be implemented by registering for + * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is + * still generated by the driver. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3544,6 +3554,7 @@ enum nl80211_feature_flags { /* bit 13 is reserved */ NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14, NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15, + NL80211_FEATURE_USERSPACE_MPM = 1 << 16, }; /** diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 9688b249a80..0bb93f3061a 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -85,6 +85,7 @@ const struct mesh_setup default_mesh_setup = { .ie = NULL, .ie_len = 0, .is_secure = false, + .user_mpm = false, .beacon_interval = MESH_DEFAULT_BEACON_INTERVAL, .dtim_period = MESH_DEFAULT_DTIM_PERIOD, }; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7469020175d..bdf39836d9d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4618,6 +4618,7 @@ static const struct nla_policy [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, + [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG }, [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, @@ -4756,6 +4757,7 @@ do { \ static int nl80211_parse_mesh_setup(struct genl_info *info, struct mesh_setup *setup) { + struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1]; if (!info->attrs[NL80211_ATTR_MESH_SETUP]) @@ -4792,8 +4794,14 @@ static int nl80211_parse_mesh_setup(struct genl_info *info, setup->ie = nla_data(ieattr); setup->ie_len = nla_len(ieattr); } + if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] && + !(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM)) + return -EINVAL; + setup->user_mpm = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_MPM]); setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]); + if (setup->is_secure) + setup->user_mpm = true; return 0; } -- cgit v1.2.3-70-g09d2 From eef941e6d6be8bce72b5c2963b69f948be4df7a7 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 4 Mar 2013 13:06:11 -0800 Subject: cfg80211: rename mesh station types The mesh station types used to refer to whether the station was secure or nonsecure. Really the salient information is whether it is managed by the kernel or userspace Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 8 ++++---- net/mac80211/cfg.c | 4 ++-- net/wireless/nl80211.c | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 69b2b2631b9..bdba9b61906 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -690,8 +690,8 @@ struct station_parameters { * supported/used) * @CFG80211_STA_TDLS_PEER_ACTIVE: TDLS peer on managed interface (active * entry that is operating, has been marked authorized by userspace) - * @CFG80211_STA_MESH_PEER_NONSEC: peer on mesh interface (non-secured) - * @CFG80211_STA_MESH_PEER_SECURE: peer on mesh interface (secured) + * @CFG80211_STA_MESH_PEER_KERNEL: peer on mesh interface (kernel managed) + * @CFG80211_STA_MESH_PEER_USER: peer on mesh interface (user managed) */ enum cfg80211_station_type { CFG80211_STA_AP_CLIENT, @@ -700,8 +700,8 @@ enum cfg80211_station_type { CFG80211_STA_IBSS, CFG80211_STA_TDLS_PEER_SETUP, CFG80211_STA_TDLS_PEER_ACTIVE, - CFG80211_STA_MESH_PEER_NONSEC, - CFG80211_STA_MESH_PEER_SECURE, + CFG80211_STA_MESH_PEER_KERNEL, + CFG80211_STA_MESH_PEER_USER, }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9d708f9e246..6ac89e5c296 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1436,9 +1436,9 @@ static int ieee80211_change_station(struct wiphy *wiphy, switch (sdata->vif.type) { case NL80211_IFTYPE_MESH_POINT: if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED) - statype = CFG80211_STA_MESH_PEER_SECURE; + statype = CFG80211_STA_MESH_PEER_USER; else - statype = CFG80211_STA_MESH_PEER_NONSEC; + statype = CFG80211_STA_MESH_PEER_KERNEL; break; case NL80211_IFTYPE_ADHOC: statype = CFG80211_STA_IBSS; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bdf39836d9d..946b2e7acdf 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3617,8 +3617,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy, BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7); switch (statype) { - case CFG80211_STA_MESH_PEER_NONSEC: - case CFG80211_STA_MESH_PEER_SECURE: + case CFG80211_STA_MESH_PEER_KERNEL: + case CFG80211_STA_MESH_PEER_USER: /* * No ignoring the TDLS flag here -- the userspace mesh * code doesn't have the bug of including TDLS in the @@ -3720,11 +3720,11 @@ int cfg80211_check_station_change(struct wiphy *wiphy, case CFG80211_STA_TDLS_PEER_ACTIVE: /* reject any changes */ return -EINVAL; - case CFG80211_STA_MESH_PEER_NONSEC: + case CFG80211_STA_MESH_PEER_KERNEL: if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) return -EINVAL; break; - case CFG80211_STA_MESH_PEER_SECURE: + case CFG80211_STA_MESH_PEER_USER: if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION) return -EINVAL; break; -- cgit v1.2.3-70-g09d2 From d37bb18ae3a3fa7ef239aad533742a8b07eae15f Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 4 Mar 2013 13:06:13 -0800 Subject: nl80211: user_mpm overrides auto_open_plinks If the user requested a userspace MPM, automatically disable auto_open_plinks to fully disable the kernel MPM. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 6 ++++-- net/wireless/nl80211.c | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 8134c6a96f5..79da8710448 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2467,8 +2467,10 @@ enum nl80211_mesh_power_mode { * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh * point. * - * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically - * open peer links when we detect compatible mesh peers. + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open + * peer links when we detect compatible mesh peers. Disabled if + * @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are + * set. * * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames * containing a PREQ that an MP can send to a particular destination (path diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 946b2e7acdf..f924d45af1b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7449,6 +7449,9 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) return err; } + if (setup.user_mpm) + cfg.auto_open_plinks = false; + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { err = nl80211_parse_chandef(rdev, info, &setup.chandef); if (err) -- cgit v1.2.3-70-g09d2 From acbba0d0f88e2577b9d92b61b136d13f65831a52 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 6 Mar 2013 01:31:12 +0000 Subject: team: introduce two default team_modeop functions and use them in modes No need to duplicate code for this. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 19 ++++++++++++++++--- drivers/net/team/team_mode_broadcast.c | 14 ++------------ drivers/net/team/team_mode_roundrobin.c | 14 ++------------ include/linux/if_team.h | 5 ++++- 4 files changed, 24 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 05c5efe8459..ece70a4abbb 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -73,11 +73,24 @@ static int team_port_set_orig_dev_addr(struct team_port *port) return __set_port_dev_addr(port->dev, port->orig.dev_addr); } -int team_port_set_team_dev_addr(struct team_port *port) +static int team_port_set_team_dev_addr(struct team *team, + struct team_port *port) +{ + return __set_port_dev_addr(port->dev, team->dev->dev_addr); +} + +int team_modeop_port_enter(struct team *team, struct team_port *port) +{ + return team_port_set_team_dev_addr(team, port); +} +EXPORT_SYMBOL(team_modeop_port_enter); + +void team_modeop_port_change_dev_addr(struct team *team, + struct team_port *port) { - return __set_port_dev_addr(port->dev, port->team->dev->dev_addr); + team_port_set_team_dev_addr(team, port); } -EXPORT_SYMBOL(team_port_set_team_dev_addr); +EXPORT_SYMBOL(team_modeop_port_change_dev_addr); static void team_refresh_port_linkup(struct team_port *port) { diff --git a/drivers/net/team/team_mode_broadcast.c b/drivers/net/team/team_mode_broadcast.c index c5db428e73f..c366cd299c0 100644 --- a/drivers/net/team/team_mode_broadcast.c +++ b/drivers/net/team/team_mode_broadcast.c @@ -46,20 +46,10 @@ static bool bc_transmit(struct team *team, struct sk_buff *skb) return sum_ret; } -static int bc_port_enter(struct team *team, struct team_port *port) -{ - return team_port_set_team_dev_addr(port); -} - -static void bc_port_change_dev_addr(struct team *team, struct team_port *port) -{ - team_port_set_team_dev_addr(port); -} - static const struct team_mode_ops bc_mode_ops = { .transmit = bc_transmit, - .port_enter = bc_port_enter, - .port_change_dev_addr = bc_port_change_dev_addr, + .port_enter = team_modeop_port_enter, + .port_change_dev_addr = team_modeop_port_change_dev_addr, }; static const struct team_mode bc_mode = { diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index 105135aa8f0..ed63a6bc66c 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -64,20 +64,10 @@ drop: return false; } -static int rr_port_enter(struct team *team, struct team_port *port) -{ - return team_port_set_team_dev_addr(port); -} - -static void rr_port_change_dev_addr(struct team *team, struct team_port *port) -{ - team_port_set_team_dev_addr(port); -} - static const struct team_mode_ops rr_mode_ops = { .transmit = rr_transmit, - .port_enter = rr_port_enter, - .port_change_dev_addr = rr_port_change_dev_addr, + .port_enter = team_modeop_port_enter, + .port_change_dev_addr = team_modeop_port_change_dev_addr, }; static const struct team_mode rr_mode = { diff --git a/include/linux/if_team.h b/include/linux/if_team.h index cfd21e3d550..3283def7448 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -112,6 +112,10 @@ struct team_mode_ops { void (*port_disabled)(struct team *team, struct team_port *port); }; +extern int team_modeop_port_enter(struct team *team, struct team_port *port); +extern void team_modeop_port_change_dev_addr(struct team *team, + struct team_port *port); + enum team_option_type { TEAM_OPTION_TYPE_U32, TEAM_OPTION_TYPE_STRING, @@ -236,7 +240,6 @@ static inline struct team_port *team_get_port_by_index_rcu(struct team *team, return NULL; } -extern int team_port_set_team_dev_addr(struct team_port *port); extern int team_options_register(struct team *team, const struct team_option *option, size_t option_count); -- cgit v1.2.3-70-g09d2 From 753f993911b32e479b4fab5d228dc07c11d1e7e7 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 6 Mar 2013 01:31:13 +0000 Subject: team: introduce random mode As suggested by Eric Dumazet, allow user to select mode which chooses TX port randomly. Functionality should be more of less similar to round-robin mode with even lower overhead. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/Kconfig | 12 ++++++ drivers/net/team/Makefile | 1 + drivers/net/team/team_mode_random.c | 71 +++++++++++++++++++++++++++++++++ drivers/net/team/team_mode_roundrobin.c | 22 +--------- include/linux/if_team.h | 20 ++++++++++ 5 files changed, 105 insertions(+), 21 deletions(-) create mode 100644 drivers/net/team/team_mode_random.c (limited to 'include') diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig index c3011af68e9..c853d84fd99 100644 --- a/drivers/net/team/Kconfig +++ b/drivers/net/team/Kconfig @@ -37,6 +37,18 @@ config NET_TEAM_MODE_ROUNDROBIN To compile this team mode as a module, choose M here: the module will be called team_mode_roundrobin. +config NET_TEAM_MODE_RANDOM + tristate "Random mode support" + depends on NET_TEAM + ---help--- + Basic mode where port used for transmitting packets is selected + randomly. + + All added ports are setup to have team's device address. + + To compile this team mode as a module, choose M here: the module + will be called team_mode_random. + config NET_TEAM_MODE_ACTIVEBACKUP tristate "Active-backup mode support" depends on NET_TEAM diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile index 975763014e5..c57e8588975 100644 --- a/drivers/net/team/Makefile +++ b/drivers/net/team/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_NET_TEAM) += team.o obj-$(CONFIG_NET_TEAM_MODE_BROADCAST) += team_mode_broadcast.o obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o +obj-$(CONFIG_NET_TEAM_MODE_RANDOM) += team_mode_random.o obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o obj-$(CONFIG_NET_TEAM_MODE_LOADBALANCE) += team_mode_loadbalance.o diff --git a/drivers/net/team/team_mode_random.c b/drivers/net/team/team_mode_random.c new file mode 100644 index 00000000000..9eabfaa22f3 --- /dev/null +++ b/drivers/net/team/team_mode_random.c @@ -0,0 +1,71 @@ +/* + * drivers/net/team/team_mode_random.c - Random mode for team + * Copyright (c) 2013 Jiri Pirko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +static u32 random_N(unsigned int N) +{ + return reciprocal_divide(random32(), N); +} + +static bool rnd_transmit(struct team *team, struct sk_buff *skb) +{ + struct team_port *port; + int port_index; + + port_index = random_N(team->en_port_count); + port = team_get_port_by_index_rcu(team, port_index); + port = team_get_first_port_txable_rcu(team, port); + if (unlikely(!port)) + goto drop; + if (team_dev_queue_xmit(team, port, skb)) + return false; + return true; + +drop: + dev_kfree_skb_any(skb); + return false; +} + +static const struct team_mode_ops rnd_mode_ops = { + .transmit = rnd_transmit, + .port_enter = team_modeop_port_enter, + .port_change_dev_addr = team_modeop_port_change_dev_addr, +}; + +static const struct team_mode rnd_mode = { + .kind = "random", + .owner = THIS_MODULE, + .ops = &rnd_mode_ops, +}; + +static int __init rnd_init_module(void) +{ + return team_mode_register(&rnd_mode); +} + +static void __exit rnd_cleanup_module(void) +{ + team_mode_unregister(&rnd_mode); +} + +module_init(rnd_init_module); +module_exit(rnd_cleanup_module); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jiri Pirko "); +MODULE_DESCRIPTION("Random mode for team"); +MODULE_ALIAS("team-mode-random"); diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index ed63a6bc66c..d268e4de781 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -25,26 +25,6 @@ static struct rr_priv *rr_priv(struct team *team) return (struct rr_priv *) &team->mode_priv; } -static struct team_port *__get_first_port_up(struct team *team, - struct team_port *port) -{ - struct team_port *cur; - - if (team_port_txable(port)) - return port; - cur = port; - list_for_each_entry_continue_rcu(cur, &team->port_list, list) - if (team_port_txable(port)) - return cur; - list_for_each_entry_rcu(cur, &team->port_list, list) { - if (cur == port) - break; - if (team_port_txable(port)) - return cur; - } - return NULL; -} - static bool rr_transmit(struct team *team, struct sk_buff *skb) { struct team_port *port; @@ -52,7 +32,7 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb) port_index = rr_priv(team)->sent_packets++ % team->en_port_count; port = team_get_port_by_index_rcu(team, port_index); - port = __get_first_port_up(team, port); + port = team_get_first_port_txable_rcu(team, port); if (unlikely(!port)) goto drop; if (team_dev_queue_xmit(team, port, skb)) diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 3283def7448..4474557904f 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -240,6 +240,26 @@ static inline struct team_port *team_get_port_by_index_rcu(struct team *team, return NULL; } +static inline struct team_port * +team_get_first_port_txable_rcu(struct team *team, struct team_port *port) +{ + struct team_port *cur; + + if (likely(team_port_txable(port))) + return port; + cur = port; + list_for_each_entry_continue_rcu(cur, &team->port_list, list) + if (team_port_txable(port)) + return cur; + list_for_each_entry_rcu(cur, &team->port_list, list) { + if (cur == port) + break; + if (team_port_txable(port)) + return cur; + } + return NULL; +} + extern int team_options_register(struct team *team, const struct team_option *option, size_t option_count); -- cgit v1.2.3-70-g09d2 From 6906f4ed6f85b2d72fd944e15da6a905fdde8b40 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 6 Mar 2013 06:49:21 +0000 Subject: htb: add HTB_DIRECT_QLEN attribute HTB uses an internal pfifo queue, which limit is not reported to userland tools (tc), and value inherited from device tx_queue_len at setup time. Introduce TCA_HTB_DIRECT_QLEN attribute to allow finer control. Remove two obsolete pr_err() calls as well. Signed-off-by: Eric Dumazet Cc: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/uapi/linux/pkt_sched.h | 1 + net/sched/sch_htb.c | 31 ++++++++++++++++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index 32aef0a439e..dbd71b0c7d8 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -348,6 +348,7 @@ enum { TCA_HTB_INIT, TCA_HTB_CTAB, TCA_HTB_RTAB, + TCA_HTB_DIRECT_QLEN, __TCA_HTB_MAX, }; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 571f1d211f4..79b1876b6cd 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -981,6 +981,7 @@ static const struct nla_policy htb_policy[TCA_HTB_MAX + 1] = { [TCA_HTB_INIT] = { .len = sizeof(struct tc_htb_glob) }, [TCA_HTB_CTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE }, [TCA_HTB_RTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE }, + [TCA_HTB_DIRECT_QLEN] = { .type = NLA_U32 }, }; static void htb_work_func(struct work_struct *work) @@ -994,7 +995,7 @@ static void htb_work_func(struct work_struct *work) static int htb_init(struct Qdisc *sch, struct nlattr *opt) { struct htb_sched *q = qdisc_priv(sch); - struct nlattr *tb[TCA_HTB_INIT + 1]; + struct nlattr *tb[TCA_HTB_MAX + 1]; struct tc_htb_glob *gopt; int err; int i; @@ -1002,20 +1003,16 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt) if (!opt) return -EINVAL; - err = nla_parse_nested(tb, TCA_HTB_INIT, opt, htb_policy); + err = nla_parse_nested(tb, TCA_HTB_MAX, opt, htb_policy); if (err < 0) return err; - if (tb[TCA_HTB_INIT] == NULL) { - pr_err("HTB: hey probably you have bad tc tool ?\n"); + if (!tb[TCA_HTB_INIT]) return -EINVAL; - } + gopt = nla_data(tb[TCA_HTB_INIT]); - if (gopt->version != HTB_VER >> 16) { - pr_err("HTB: need tc/htb version %d (minor is %d), you have %d\n", - HTB_VER >> 16, HTB_VER & 0xffff, gopt->version); + if (gopt->version != HTB_VER >> 16) return -EINVAL; - } err = qdisc_class_hash_init(&q->clhash); if (err < 0) @@ -1027,10 +1024,13 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt) INIT_WORK(&q->work, htb_work_func); skb_queue_head_init(&q->direct_queue); - q->direct_qlen = qdisc_dev(sch)->tx_queue_len; - if (q->direct_qlen < 2) /* some devices have zero tx_queue_len */ - q->direct_qlen = 2; - + if (tb[TCA_HTB_DIRECT_QLEN]) + q->direct_qlen = nla_get_u32(tb[TCA_HTB_DIRECT_QLEN]); + else { + q->direct_qlen = qdisc_dev(sch)->tx_queue_len; + if (q->direct_qlen < 2) /* some devices have zero tx_queue_len */ + q->direct_qlen = 2; + } if ((q->rate2quantum = gopt->rate2quantum) < 1) q->rate2quantum = 1; q->defcls = gopt->defcls; @@ -1056,7 +1056,8 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) nest = nla_nest_start(skb, TCA_OPTIONS); if (nest == NULL) goto nla_put_failure; - if (nla_put(skb, TCA_HTB_INIT, sizeof(gopt), &gopt)) + if (nla_put(skb, TCA_HTB_INIT, sizeof(gopt), &gopt) || + nla_put_u32(skb, TCA_HTB_DIRECT_QLEN, q->direct_qlen)) goto nla_put_failure; nla_nest_end(skb, nest); @@ -1311,7 +1312,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl = (struct htb_class *)*arg, *parent; struct nlattr *opt = tca[TCA_OPTIONS]; - struct nlattr *tb[__TCA_HTB_MAX]; + struct nlattr *tb[TCA_HTB_MAX + 1]; struct tc_htb_opt *hopt; /* extract all subattrs from opt attr */ -- cgit v1.2.3-70-g09d2 From 8524982847ff00b66ffb89314c342c51f4138ee7 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Mon, 18 Feb 2013 21:47:45 +0100 Subject: ssb: fix unaligned access to mac address The mac address should be aligned to u16 to prevent an unaligned access in drivers/ssb/pci.c where it is casted to __be16. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- include/linux/ssb/ssb.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 22958d68ecf..8b1322296fe 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -26,9 +26,9 @@ struct ssb_sprom_core_pwr_info { struct ssb_sprom { u8 revision; - u8 il0mac[6]; /* MAC address for 802.11b/g */ - u8 et0mac[6]; /* MAC address for Ethernet */ - u8 et1mac[6]; /* MAC address for 802.11a */ + u8 il0mac[6] __aligned(sizeof(u16)); /* MAC address for 802.11b/g */ + u8 et0mac[6] __aligned(sizeof(u16)); /* MAC address for Ethernet */ + u8 et1mac[6] __aligned(sizeof(u16)); /* MAC address for 802.11a */ u8 et0phyaddr; /* MII address for enet0 */ u8 et1phyaddr; /* MII address for enet1 */ u8 et0mdcport; /* MDIO for enet0 */ -- cgit v1.2.3-70-g09d2 From f04a9d8adf766c480353c0f2427e641251c9b059 Mon Sep 17 00:00:00 2001 From: Rajkumar Kasirajan Date: Wed, 30 May 2012 16:32:37 +0530 Subject: mfd: ab8500-sysctrl: Update correct turn on status In L9540, turn_on_status register is not updated correctly if the device is rebooted with AC/USB charger connected. Due to this, the device boots android instead of entering into charge only mode. Read the AC/USB status register to detect the charger presence and update the turn on status manually. Signed-off-by: Rajkumar Kasirajan Signed-off-by: Per Forlin Signed-off-by: Lee Jones Reviewed-by: Rupesh KUMAR Reviewed-by: Philippe LANGLAIS Tested-by: Rupesh KUMAR Tested-by: Philippe LANGLAIS Acked-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 39 +++++++++++++++++++++++++++++++++++++++ drivers/mfd/ab8500-sysctrl.c | 2 +- include/linux/mfd/abx500/ab8500.h | 2 ++ 3 files changed, 42 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 7c84ced2e01..50e6f1e2972 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -111,6 +111,13 @@ #define AB8500_TURN_ON_STATUS 0x00 +#define AB8500_CH_USBCH_STAT1_REG 0x02 +#define VBUS_DET_DBNC100 0x02 +#define VBUS_DET_DBNC1 0x01 + +static DEFINE_SPINLOCK(on_stat_lock); +static u8 turn_on_stat_mask = 0xFF; +static u8 turn_on_stat_set; static bool no_bm; /* No battery management */ module_param(no_bm, bool, S_IRUGO); @@ -1171,6 +1178,15 @@ static ssize_t show_switch_off_status(struct device *dev, return sprintf(buf, "%#x\n", value); } +/* use mask and set to override the register turn_on_stat value */ +void ab8500_override_turn_on_stat(u8 mask, u8 set) +{ + spin_lock(&on_stat_lock); + turn_on_stat_mask = mask; + turn_on_stat_set = set; + spin_unlock(&on_stat_lock); +} + /* * ab8500 has turned on due to (TURN_ON_STATUS): * 0x01 PORnVbat @@ -1194,6 +1210,20 @@ static ssize_t show_turn_on_status(struct device *dev, AB8500_TURN_ON_STATUS, &value); if (ret < 0) return ret; + + /* + * In L9540, turn_on_status register is not updated correctly if + * the device is rebooted with AC/USB charger connected. Due to + * this, the device boots android instead of entering into charge + * only mode. Read the AC/USB status register to detect the charger + * presence and update the turn on status manually. + */ + if (is_ab9540(ab8500)) { + spin_lock(&on_stat_lock); + value = (value & turn_on_stat_mask) | turn_on_stat_set; + spin_unlock(&on_stat_lock); + } + return sprintf(buf, "%#x\n", value); } @@ -1399,6 +1429,15 @@ static int ab8500_probe(struct platform_device *pdev) if (plat && plat->init) plat->init(ab8500); + if (is_ab9540(ab8500)) { + ret = get_register_interruptible(ab8500, AB8500_CHARGER, + AB8500_CH_USBCH_STAT1_REG, &value); + if (ret < 0) + return ret; + if ((value & VBUS_DET_DBNC1) && (value & VBUS_DET_DBNC100)) + ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON, + AB8500_VBUS_DET); + } /* Clear and mask all interrupts */ for (i = 0; i < ab8500->mask_size; i++) { diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 108fd86552f..7c773797d26 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -21,7 +21,7 @@ void ab8500_power_off(void) { sigset_t old; sigset_t all; - static char *pss[] = {"ab8500_ac", "ab8500_usb"}; + static char *pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"}; int i; bool charger_present = false; union power_supply_propval val; diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 9db0bda446a..fdd8be64fee 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -512,6 +512,8 @@ static inline int is_ab9540_2p0_or_earlier(struct ab8500 *ab) return (is_ab9540(ab) && (ab->chip_id < AB8500_CUT2P0)); } +void ab8500_override_turn_on_stat(u8 mask, u8 set); + #ifdef CONFIG_AB8500_DEBUG void ab8500_dump_all_banks(struct device *dev); void ab8500_debug_register_interrupt(int line); -- cgit v1.2.3-70-g09d2 From 734823462590335cbf5c6a1fa5cae84a881dcb43 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 26 Feb 2013 10:06:55 +0000 Subject: mfd: ab8500-gpadc: Add gpadc hw conversion Add the support of gpacd hw conversion and make the number of sample configurable. Signed-off-by: M'boumba Cedric Madianga Signed-off-by: Lee Jones Reviewed-by: Mattias WALLIN Tested-by: Michel JAOUEN Acked-by: Samuel Ortiz --- drivers/mfd/ab8500-debugfs.c | 292 +++++++++++++++++++++++++++++--- drivers/mfd/ab8500-gpadc.c | 282 ++++++++++++++++++++++-------- include/linux/mfd/abx500/ab8500-gpadc.h | 30 +++- 3 files changed, 499 insertions(+), 105 deletions(-) (limited to 'include') diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 45fe3c50eb0..59ecd8c680e 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -101,6 +101,11 @@ static int num_irqs; static struct device_attribute **dev_attr; static char **event_name; +static u8 avg_sample = SAMPLE_16; +static u8 trig_edge = RISING_EDGE; +static u8 conv_type = ADC_SW; +static u8 trig_timer; + /** * struct ab8500_reg_range * @first: the first address of the range @@ -808,9 +813,10 @@ static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL); + bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL, + avg_sample, trig_edge, trig_timer, conv_type); bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, - BAT_CTRL, bat_ctrl_raw); + BAT_CTRL, bat_ctrl_raw); return seq_printf(s, "%d,0x%X\n", bat_ctrl_convert, bat_ctrl_raw); @@ -836,9 +842,10 @@ static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL); + btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL, + avg_sample, trig_edge, trig_timer, conv_type); btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL, - btemp_ball_raw); + btemp_ball_raw); return seq_printf(s, "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw); @@ -865,9 +872,10 @@ static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V); + main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V, + avg_sample, trig_edge, trig_timer, conv_type); main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, - MAIN_CHARGER_V, main_charger_v_raw); + MAIN_CHARGER_V, main_charger_v_raw); return seq_printf(s, "%d,0x%X\n", main_charger_v_convert, main_charger_v_raw); @@ -895,9 +903,10 @@ static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1); + acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1, + avg_sample, trig_edge, trig_timer, conv_type); acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1, - acc_detect1_raw); + acc_detect1_raw); return seq_printf(s, "%d,0x%X\n", acc_detect1_convert, acc_detect1_raw); @@ -925,9 +934,10 @@ static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2); + acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2, + avg_sample, trig_edge, trig_timer, conv_type); acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc, - ACC_DETECT2, acc_detect2_raw); + ACC_DETECT2, acc_detect2_raw); return seq_printf(s, "%d,0x%X\n", acc_detect2_convert, acc_detect2_raw); @@ -955,9 +965,10 @@ static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1); + aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1, + avg_sample, trig_edge, trig_timer, conv_type); aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1, - aux1_raw); + aux1_raw); return seq_printf(s, "%d,0x%X\n", aux1_convert, aux1_raw); @@ -983,9 +994,10 @@ static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2); + aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2, + avg_sample, trig_edge, trig_timer, conv_type); aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2, - aux2_raw); + aux2_raw); return seq_printf(s, "%d,0x%X\n", aux2_convert, aux2_raw); @@ -1011,9 +1023,10 @@ static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V); + main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V, + avg_sample, trig_edge, trig_timer, conv_type); main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V, - main_bat_v_raw); + main_bat_v_raw); return seq_printf(s, "%d,0x%X\n", main_bat_v_convert, main_bat_v_raw); @@ -1040,9 +1053,10 @@ static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V); + vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V, + avg_sample, trig_edge, trig_timer, conv_type); vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V, - vbus_v_raw); + vbus_v_raw); return seq_printf(s, "%d,0x%X\n", vbus_v_convert, vbus_v_raw); @@ -1068,9 +1082,10 @@ static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C); + main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C, + avg_sample, trig_edge, trig_timer, conv_type); main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, - MAIN_CHARGER_C, main_charger_c_raw); + MAIN_CHARGER_C, main_charger_c_raw); return seq_printf(s, "%d,0x%X\n", main_charger_c_convert, main_charger_c_raw); @@ -1098,9 +1113,10 @@ static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C); + usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C, + avg_sample, trig_edge, trig_timer, conv_type); usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, - USB_CHARGER_C, usb_charger_c_raw); + USB_CHARGER_C, usb_charger_c_raw); return seq_printf(s, "%d,0x%X\n", usb_charger_c_convert, usb_charger_c_raw); @@ -1128,9 +1144,10 @@ static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V); + bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V, + avg_sample, trig_edge, trig_timer, conv_type); bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, - BK_BAT_V, bk_bat_v_raw); + BK_BAT_V, bk_bat_v_raw); return seq_printf(s, "%d,0x%X\n", bk_bat_v_convert, bk_bat_v_raw); @@ -1156,9 +1173,10 @@ static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p) struct ab8500_gpadc *gpadc; gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP); + die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP, + avg_sample, trig_edge, trig_timer, conv_type); die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP, - die_temp_raw); + die_temp_raw); return seq_printf(s, "%d,0x%X\n", die_temp_convert, die_temp_raw); @@ -1177,6 +1195,208 @@ static const struct file_operations ab8500_gpadc_die_temp_fops = { .owner = THIS_MODULE, }; +static int ab8500_gpadc_avg_sample_print(struct seq_file *s, void *p) +{ + return seq_printf(s, "%d\n", avg_sample); +} + +static int ab8500_gpadc_avg_sample_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab8500_gpadc_avg_sample_print, + inode->i_private); +} + +static ssize_t ab8500_gpadc_avg_sample_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct device *dev = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_avg_sample; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_avg_sample); + if (err) + return -EINVAL; + if ((user_avg_sample == SAMPLE_1) || (user_avg_sample == SAMPLE_4) + || (user_avg_sample == SAMPLE_8) + || (user_avg_sample == SAMPLE_16)) { + avg_sample = (u8) user_avg_sample; + } else { + dev_err(dev, "debugfs error input: " + "should be egal to 1, 4, 8 or 16\n"); + return -EINVAL; + } + return buf_size; +} + +static const struct file_operations ab8500_gpadc_avg_sample_fops = { + .open = ab8500_gpadc_avg_sample_open, + .read = seq_read, + .write = ab8500_gpadc_avg_sample_write, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab8500_gpadc_trig_edge_print(struct seq_file *s, void *p) +{ + return seq_printf(s, "%d\n", trig_edge); +} + +static int ab8500_gpadc_trig_edge_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab8500_gpadc_trig_edge_print, + inode->i_private); +} + +static ssize_t ab8500_gpadc_trig_edge_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct device *dev = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_trig_edge; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_trig_edge); + if (err) + return -EINVAL; + if ((user_trig_edge == RISING_EDGE) + || (user_trig_edge == FALLING_EDGE)) { + trig_edge = (u8) user_trig_edge; + } else { + dev_err(dev, "Wrong input:\n" + "Enter 0. Rising edge\n" + "Enter 1. Falling edge\n"); + return -EINVAL; + } + return buf_size; +} + +static const struct file_operations ab8500_gpadc_trig_edge_fops = { + .open = ab8500_gpadc_trig_edge_open, + .read = seq_read, + .write = ab8500_gpadc_trig_edge_write, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab8500_gpadc_trig_timer_print(struct seq_file *s, void *p) +{ + return seq_printf(s, "%d\n", trig_timer); +} + +static int ab8500_gpadc_trig_timer_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab8500_gpadc_trig_timer_print, + inode->i_private); +} + +static ssize_t ab8500_gpadc_trig_timer_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct device *dev = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_trig_timer; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_trig_timer); + if (err) + return -EINVAL; + if ((user_trig_timer >= 0) && (user_trig_timer <= 255)) { + trig_timer = (u8) user_trig_timer; + } else { + dev_err(dev, "debugfs error input: " + "should be beetween 0 to 255\n"); + return -EINVAL; + } + return buf_size; +} + +static const struct file_operations ab8500_gpadc_trig_timer_fops = { + .open = ab8500_gpadc_trig_timer_open, + .read = seq_read, + .write = ab8500_gpadc_trig_timer_write, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab8500_gpadc_conv_type_print(struct seq_file *s, void *p) +{ + return seq_printf(s, "%d\n", conv_type); +} + +static int ab8500_gpadc_conv_type_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab8500_gpadc_conv_type_print, + inode->i_private); +} + +static ssize_t ab8500_gpadc_conv_type_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct device *dev = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_conv_type; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_conv_type); + if (err) + return -EINVAL; + if ((user_conv_type == ADC_SW) + || (user_conv_type == ADC_HW)) { + conv_type = (u8) user_conv_type; + } else { + dev_err(dev, "Wrong input:\n" + "Enter 0. ADC SW conversion\n" + "Enter 1. ADC HW conversion\n"); + return -EINVAL; + } + return buf_size; +} + +static const struct file_operations ab8500_gpadc_conv_type_fops = { + .open = ab8500_gpadc_conv_type_open, + .read = seq_read, + .write = ab8500_gpadc_conv_type_write, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + /* * return length of an ASCII numerical value, 0 is string is not a * numerical value. @@ -1722,6 +1942,26 @@ static int ab8500_debug_probe(struct platform_device *plf) if (!file) goto err; + file = debugfs_create_file("avg_sample", (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_avg_sample_fops); + if (!file) + goto err; + + file = debugfs_create_file("trig_edge", (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_edge_fops); + if (!file) + goto err; + + file = debugfs_create_file("trig_timer", (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_timer_fops); + if (!file) + goto err; + + file = debugfs_create_file("conv_type", (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_conv_type_fops); + if (!file) + goto err; + return 0; err: diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index 7f39479c1af..8673bf66f7d 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -55,13 +55,18 @@ #define EN_VTVOUT 0x02 #define EN_GPADC 0x01 #define DIS_GPADC 0x00 -#define SW_AVG_16 0x60 +#define AVG_1 0x00 +#define AVG_4 0x20 +#define AVG_8 0x40 +#define AVG_16 0x60 #define ADC_SW_CONV 0x04 #define EN_ICHAR 0x80 #define BTEMP_PULL_UP 0x08 #define EN_BUF 0x40 #define DIS_ZERO 0x00 #define GPADC_BUSY 0x01 +#define EN_FALLING 0x10 +#define EN_TRIG_EDGE 0x02 /* GPADC constants from AB8500 spec, UM0836 */ #define ADC_RESOLUTION 1024 @@ -116,7 +121,10 @@ struct adc_cal_data { * the completion of gpadc conversion * @ab8500_gpadc_lock: structure of type mutex * @regu: pointer to the struct regulator - * @irq: interrupt number that is used by gpadc + * @irq_sw: interrupt number that is used by gpadc for Sw + * conversion + * @irq_hw: interrupt number that is used by gpadc for Hw + * conversion * @cal_data array of ADC calibration data structs */ struct ab8500_gpadc { @@ -126,7 +134,8 @@ struct ab8500_gpadc { struct completion ab8500_gpadc_complete; struct mutex ab8500_gpadc_lock; struct regulator *regu; - int irq; + int irq_sw; + int irq_hw; struct adc_cal_data cal_data[NBR_CAL_INPUTS]; }; @@ -244,30 +253,35 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage); /** - * ab8500_gpadc_convert() - gpadc conversion + * ab8500_gpadc_sw_hw_convert() - gpadc conversion * @channel: analog channel to be converted to digital data + * @avg_sample: number of ADC sample to average + * @trig_egde: selected ADC trig edge + * @trig_timer: selected ADC trigger delay timer + * @conv_type: selected conversion type (HW or SW conversion) * * This function converts the selected analog i/p to digital * data. */ -int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel) +int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel, + u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type) { int ad_value; int voltage; - ad_value = ab8500_gpadc_read_raw(gpadc, channel); - - /* On failure retry a second time */ + ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample, + trig_edge, trig_timer, conv_type); +/* On failure retry a second time */ if (ad_value < 0) - ad_value = ab8500_gpadc_read_raw(gpadc, channel); - - if (ad_value < 0) { - dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel); + ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample, + trig_edge, trig_timer, conv_type); +if (ad_value < 0) { + dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", + channel); return ad_value; } voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value); - if (voltage < 0) dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:" " %d AD: 0x%x\n", channel, ad_value); @@ -279,11 +293,16 @@ EXPORT_SYMBOL(ab8500_gpadc_convert); /** * ab8500_gpadc_read_raw() - gpadc read * @channel: analog channel to be read + * @avg_sample: number of ADC sample to average + * @trig_edge: selected trig edge + * @trig_timer: selected ADC trigger delay timer + * @conv_type: selected conversion type (HW or SW conversion) * - * This function obtains the raw ADC value, this then needs - * to be converted by calling ab8500_gpadc_ad_to_voltage() + * This function obtains the raw ADC value for an hardware conversion, + * this then needs to be converted by calling ab8500_gpadc_ad_to_voltage() */ -int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) +int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, + u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type) { int ret; int looplimit = 0; @@ -293,7 +312,6 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) return -ENODEV; mutex_lock(&gpadc->ab8500_gpadc_lock); - /* Enable VTVout LDO this is required for GPADC */ pm_runtime_get_sync(gpadc->dev); @@ -321,9 +339,29 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) goto out; } - /* Select the channel source and set average samples to 16 */ - ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, - AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16)); + /* Select the channel source and set average samples */ + switch (avg_sample) { + case SAMPLE_1: + val = channel | AVG_1; + break; + case SAMPLE_4: + val = channel | AVG_4; + break; + case SAMPLE_8: + val = channel | AVG_8; + break; + default: + val = channel | AVG_16; + break; + + } + + if (conv_type == ADC_HW) + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL3_REG, val); + else + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL2_REG, val); if (ret < 0) { dev_err(gpadc->dev, "gpadc_conversion: set avg samples failed\n"); @@ -335,22 +373,43 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) * charging current sense if it needed, ABB 3.0 needs some special * treatment too. */ + if ((conv_type == ADC_HW) && (trig_edge)) { + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_FALLING, EN_FALLING); + + } switch (channel) { case MAIN_CHARGER_C: case USB_CHARGER_C: - ret = abx500_mask_and_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL1_REG, - EN_BUF | EN_ICHAR, - EN_BUF | EN_ICHAR); - break; - case BTEMP_BALL: - if (!is_ab8500_2p0_or_earlier(gpadc->parent)) { - /* Turn on btemp pull-up on ABB 3.0 */ + if (conv_type == ADC_HW) ret = abx500_mask_and_set_register_interruptible( gpadc->dev, AB8500_GPADC, AB8500_GPADC_CTRL1_REG, - EN_BUF | BTEMP_PULL_UP, - EN_BUF | BTEMP_PULL_UP); + EN_BUF | EN_ICHAR | EN_TRIG_EDGE, + EN_BUF | EN_ICHAR | EN_TRIG_EDGE); + else + ret = abx500_mask_and_set_register_interruptible( + gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF | EN_ICHAR, + EN_BUF | EN_ICHAR); + break; + case BTEMP_BALL: + if (!is_ab8500_2p0_or_earlier(gpadc->parent)) { + if (conv_type == ADC_HW) + /* Turn on btemp pull-up on ABB 3.0 */ + ret = abx500_mask_and_set_register_interruptible + (gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF | BTEMP_PULL_UP | EN_TRIG_EDGE, + EN_BUF | BTEMP_PULL_UP | EN_TRIG_EDGE); + else + ret = abx500_mask_and_set_register_interruptible + (gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF | BTEMP_PULL_UP, + EN_BUF | BTEMP_PULL_UP); /* * Delay might be needed for ABB8500 cut 3.0, if not, remove @@ -361,8 +420,17 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) } /* Intentional fallthrough */ default: - ret = abx500_mask_and_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF); + if (conv_type == ADC_HW) + ret = abx500_mask_and_set_register_interruptible( + gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF | EN_TRIG_EDGE, + EN_BUF | EN_TRIG_EDGE); + else + ret = abx500_mask_and_set_register_interruptible( + gpadc->dev, + AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF); break; } if (ret < 0) { @@ -371,36 +439,83 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) goto out; } - ret = abx500_mask_and_set_register_interruptible(gpadc->dev, - AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: start s/w conversion failed\n"); - goto out; + /* Set trigger delay timer */ + if (conv_type == ADC_HW) { + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, trig_timer); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: trig timer failed\n"); + goto out; + } + } + + /* Start SW conversion */ + if (conv_type == ADC_SW) { + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + ADC_SW_CONV, ADC_SW_CONV); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: start s/w conv failed\n"); + goto out; + } } + /* wait for completion of conversion */ - if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, - msecs_to_jiffies(CONVERSION_TIME))) { - dev_err(gpadc->dev, - "timeout: didn't receive GPADC conversion interrupt\n"); - ret = -EINVAL; - goto out; + if (conv_type == ADC_HW) { + if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, + 2*HZ)) { + dev_err(gpadc->dev, + "timeout didn't receive" + " hw GPADC conv interrupt\n"); + ret = -EINVAL; + goto out; + } + } else { + if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, + msecs_to_jiffies(CONVERSION_TIME))) { + dev_err(gpadc->dev, + "timeout didn't receive" + " sw GPADC conv interrupt\n"); + ret = -EINVAL; + goto out; + } } /* Read the converted RAW data */ - ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC, - AB8500_GPADC_MANDATAL_REG, &low_data); - if (ret < 0) { - dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n"); - goto out; - } + if (conv_type == ADC_HW) { + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_AUTODATAL_REG, &low_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read hw low data failed\n"); + goto out; + } - ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC, - AB8500_GPADC_MANDATAH_REG, &high_data); - if (ret < 0) { - dev_err(gpadc->dev, - "gpadc_conversion: read high data failed\n"); - goto out; + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_AUTODATAH_REG, &high_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read hw high data failed\n"); + goto out; + } + } else { + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_MANDATAL_REG, &low_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw low data failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_MANDATAH_REG, &high_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw high data failed\n"); + goto out; + } } /* Disable GPADC */ @@ -411,6 +526,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) goto out; } + /* Disable VTVout LDO this is required for GPADC */ pm_runtime_mark_last_busy(gpadc->dev); pm_runtime_put_autosuspend(gpadc->dev); @@ -427,9 +543,7 @@ out: */ (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, AB8500_GPADC_CTRL1_REG, DIS_GPADC); - pm_runtime_put(gpadc->dev); - mutex_unlock(&gpadc->ab8500_gpadc_lock); dev_err(gpadc->dev, "gpadc_conversion: Failed to AD convert channel %d\n", channel); @@ -438,16 +552,16 @@ out: EXPORT_SYMBOL(ab8500_gpadc_read_raw); /** - * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion + * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion * @irq: irq number * @data: pointer to the data passed during request irq * - * This is a interrupt service routine for s/w gpadc conversion completion. + * This is a interrupt service routine for gpadc conversion completion. * Notifies the gpadc completion is completed and the converted raw value * can be read from the registers. * Returns IRQ status(IRQ_HANDLED) */ -static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc) +static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *_gpadc) { struct ab8500_gpadc *gpadc = _gpadc; @@ -646,11 +760,19 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) return -ENOMEM; } - gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END"); - if (gpadc->irq < 0) { - dev_err(&pdev->dev, "failed to get platform irq-%d\n", - gpadc->irq); - ret = gpadc->irq; + gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END"); + if (gpadc->irq_sw < 0) { + dev_err(gpadc->dev, "failed to get platform irq-%d\n", + gpadc->irq_sw); + ret = gpadc->irq_sw; + goto fail; + } + + gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END"); + if (gpadc->irq_hw < 0) { + dev_err(gpadc->dev, "failed to get platform irq-%d\n", + gpadc->irq_hw); + ret = gpadc->irq_hw; goto fail; } @@ -661,14 +783,21 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) /* Initialize completion used to notify completion of conversion */ init_completion(&gpadc->ab8500_gpadc_complete); - /* Register interrupt - SwAdcComplete */ - ret = request_threaded_irq(gpadc->irq, NULL, - ab8500_bm_gpswadcconvend_handler, - IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED, - "ab8500-gpadc", gpadc); + /* Register interrupts */ + ret = request_threaded_irq(gpadc->irq_sw, NULL, + ab8500_bm_gpadcconvend_handler, + IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw", gpadc); + if (ret < 0) { + dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n", + gpadc->irq_sw); + goto fail; + } + ret = request_threaded_irq(gpadc->irq_hw, NULL, + ab8500_bm_gpadcconvend_handler, + IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw", gpadc); if (ret < 0) { dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n", - gpadc->irq); + gpadc->irq_hw); goto fail; } @@ -694,7 +823,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) dev_dbg(gpadc->dev, "probe success\n"); return 0; fail_irq: - free_irq(gpadc->irq, gpadc); + free_irq(gpadc->irq_sw, gpadc); + free_irq(gpadc->irq_hw, gpadc); fail: kfree(gpadc); gpadc = NULL; @@ -708,7 +838,8 @@ static int ab8500_gpadc_remove(struct platform_device *pdev) /* remove this gpadc entry from the list */ list_del(&gpadc->node); /* remove interrupt - completion of Sw ADC conversion */ - free_irq(gpadc->irq, gpadc); + free_irq(gpadc->irq_sw, gpadc); + free_irq(gpadc->irq_hw, gpadc); pm_runtime_get_sync(gpadc->dev); pm_runtime_disable(gpadc->dev); @@ -757,6 +888,7 @@ subsys_initcall_sync(ab8500_gpadc_init); module_exit(ab8500_gpadc_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson"); +MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson," + "M'boumba Cedric Madianga"); MODULE_ALIAS("platform:ab8500_gpadc"); MODULE_DESCRIPTION("AB8500 GPADC driver"); diff --git a/include/linux/mfd/abx500/ab8500-gpadc.h b/include/linux/mfd/abx500/ab8500-gpadc.h index 252966769d9..7694e7ab188 100644 --- a/include/linux/mfd/abx500/ab8500-gpadc.h +++ b/include/linux/mfd/abx500/ab8500-gpadc.h @@ -4,12 +4,14 @@ * * Author: Arun R Murthy * Author: Daniel Willerud + * Author: M'boumba Cedric Madianga */ #ifndef _AB8500_GPADC_H #define _AB8500_GPADC_H -/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2) */ +/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2 + * and ADCHwSel[4:0] in GPADCCtrl3 ) */ #define BAT_CTRL 0x01 #define BTEMP_BALL 0x02 #define MAIN_CHARGER_V 0x03 @@ -24,12 +26,32 @@ #define BK_BAT_V 0x0C #define DIE_TEMP 0x0D +#define SAMPLE_1 1 +#define SAMPLE_4 4 +#define SAMPLE_8 8 +#define SAMPLE_16 16 +#define RISING_EDGE 0 +#define FALLING_EDGE 1 + +/* Arbitrary ADC conversion type constants */ +#define ADC_SW 0 +#define ADC_HW 1 + + struct ab8500_gpadc; struct ab8500_gpadc *ab8500_gpadc_get(char *name); -int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel); -int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel); +int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel, + u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type); +static inline int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel) +{ + return ab8500_gpadc_sw_hw_convert(gpadc, channel, + SAMPLE_16, 0, 0, ADC_SW); +} + +int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, + u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type); int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, - u8 channel, int ad_value); + u8 channel, int ad_value); #endif /* _AB8500_GPADC_H */ -- cgit v1.2.3-70-g09d2 From 022ab148d28e8466e45d28552224e3029f1cccd8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 16 Feb 2013 10:25:07 +0100 Subject: pinctrl: Declare operation structures as const The pinconf, pinctrl and pinmux operation structures hold function pointers that are never modified. Declare them as const. Signed-off-by: Laurent Pinchart Signed-off-by: Linus Walleij --- drivers/pinctrl/devicetree.c | 4 ++-- drivers/pinctrl/mvebu/pinctrl-mvebu.c | 6 +++--- drivers/pinctrl/pinconf.c | 2 +- drivers/pinctrl/pinctrl-abx500.c | 6 +++--- drivers/pinctrl/pinctrl-at91.c | 6 +++--- drivers/pinctrl/pinctrl-bcm2835.c | 6 +++--- drivers/pinctrl/pinctrl-exynos5440.c | 6 +++--- drivers/pinctrl/pinctrl-falcon.c | 2 +- drivers/pinctrl/pinctrl-imx.c | 6 +++--- drivers/pinctrl/pinctrl-lantiq.c | 4 ++-- drivers/pinctrl/pinctrl-mxs.c | 6 +++--- drivers/pinctrl/pinctrl-nomadik.c | 6 +++--- drivers/pinctrl/pinctrl-pxa3xx.c | 4 ++-- drivers/pinctrl/pinctrl-samsung.c | 6 +++--- drivers/pinctrl/pinctrl-single.c | 6 +++--- drivers/pinctrl/pinctrl-sirf.c | 4 ++-- drivers/pinctrl/pinctrl-sunxi.c | 6 +++--- drivers/pinctrl/pinctrl-tegra.c | 6 +++--- drivers/pinctrl/pinctrl-u300.c | 6 +++--- drivers/pinctrl/pinctrl-xway.c | 2 +- drivers/pinctrl/spear/pinctrl-spear.c | 4 ++-- include/linux/pinctrl/pinctrl.h | 6 +++--- 22 files changed, 55 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index fd40a11ad64..c7b7cb47712 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -41,7 +41,7 @@ static void dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps) { if (pctldev) { - struct pinctrl_ops *ops = pctldev->desc->pctlops; + const struct pinctrl_ops *ops = pctldev->desc->pctlops; ops->dt_free_map(pctldev, map, num_maps); } else { /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */ @@ -122,7 +122,7 @@ static int dt_to_map_one_config(struct pinctrl *p, const char *statename, { struct device_node *np_pctldev; struct pinctrl_dev *pctldev; - struct pinctrl_ops *ops; + const struct pinctrl_ops *ops; int ret; struct pinctrl_map *map; unsigned num_maps; diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.c b/drivers/pinctrl/mvebu/pinctrl-mvebu.c index c689c04a4f5..61149914882 100644 --- a/drivers/pinctrl/mvebu/pinctrl-mvebu.c +++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.c @@ -263,7 +263,7 @@ static void mvebu_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, return; } -static struct pinconf_ops mvebu_pinconf_ops = { +static const struct pinconf_ops mvebu_pinconf_ops = { .pin_config_group_get = mvebu_pinconf_group_get, .pin_config_group_set = mvebu_pinconf_group_set, .pin_config_group_dbg_show = mvebu_pinconf_group_dbg_show, @@ -369,7 +369,7 @@ static int mvebu_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, return -ENOTSUPP; } -static struct pinmux_ops mvebu_pinmux_ops = { +static const struct pinmux_ops mvebu_pinmux_ops = { .get_functions_count = mvebu_pinmux_get_funcs_count, .get_function_name = mvebu_pinmux_get_func_name, .get_function_groups = mvebu_pinmux_get_groups, @@ -470,7 +470,7 @@ static void mvebu_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops mvebu_pinctrl_ops = { +static const struct pinctrl_ops mvebu_pinctrl_ops = { .get_groups_count = mvebu_pinctrl_get_groups_count, .get_group_name = mvebu_pinctrl_get_group_name, .get_group_pins = mvebu_pinctrl_get_group_pins, diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index ac8d382a79b..8aefd28c797 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -670,7 +670,7 @@ static int pinconf_dbg_config_print(struct seq_file *s, void *d) struct pinctrl_maps *maps_node; struct pinctrl_map const *map; struct pinctrl_dev *pctldev = NULL; - struct pinconf_ops *confops = NULL; + const struct pinconf_ops *confops = NULL; int i, j; bool found = false; diff --git a/drivers/pinctrl/pinctrl-abx500.c b/drivers/pinctrl/pinctrl-abx500.c index caecdd37306..169d72c59a7 100644 --- a/drivers/pinctrl/pinctrl-abx500.c +++ b/drivers/pinctrl/pinctrl-abx500.c @@ -656,7 +656,7 @@ static void abx500_gpio_disable_free(struct pinctrl_dev *pctldev, { } -static struct pinmux_ops abx500_pinmux_ops = { +static const struct pinmux_ops abx500_pinmux_ops = { .get_functions_count = abx500_pmx_get_funcs_cnt, .get_function_name = abx500_pmx_get_func_name, .get_function_groups = abx500_pmx_get_func_groups, @@ -704,7 +704,7 @@ static void abx500_pin_dbg_show(struct pinctrl_dev *pctldev, chip->base + offset - 1); } -static struct pinctrl_ops abx500_pinctrl_ops = { +static const struct pinctrl_ops abx500_pinctrl_ops = { .get_groups_count = abx500_get_groups_cnt, .get_group_name = abx500_get_group_name, .get_group_pins = abx500_get_group_pins, @@ -778,7 +778,7 @@ int abx500_pin_config_set(struct pinctrl_dev *pctldev, return ret; } -static struct pinconf_ops abx500_pinconf_ops = { +static const struct pinconf_ops abx500_pinconf_ops = { .pin_config_get = abx500_pin_config_get, .pin_config_set = abx500_pin_config_set, }; diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 75933a6aa82..e50fa5f863e 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -294,7 +294,7 @@ static void at91_dt_free_map(struct pinctrl_dev *pctldev, { } -static struct pinctrl_ops at91_pctrl_ops = { +static const struct pinctrl_ops at91_pctrl_ops = { .get_groups_count = at91_get_groups_count, .get_group_name = at91_get_group_name, .get_group_pins = at91_get_group_pins, @@ -696,7 +696,7 @@ static void at91_gpio_disable_free(struct pinctrl_dev *pctldev, /* Set the pin to some default state, GPIO is usually default */ } -static struct pinmux_ops at91_pmx_ops = { +static const struct pinmux_ops at91_pmx_ops = { .get_functions_count = at91_pmx_get_funcs_count, .get_function_name = at91_pmx_get_func_name, .get_function_groups = at91_pmx_get_groups, @@ -776,7 +776,7 @@ static void at91_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, { } -static struct pinconf_ops at91_pinconf_ops = { +static const struct pinconf_ops at91_pinconf_ops = { .pin_config_get = at91_pinconf_get, .pin_config_set = at91_pinconf_set, .pin_config_dbg_show = at91_pinconf_dbg_show, diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c index 4eb6d2c4e4d..f28d4b08771 100644 --- a/drivers/pinctrl/pinctrl-bcm2835.c +++ b/drivers/pinctrl/pinctrl-bcm2835.c @@ -795,7 +795,7 @@ out: return err; } -static struct pinctrl_ops bcm2835_pctl_ops = { +static const struct pinctrl_ops bcm2835_pctl_ops = { .get_groups_count = bcm2835_pctl_get_groups_count, .get_group_name = bcm2835_pctl_get_group_name, .get_group_pins = bcm2835_pctl_get_group_pins, @@ -872,7 +872,7 @@ static int bcm2835_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, return 0; } -static struct pinmux_ops bcm2835_pmx_ops = { +static const struct pinmux_ops bcm2835_pmx_ops = { .get_functions_count = bcm2835_pmx_get_functions_count, .get_function_name = bcm2835_pmx_get_function_name, .get_function_groups = bcm2835_pmx_get_function_groups, @@ -916,7 +916,7 @@ static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev, return 0; } -static struct pinconf_ops bcm2835_pinconf_ops = { +static const struct pinconf_ops bcm2835_pinconf_ops = { .pin_config_get = bcm2835_pinconf_get, .pin_config_set = bcm2835_pinconf_set, }; diff --git a/drivers/pinctrl/pinctrl-exynos5440.c b/drivers/pinctrl/pinctrl-exynos5440.c index 1376eb7305d..169ea3e5f77 100644 --- a/drivers/pinctrl/pinctrl-exynos5440.c +++ b/drivers/pinctrl/pinctrl-exynos5440.c @@ -286,7 +286,7 @@ static void exynos5440_dt_free_map(struct pinctrl_dev *pctldev, } /* list of pinctrl callbacks for the pinctrl core */ -static struct pinctrl_ops exynos5440_pctrl_ops = { +static const struct pinctrl_ops exynos5440_pctrl_ops = { .get_groups_count = exynos5440_get_group_count, .get_group_name = exynos5440_get_group_name, .get_group_pins = exynos5440_get_group_pins, @@ -374,7 +374,7 @@ static int exynos5440_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, } /* list of pinmux callbacks for the pinmux vertical in pinctrl core */ -static struct pinmux_ops exynos5440_pinmux_ops = { +static const struct pinmux_ops exynos5440_pinmux_ops = { .get_functions_count = exynos5440_get_functions_count, .get_function_name = exynos5440_pinmux_get_fname, .get_function_groups = exynos5440_pinmux_get_groups, @@ -523,7 +523,7 @@ static int exynos5440_pinconf_group_get(struct pinctrl_dev *pctldev, } /* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */ -static struct pinconf_ops exynos5440_pinconf_ops = { +static const struct pinconf_ops exynos5440_pinconf_ops = { .pin_config_get = exynos5440_pinconf_get, .pin_config_set = exynos5440_pinconf_set, .pin_config_group_get = exynos5440_pinconf_group_get, diff --git a/drivers/pinctrl/pinctrl-falcon.c b/drivers/pinctrl/pinctrl-falcon.c index af97a1f9000..f9b2a1d4854 100644 --- a/drivers/pinctrl/pinctrl-falcon.c +++ b/drivers/pinctrl/pinctrl-falcon.c @@ -353,7 +353,7 @@ static void falcon_pinconf_group_dbg_show(struct pinctrl_dev *pctrldev, { } -static struct pinconf_ops falcon_pinconf_ops = { +static const struct pinconf_ops falcon_pinconf_ops = { .pin_config_get = falcon_pinconf_get, .pin_config_set = falcon_pinconf_set, .pin_config_group_get = falcon_pinconf_group_get, diff --git a/drivers/pinctrl/pinctrl-imx.c b/drivers/pinctrl/pinctrl-imx.c index 4cebb9c6c5c..0ef190449ea 100644 --- a/drivers/pinctrl/pinctrl-imx.c +++ b/drivers/pinctrl/pinctrl-imx.c @@ -207,7 +207,7 @@ static void imx_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops imx_pctrl_ops = { +static const struct pinctrl_ops imx_pctrl_ops = { .get_groups_count = imx_get_groups_count, .get_group_name = imx_get_group_name, .get_group_pins = imx_get_group_pins, @@ -299,7 +299,7 @@ static int imx_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -static struct pinmux_ops imx_pmx_ops = { +static const struct pinmux_ops imx_pmx_ops = { .get_functions_count = imx_pmx_get_funcs_count, .get_function_name = imx_pmx_get_func_name, .get_function_groups = imx_pmx_get_groups, @@ -397,7 +397,7 @@ static void imx_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, } } -static struct pinconf_ops imx_pinconf_ops = { +static const struct pinconf_ops imx_pinconf_ops = { .pin_config_get = imx_pinconf_get, .pin_config_set = imx_pinconf_set, .pin_config_dbg_show = imx_pinconf_dbg_show, diff --git a/drivers/pinctrl/pinctrl-lantiq.c b/drivers/pinctrl/pinctrl-lantiq.c index a7038461135..615c5002b75 100644 --- a/drivers/pinctrl/pinctrl-lantiq.c +++ b/drivers/pinctrl/pinctrl-lantiq.c @@ -169,7 +169,7 @@ static int ltq_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, return 0; } -static struct pinctrl_ops ltq_pctrl_ops = { +static const struct pinctrl_ops ltq_pctrl_ops = { .get_groups_count = ltq_get_group_count, .get_group_name = ltq_get_group_name, .get_group_pins = ltq_get_group_pins, @@ -311,7 +311,7 @@ static int ltq_pmx_gpio_request_enable(struct pinctrl_dev *pctrldev, return info->apply_mux(pctrldev, mfp, pin_func); } -static struct pinmux_ops ltq_pmx_ops = { +static const struct pinmux_ops ltq_pmx_ops = { .get_functions_count = ltq_pmx_func_count, .get_function_name = ltq_pmx_func_name, .get_function_groups = ltq_pmx_get_groups, diff --git a/drivers/pinctrl/pinctrl-mxs.c b/drivers/pinctrl/pinctrl-mxs.c index 23af9f1f9c3..b45c4eb3579 100644 --- a/drivers/pinctrl/pinctrl-mxs.c +++ b/drivers/pinctrl/pinctrl-mxs.c @@ -158,7 +158,7 @@ static void mxs_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops mxs_pinctrl_ops = { +static const struct pinctrl_ops mxs_pinctrl_ops = { .get_groups_count = mxs_get_groups_count, .get_group_name = mxs_get_group_name, .get_group_pins = mxs_get_group_pins, @@ -219,7 +219,7 @@ static int mxs_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -static struct pinmux_ops mxs_pinmux_ops = { +static const struct pinmux_ops mxs_pinmux_ops = { .get_functions_count = mxs_pinctrl_get_funcs_count, .get_function_name = mxs_pinctrl_get_func_name, .get_function_groups = mxs_pinctrl_get_func_groups, @@ -319,7 +319,7 @@ static void mxs_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, seq_printf(s, "0x%lx", config); } -static struct pinconf_ops mxs_pinconf_ops = { +static const struct pinconf_ops mxs_pinconf_ops = { .pin_config_get = mxs_pinconf_get, .pin_config_set = mxs_pinconf_set, .pin_config_group_get = mxs_pinconf_group_get, diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index 36d20293de5..2328baaa86b 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -1764,7 +1764,7 @@ int nmk_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, return 0; } -static struct pinctrl_ops nmk_pinctrl_ops = { +static const struct pinctrl_ops nmk_pinctrl_ops = { .get_groups_count = nmk_get_groups_cnt, .get_group_name = nmk_get_group_name, .get_group_pins = nmk_get_group_pins, @@ -1975,7 +1975,7 @@ static void nmk_gpio_disable_free(struct pinctrl_dev *pctldev, /* Set the pin to some default state, GPIO is usually default */ } -static struct pinmux_ops nmk_pinmux_ops = { +static const struct pinmux_ops nmk_pinmux_ops = { .get_functions_count = nmk_pmx_get_funcs_cnt, .get_function_name = nmk_pmx_get_func_name, .get_function_groups = nmk_pmx_get_func_groups, @@ -2089,7 +2089,7 @@ static int nmk_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, return 0; } -static struct pinconf_ops nmk_pinconf_ops = { +static const struct pinconf_ops nmk_pinconf_ops = { .pin_config_get = nmk_pin_config_get, .pin_config_set = nmk_pin_config_set, }; diff --git a/drivers/pinctrl/pinctrl-pxa3xx.c b/drivers/pinctrl/pinctrl-pxa3xx.c index 1f49bb02a6a..05e11de1d14 100644 --- a/drivers/pinctrl/pinctrl-pxa3xx.c +++ b/drivers/pinctrl/pinctrl-pxa3xx.c @@ -53,7 +53,7 @@ static int pxa3xx_get_group_pins(struct pinctrl_dev *pctrldev, return 0; } -static struct pinctrl_ops pxa3xx_pctrl_ops = { +static const struct pinctrl_ops pxa3xx_pctrl_ops = { .get_groups_count = pxa3xx_get_groups_count, .get_group_name = pxa3xx_get_group_name, .get_group_pins = pxa3xx_get_group_pins, @@ -161,7 +161,7 @@ static int pxa3xx_pmx_request_gpio(struct pinctrl_dev *pctrldev, return 0; } -static struct pinmux_ops pxa3xx_pmx_ops = { +static const struct pinmux_ops pxa3xx_pmx_ops = { .get_functions_count = pxa3xx_pmx_get_funcs_count, .get_function_name = pxa3xx_pmx_get_func_name, .get_function_groups = pxa3xx_pmx_get_groups, diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index f206df17565..3475b92b24a 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -214,7 +214,7 @@ static void samsung_dt_free_map(struct pinctrl_dev *pctldev, } /* list of pinctrl callbacks for the pinctrl core */ -static struct pinctrl_ops samsung_pctrl_ops = { +static const struct pinctrl_ops samsung_pctrl_ops = { .get_groups_count = samsung_get_group_count, .get_group_name = samsung_get_group_name, .get_group_pins = samsung_get_group_pins, @@ -357,7 +357,7 @@ static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, } /* list of pinmux callbacks for the pinmux vertical in pinctrl core */ -static struct pinmux_ops samsung_pinmux_ops = { +static const struct pinmux_ops samsung_pinmux_ops = { .get_functions_count = samsung_get_functions_count, .get_function_name = samsung_pinmux_get_fname, .get_function_groups = samsung_pinmux_get_groups, @@ -468,7 +468,7 @@ static int samsung_pinconf_group_get(struct pinctrl_dev *pctldev, } /* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */ -static struct pinconf_ops samsung_pinconf_ops = { +static const struct pinconf_ops samsung_pinconf_ops = { .pin_config_get = samsung_pinconf_get, .pin_config_set = samsung_pinconf_set, .pin_config_group_get = samsung_pinconf_group_get, diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 5c32e880bcb..0c0e2da9d88 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -270,7 +270,7 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps); -static struct pinctrl_ops pcs_pinctrl_ops = { +static const struct pinctrl_ops pcs_pinctrl_ops = { .get_groups_count = pcs_get_groups_count, .get_group_name = pcs_get_group_name, .get_group_pins = pcs_get_group_pins, @@ -408,7 +408,7 @@ static int pcs_request_gpio(struct pinctrl_dev *pctldev, return -ENOTSUPP; } -static struct pinmux_ops pcs_pinmux_ops = { +static const struct pinmux_ops pcs_pinmux_ops = { .get_functions_count = pcs_get_functions_count, .get_function_name = pcs_get_function_name, .get_function_groups = pcs_get_function_groups, @@ -451,7 +451,7 @@ static void pcs_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, { } -static struct pinconf_ops pcs_pinconf_ops = { +static const struct pinconf_ops pcs_pinconf_ops = { .pin_config_get = pcs_pinconf_get, .pin_config_set = pcs_pinconf_set, .pin_config_group_get = pcs_pinconf_group_get, diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c index d02498b30c6..0990a721758 100644 --- a/drivers/pinctrl/pinctrl-sirf.c +++ b/drivers/pinctrl/pinctrl-sirf.c @@ -979,7 +979,7 @@ static void sirfsoc_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops sirfsoc_pctrl_ops = { +static const struct pinctrl_ops sirfsoc_pctrl_ops = { .get_groups_count = sirfsoc_get_groups_count, .get_group_name = sirfsoc_get_group_name, .get_group_pins = sirfsoc_get_group_pins, @@ -1181,7 +1181,7 @@ static int sirfsoc_pinmux_request_gpio(struct pinctrl_dev *pmxdev, return 0; } -static struct pinmux_ops sirfsoc_pinmux_ops = { +static const struct pinmux_ops sirfsoc_pinmux_ops = { .enable = sirfsoc_pinmux_enable, .disable = sirfsoc_pinmux_disable, .get_functions_count = sirfsoc_pinmux_get_funcs_count, diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c index 80b11e3415b..46b8f2d4f0a 100644 --- a/drivers/pinctrl/pinctrl-sunxi.c +++ b/drivers/pinctrl/pinctrl-sunxi.c @@ -1029,7 +1029,7 @@ static void sunxi_pctrl_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops sunxi_pctrl_ops = { +static const struct pinctrl_ops sunxi_pctrl_ops = { .dt_node_to_map = sunxi_pctrl_dt_node_to_map, .dt_free_map = sunxi_pctrl_dt_free_map, .get_groups_count = sunxi_pctrl_get_groups_count, @@ -1098,7 +1098,7 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, return 0; } -static struct pinconf_ops sunxi_pconf_ops = { +static const struct pinconf_ops sunxi_pconf_ops = { .pin_config_group_get = sunxi_pconf_group_get, .pin_config_group_set = sunxi_pconf_group_set, }; @@ -1204,7 +1204,7 @@ error: return ret; } -static struct pinmux_ops sunxi_pmx_ops = { +static const struct pinmux_ops sunxi_pmx_ops = { .get_functions_count = sunxi_pmx_get_funcs_cnt, .get_function_name = sunxi_pmx_get_func_name, .get_function_groups = sunxi_pmx_get_func_groups, diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index f195d77a357..2fa9bc6cd7a 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -316,7 +316,7 @@ static int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, return 0; } -static struct pinctrl_ops tegra_pinctrl_ops = { +static const struct pinctrl_ops tegra_pinctrl_ops = { .get_groups_count = tegra_pinctrl_get_groups_count, .get_group_name = tegra_pinctrl_get_group_name, .get_group_pins = tegra_pinctrl_get_group_pins, @@ -401,7 +401,7 @@ static void tegra_pinctrl_disable(struct pinctrl_dev *pctldev, pmx_writel(pmx, val, g->mux_bank, g->mux_reg); } -static struct pinmux_ops tegra_pinmux_ops = { +static const struct pinmux_ops tegra_pinmux_ops = { .get_functions_count = tegra_pinctrl_get_funcs_count, .get_function_name = tegra_pinctrl_get_func_name, .get_function_groups = tegra_pinctrl_get_func_groups, @@ -676,7 +676,7 @@ static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, } #endif -static struct pinconf_ops tegra_pinconf_ops = { +static const struct pinconf_ops tegra_pinconf_ops = { .pin_config_get = tegra_pinconf_get, .pin_config_set = tegra_pinconf_set, .pin_config_group_get = tegra_pinconf_group_get, diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 2b577255083..6a3a7503e6a 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -860,7 +860,7 @@ static void u300_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, seq_printf(s, " " DRIVER_NAME); } -static struct pinctrl_ops u300_pctrl_ops = { +static const struct pinctrl_ops u300_pctrl_ops = { .get_groups_count = u300_get_groups_count, .get_group_name = u300_get_group_name, .get_group_pins = u300_get_group_pins, @@ -1003,7 +1003,7 @@ static int u300_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -static struct pinmux_ops u300_pmx_ops = { +static const struct pinmux_ops u300_pmx_ops = { .get_functions_count = u300_pmx_get_funcs_count, .get_function_name = u300_pmx_get_func_name, .get_function_groups = u300_pmx_get_groups, @@ -1046,7 +1046,7 @@ static int u300_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, return 0; } -static struct pinconf_ops u300_pconf_ops = { +static const struct pinconf_ops u300_pconf_ops = { .is_generic = true, .pin_config_get = u300_pin_config_get, .pin_config_set = u300_pin_config_set, diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c index 068224efa6f..f2977cff836 100644 --- a/drivers/pinctrl/pinctrl-xway.c +++ b/drivers/pinctrl/pinctrl-xway.c @@ -553,7 +553,7 @@ int xway_pinconf_group_set(struct pinctrl_dev *pctldev, return ret; } -static struct pinconf_ops xway_pinconf_ops = { +static const struct pinconf_ops xway_pinconf_ops = { .pin_config_get = xway_pinconf_get, .pin_config_set = xway_pinconf_set, .pin_config_group_set = xway_pinconf_group_set, diff --git a/drivers/pinctrl/spear/pinctrl-spear.c b/drivers/pinctrl/spear/pinctrl-spear.c index 6a7dae70db0..116da0412c4 100644 --- a/drivers/pinctrl/spear/pinctrl-spear.c +++ b/drivers/pinctrl/spear/pinctrl-spear.c @@ -198,7 +198,7 @@ static void spear_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, kfree(map); } -static struct pinctrl_ops spear_pinctrl_ops = { +static const struct pinctrl_ops spear_pinctrl_ops = { .get_groups_count = spear_pinctrl_get_groups_cnt, .get_group_name = spear_pinctrl_get_group_name, .get_group_pins = spear_pinctrl_get_group_pins, @@ -340,7 +340,7 @@ static void gpio_disable_free(struct pinctrl_dev *pctldev, gpio_request_endisable(pctldev, range, offset, false); } -static struct pinmux_ops spear_pinmux_ops = { +static const struct pinmux_ops spear_pinmux_ops = { .get_functions_count = spear_pinctrl_get_funcs_count, .get_function_name = spear_pinctrl_get_func_name, .get_function_groups = spear_pinctrl_get_func_groups, diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 778804df293..2c2a9e8d857 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -118,9 +118,9 @@ struct pinctrl_desc { const char *name; struct pinctrl_pin_desc const *pins; unsigned int npins; - struct pinctrl_ops *pctlops; - struct pinmux_ops *pmxops; - struct pinconf_ops *confops; + const struct pinctrl_ops *pctlops; + const struct pinmux_ops *pmxops; + const struct pinconf_ops *confops; struct module *owner; }; -- cgit v1.2.3-70-g09d2 From 3e1a498f2728476535571d270081a17fdfceaf26 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 25 Feb 2013 14:57:35 +0000 Subject: mfd: ab8500-core: Add Interrupt support for ab8540 ITSource/ITLatch 7, 8, 9 and 10 don't exist on AB8540. This patch replaces them with '-1' in the interrupt list, and handles the '-1' in the code accordingly. Signed-off-by: Lee Jones Acked-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 50 +++++++++++++++++++++++++++++++++------ include/linux/mfd/abx500/ab8500.h | 1 + 2 files changed, 44 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 23db4fcea49..0ba08a26cf7 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -103,8 +103,10 @@ #define AB8500_IT_LATCHHIER1_REG 0x60 #define AB8500_IT_LATCHHIER2_REG 0x61 #define AB8500_IT_LATCHHIER3_REG 0x62 +#define AB8540_IT_LATCHHIER4_REG 0x63 #define AB8500_IT_LATCHHIER_NUM 3 +#define AB8540_IT_LATCHHIER_NUM 4 #define AB8500_REV_REG 0x80 #define AB8500_IC_NAME_REG 0x82 @@ -143,6 +145,12 @@ static const int ab9540_irq_regoffset[AB9540_NUM_IRQ_REGS] = { 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24, 5, 22, 23 }; +/* AB8540 support */ +static const int ab8540_irq_regoffset[AB8540_NUM_IRQ_REGS] = { + 0, 1, 2, 3, 4, -1, -1, -1, -1, 11, 18, 19, 20, 21, 12, 13, 24, 5, 22, 23, + 25, 26, 27, 28, 29, 30, 31, +}; + static const char ab8500_version_str[][7] = { [AB8500_VERSION_AB8500] = "AB8500", [AB8500_VERSION_AB8505] = "AB8505", @@ -360,6 +368,9 @@ static void ab8500_irq_sync_unlock(struct irq_data *data) is_ab8500_1p1_or_earlier(ab8500)) continue; + if (ab8500->irq_reg_offset[i] < 0) + continue; + ab8500->oldmask[i] = new; reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i]; @@ -431,6 +442,18 @@ static struct irq_chip ab8500_irq_chip = { .irq_set_type = ab8500_irq_set_type, }; +static void update_latch_offset(u8 *offset, int i) +{ + /* Fix inconsistent ITFromLatch25 bit mapping... */ + if (unlikely(*offset == 17)) + *offset = 24; + /* Fix inconsistent ab8540 bit mapping... */ + if (unlikely(*offset == 16)) + *offset = 25; + if ((i==3) && (*offset >= 24)) + *offset += 2; +} + static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, int latch_offset, u8 latch_val) { @@ -482,9 +505,7 @@ static int ab8500_handle_hierarchical_latch(struct ab8500 *ab8500, latch_bit = __ffs(hier_val); latch_offset = (hier_offset << 3) + latch_bit; - /* Fix inconsistent ITFromLatch25 bit mapping... */ - if (unlikely(latch_offset == 17)) - latch_offset = 24; + update_latch_offset(&latch_offset, hier_offset); status = get_register_interruptible(ab8500, AB8500_INTERRUPT, @@ -512,7 +533,7 @@ static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev) dev_vdbg(ab8500->dev, "interrupt\n"); /* Hierarchical interrupt version */ - for (i = 0; i < AB8500_IT_LATCHHIER_NUM; i++) { + for (i = 0; i < (ab8500->it_latchhier_num); i++) { int status; u8 hier_val; @@ -565,6 +586,9 @@ static irqreturn_t ab8500_irq(int irq, void *dev) if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500)) continue; + if (regoffset < 0) + continue; + status = get_register_interruptible(ab8500, AB8500_INTERRUPT, AB8500_IT_LATCH1_REG + regoffset, &value); if (status < 0 || value == 0) @@ -615,7 +639,9 @@ static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np) { int num_irqs; - if (is_ab9540(ab8500)) + if (is_ab8540(ab8500)) + num_irqs = AB8540_NR_IRQS; + else if (is_ab9540(ab8500)) num_irqs = AB9540_NR_IRQS; else if (is_ab8505(ab8500)) num_irqs = AB8505_NR_IRQS; @@ -1552,13 +1578,20 @@ static int ab8500_probe(struct platform_device *pdev) ab8500->chip_id >> 4, ab8500->chip_id & 0x0F); - /* Configure AB8500 or AB9540 IRQ */ - if (is_ab9540(ab8500) || is_ab8505(ab8500)) { + /* Configure AB8540 */ + if (is_ab8540(ab8500)) { + ab8500->mask_size = AB8540_NUM_IRQ_REGS; + ab8500->irq_reg_offset = ab8540_irq_regoffset; + ab8500->it_latchhier_num = AB8540_IT_LATCHHIER_NUM; + }/* Configure AB8500 or AB9540 IRQ */ + else if (is_ab9540(ab8500) || is_ab8505(ab8500)) { ab8500->mask_size = AB9540_NUM_IRQ_REGS; ab8500->irq_reg_offset = ab9540_irq_regoffset; + ab8500->it_latchhier_num = AB8500_IT_LATCHHIER_NUM; } else { ab8500->mask_size = AB8500_NUM_IRQ_REGS; ab8500->irq_reg_offset = ab8500_irq_regoffset; + ab8500->it_latchhier_num = AB8500_IT_LATCHHIER_NUM; } ab8500->mask = devm_kzalloc(&pdev->dev, ab8500->mask_size, GFP_KERNEL); if (!ab8500->mask) @@ -1620,6 +1653,9 @@ static int ab8500_probe(struct platform_device *pdev) is_ab8500_1p1_or_earlier(ab8500)) continue; + if (ab8500->irq_reg_offset[i] < 0) + continue; + get_register_interruptible(ab8500, AB8500_INTERRUPT, AB8500_IT_LATCH1_REG + ab8500->irq_reg_offset[i], &value); diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index fdd8be64fee..b5780fd40fe 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -362,6 +362,7 @@ struct ab8500 { u8 *oldmask; int mask_size; const int *irq_reg_offset; + int it_latchhier_num; }; struct regulator_reg_init; -- cgit v1.2.3-70-g09d2 From 75932094601b404fc9ef28f7b6c0aa83dd619af0 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 12 Feb 2013 15:11:19 +0000 Subject: mfd: ab8500-sysctrl: Add new reset function Add a new reset function which uses the AB WD with 0 timeout. Signed-off-by: Lee Jones Acked-by: Samuel Ortiz --- drivers/mfd/ab8500-sysctrl.c | 63 +++++++++++++++++++++++++++++++ include/linux/mfd/abx500/ab8500-sysctrl.h | 6 +++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 6ac63a05893..f43c42b9f32 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -15,6 +15,12 @@ #include #include +/* RtcCtrl bits */ +#define AB8500_ALARM_MIN_LOW 0x08 +#define AB8500_ALARM_MIN_MID 0x09 +#define RTC_CTRL 0x0B +#define RTC_ALARM_ENABLE 0x4 + static struct device *sysctrl_dev; void ab8500_power_off(void) @@ -79,6 +85,63 @@ shutdown: } } +/* + * Use the AB WD to reset the platform. It will perform a hard + * reset instead of a soft reset. Write the reset reason to + * the AB before reset, which can be read upon restart. + */ +void ab8500_restart(char mode, const char *cmd) +{ + struct ab8500_platform_data *plat; + struct ab8500_sysctrl_platform_data *pdata; + u16 reason = 0; + u8 val; + + if (sysctrl_dev == NULL) { + pr_err("%s: sysctrl not initialized\n", __func__); + return; + } + + plat = dev_get_platdata(sysctrl_dev->parent); + pdata = plat->sysctrl; + if (pdata->reboot_reason_code) + reason = pdata->reboot_reason_code(cmd); + else + pr_warn("[%s] No reboot reason set. Default reason %d\n", + __func__, reason); + + /* + * Disable RTC alarm, just a precaution so that no alarm + * is running when WD reset is executed. + */ + abx500_get_register_interruptible(sysctrl_dev, AB8500_RTC, + RTC_CTRL , &val); + abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, + RTC_CTRL , (val & ~RTC_ALARM_ENABLE)); + + /* + * Android is not using the RTC alarm registers during reboot + * so we borrow them for writing the reason of reset + */ + + /* reason[8 LSB] */ + val = reason & 0xFF; + abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, + AB8500_ALARM_MIN_LOW , val); + + /* reason[8 MSB] */ + val = (reason>>8) & 0xFF; + abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC, + AB8500_ALARM_MIN_MID , val); + + /* Setting WD timeout to 0 */ + ab8500_sysctrl_write(AB8500_MAINWDOGTIMER, 0xFF, 0x0); + + /* Setting the parameters to AB8500 WD*/ + ab8500_sysctrl_write(AB8500_MAINWDOGCTRL, 0xFF, (AB8500_ENABLE_WD | + AB8500_WD_RESTART_ON_EXPIRE | AB8500_KICK_WD)); +} + static inline bool valid_bank(u8 bank) { return ((bank == AB8500_SYS_CTRL1_BLOCK) || diff --git a/include/linux/mfd/abx500/ab8500-sysctrl.h b/include/linux/mfd/abx500/ab8500-sysctrl.h index ebf12e793db..990bc93f46e 100644 --- a/include/linux/mfd/abx500/ab8500-sysctrl.h +++ b/include/linux/mfd/abx500/ab8500-sysctrl.h @@ -12,6 +12,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value); int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value); +void ab8500_restart(char mode, const char *cmd); #else @@ -40,6 +41,7 @@ static inline int ab8500_sysctrl_clear(u16 reg, u8 bits) /* Configuration data for SysClkReq1RfClkBuf - SysClkReq8RfClkBuf */ struct ab8500_sysctrl_platform_data { u8 initial_req_buf_config[8]; + u16 (*reboot_reason_code)(const char *cmd); }; /* Registers */ @@ -299,4 +301,8 @@ struct ab8500_sysctrl_platform_data { #define AB9540_SYSCLK12BUF4VALID_SYSCLK12BUF4VALID_MASK 0xFF #define AB9540_SYSCLK12BUF4VALID_SYSCLK12BUF4VALID_SHIFT 0 +#define AB8500_ENABLE_WD 0x1 +#define AB8500_KICK_WD 0x2 +#define AB8500_WD_RESTART_ON_EXPIRE 0x10 + #endif /* __AB8500_SYSCTRL_H */ -- cgit v1.2.3-70-g09d2 From e4bffe8d8ad9856143b6e941a17870aee37413d7 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 11 Feb 2013 10:38:00 +0000 Subject: mfd: ab8500-gpadc: Add support for the AB8540 This patch enables the GPADC to work on AB8540 based platforms. Signed-off-by: Lee Jones Acked-by: Samuel Ortiz --- drivers/mfd/ab8500-gpadc.c | 316 ++++++++++++++++++++++++++++---- include/linux/mfd/abx500/ab8500-gpadc.h | 43 +++-- 2 files changed, 305 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index fc8da4496e8..c985b90577f 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -37,6 +37,13 @@ #define AB8500_GPADC_AUTODATAL_REG 0x07 #define AB8500_GPADC_AUTODATAH_REG 0x08 #define AB8500_GPADC_MUX_CTRL_REG 0x09 +#define AB8540_GPADC_MANDATA2L_REG 0x09 +#define AB8540_GPADC_MANDATA2H_REG 0x0A +#define AB8540_GPADC_APEAAX_REG 0x10 +#define AB8540_GPADC_APEAAT_REG 0x11 +#define AB8540_GPADC_APEAAM_REG 0x12 +#define AB8540_GPADC_APEAAH_REG 0x13 +#define AB8540_GPADC_APEAAL_REG 0x14 /* * OTP register offsets @@ -49,6 +56,10 @@ #define AB8500_GPADC_CAL_5 0x13 #define AB8500_GPADC_CAL_6 0x14 #define AB8500_GPADC_CAL_7 0x15 +/* New calibration for 8540 */ +#define AB8540_GPADC_OTP4_REG_7 0x38 +#define AB8540_GPADC_OTP4_REG_6 0x39 +#define AB8540_GPADC_OTP4_REG_5 0x3A /* gpadc constants */ #define EN_VINTCORE12 0x04 @@ -67,6 +78,7 @@ #define GPADC_BUSY 0x01 #define EN_FALLING 0x10 #define EN_TRIG_EDGE 0x02 +#define EN_VBIAS_XTAL_TEMP 0x02 /* GPADC constants from AB8500 spec, UM0836 */ #define ADC_RESOLUTION 1024 @@ -85,8 +97,21 @@ #define ADC_CH_BKBAT_MIN 0 #define ADC_CH_BKBAT_MAX 3200 +/* GPADC constants from AB8540 spec */ +#define ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat*/ +#define ADC_CH_IBAT_MAX 6000 +#define ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat*/ +#define ADC_CH_IBAT_MAX_V 60 +#define IBAT_VDROP_L (-56) /* mV */ +#define IBAT_VDROP_H 56 + /* This is used to not lose precision when dividing to get gain and offset */ -#define CALIB_SCALE 1000 +#define CALIB_SCALE 1000 +/* + * Number of bits shift used to not lose precision + * when dividing to get ibat gain. + */ +#define CALIB_SHIFT_IBAT 20 /* Time in ms before disabling regulator */ #define GPADC_AUDOSUSPEND_DELAY 1 @@ -97,6 +122,7 @@ enum cal_channels { ADC_INPUT_VMAIN = 0, ADC_INPUT_BTEMP, ADC_INPUT_VBAT, + ADC_INPUT_IBAT, NBR_CAL_INPUTS, }; @@ -107,8 +133,8 @@ enum cal_channels { * @offset: Offset of the ADC channel */ struct adc_cal_data { - u64 gain; - u64 offset; + s64 gain; + s64 offset; }; /** @@ -180,6 +206,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE; break; + case XTAL_TEMP: case BAT_CTRL: case BTEMP_BALL: case ACC_DETECT1: @@ -198,6 +225,7 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, break; case MAIN_BAT_V: + case VBAT_TRUE_MEAS: /* For some reason we don't have calibrated data */ if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) { res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX - @@ -241,6 +269,20 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, ADC_RESOLUTION; break; + case IBAT_VIRTUAL_CHANNEL: + /* For some reason we don't have calibrated data */ + if (!gpadc->cal_data[ADC_INPUT_IBAT].gain) { + res = ADC_CH_IBAT_MIN + (ADC_CH_IBAT_MAX - + ADC_CH_IBAT_MIN) * ad_value / + ADC_RESOLUTION; + break; + } + /* Here we can use the calibrated data */ + res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_IBAT].gain + + gpadc->cal_data[ADC_INPUT_IBAT].offset) + >> CALIB_SHIFT_IBAT; + break; + default: dev_err(gpadc->dev, "unknown channel, not possible to convert\n"); @@ -303,10 +345,20 @@ EXPORT_SYMBOL(ab8500_gpadc_convert); */ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type) +{ + int raw_data; + raw_data = ab8500_gpadc_double_read_raw(gpadc, channel, + avg_sample, trig_edge, trig_timer, conv_type, NULL); + return raw_data; +} + +int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel, + u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type, + int *ibat) { int ret; int looplimit = 0; - u8 val, low_data, high_data; + u8 val, low_data, high_data, low_data2, high_data2; if (!gpadc) return -ENODEV; @@ -359,7 +411,6 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, default: val = channel | AVG_16; break; - } if (conv_type == ADC_HW) @@ -383,8 +434,8 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, ret = abx500_mask_and_set_register_interruptible(gpadc->dev, AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_FALLING, EN_FALLING); - } + switch (channel) { case MAIN_CHARGER_C: case USB_CHARGER_C: @@ -401,6 +452,55 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, EN_BUF | EN_ICHAR, EN_BUF | EN_ICHAR); break; + + case XTAL_TEMP: + if (conv_type == ADC_HW) + ret = abx500_mask_and_set_register_interruptible( + gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF | EN_TRIG_EDGE, + EN_BUF | EN_TRIG_EDGE); + else + ret = abx500_mask_and_set_register_interruptible( + gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF , + EN_BUF); + break; + + case VBAT_TRUE_MEAS: + if (conv_type == ADC_HW) + ret = abx500_mask_and_set_register_interruptible( + gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF | EN_TRIG_EDGE, + EN_BUF | EN_TRIG_EDGE); + else + ret = abx500_mask_and_set_register_interruptible( + gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF , + EN_BUF); + break; + + case BAT_CTRL_AND_IBAT: + case VBAT_MEAS_AND_IBAT: + case VBAT_TRUE_MEAS_AND_IBAT: + case BAT_TEMP_AND_IBAT: + if (conv_type == ADC_HW) + ret = abx500_mask_and_set_register_interruptible( + gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_TRIG_EDGE, + EN_TRIG_EDGE); + else + ret = abx500_mask_and_set_register_interruptible( + gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF, + 0); + break; + case BTEMP_BALL: if (!is_ab8500_2p0_or_earlier(gpadc->parent)) { if (conv_type == ADC_HW) @@ -471,21 +571,19 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, /* wait for completion of conversion */ if (conv_type == ADC_HW) { if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, - 2*HZ)) { - dev_err(gpadc->dev, - "timeout didn't receive" - " hw GPADC conv interrupt\n"); - ret = -EINVAL; - goto out; + 2 * HZ)) { + dev_err(gpadc->dev, + "timeout didn't receive hw GPADC conv interrupt\n"); + ret = -EINVAL; + goto out; } } else { if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, - msecs_to_jiffies(CONVERSION_TIME))) { - dev_err(gpadc->dev, - "timeout didn't receive" - " sw GPADC conv interrupt\n"); - ret = -EINVAL; - goto out; + msecs_to_jiffies(CONVERSION_TIME))) { + dev_err(gpadc->dev, + "timeout didn't receive sw GPADC conv interrupt\n"); + ret = -EINVAL; + goto out; } } @@ -523,6 +621,46 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, goto out; } } + /* Check if double convertion is required */ + if ((channel == BAT_CTRL_AND_IBAT) || + (channel == VBAT_MEAS_AND_IBAT) || + (channel == VBAT_TRUE_MEAS_AND_IBAT) || + (channel == BAT_TEMP_AND_IBAT)) { + + if (conv_type == ADC_HW) { + /* not supported */ + ret = -ENOTSUPP; + dev_err(gpadc->dev, + "gpadc_conversion: only SW double conversion supported\n"); + goto out; + } else { + /* Read the converted RAW data 2 */ + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG, + &low_data2); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw low data 2 failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG, + &high_data2); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw high data 2 failed\n"); + goto out; + } + if (ibat != NULL) { + *ibat = (high_data2 << 8) | low_data2; + } else { + dev_warn(gpadc->dev, + "gpadc_conversion: ibat not stored\n"); + } + + } + } /* Disable GPADC */ ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, @@ -586,15 +724,27 @@ static int otp_cal_regs[] = { AB8500_GPADC_CAL_7, }; +static int otp4_cal_regs[] = { + AB8540_GPADC_OTP4_REG_7, + AB8540_GPADC_OTP4_REG_6, + AB8540_GPADC_OTP4_REG_5, +}; + static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) { int i; int ret[ARRAY_SIZE(otp_cal_regs)]; u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)]; - + int ret_otp4[ARRAY_SIZE(otp4_cal_regs)]; + u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)]; int vmain_high, vmain_low; int btemp_high, btemp_low; int vbat_high, vbat_low; + int ibat_high, ibat_low; + s64 V_gain, V_offset, V2A_gain, V2A_offset; + struct ab8500 *ab8500; + + ab8500 = gpadc->parent; /* First we read all OTP registers and store the error code */ for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) { @@ -614,7 +764,7 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) * bt_h/l = btemp_high/low * vb_h/l = vbat_high/low * - * Data bits: + * Data bits 8500/9540: * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 * |.......|.......|.......|.......|.......|.......|.......|....... * | | vm_h9 | vm_h8 @@ -632,6 +782,35 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | * |.......|.......|.......|.......|.......|.......|.......|....... * + * Data bits 8540: + * OTP2 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * Data bits 8540: + * OTP4 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | ib_h9 | ib_h8 | ib_h7 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 | + * * * Ideal output ADC codes corresponding to injected input voltages * during manufacturing is: @@ -644,38 +823,96 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) * vbat_low: Vin = 2380mV / ADC ideal code = 33 */ - /* Calculate gain and offset for VMAIN if all reads succeeded */ - if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) { - vmain_high = (((gpadc_cal[0] & 0x03) << 8) | - ((gpadc_cal[1] & 0x3F) << 2) | - ((gpadc_cal[2] & 0xC0) >> 6)); + if (is_ab8540(ab8500)) { + /* Calculate gain and offset for VMAIN if all reads succeeded*/ + if (!(ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[1] & 0xFF) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * + 19500 - (CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0; + } - vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + /* Read IBAT calibration Data */ + for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) { + ret_otp4[i] = abx500_get_register_interruptible( + gpadc->dev, AB8500_OTP_EMUL, + otp4_cal_regs[i], &gpadc_otp4[i]); + if (ret_otp4[i] < 0) + dev_err(gpadc->dev, + "%s: read otp4 reg 0x%02x failed\n", + __func__, otp4_cal_regs[i]); + } - gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * - (19500 - 315) / (vmain_high - vmain_low); + /* Calculate gain and offset for IBAT if all reads succeeded */ + if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) { + ibat_high = (((gpadc_otp4[0] & 0x07) << 7) | + ((gpadc_otp4[1] & 0xFE) >> 1)); + ibat_low = (((gpadc_otp4[1] & 0x01) << 5) | + ((gpadc_otp4[2] & 0xF8) >> 3)); + + V_gain = ((IBAT_VDROP_H - IBAT_VDROP_L) + << CALIB_SHIFT_IBAT) / (ibat_high - ibat_low); + + V_offset = (IBAT_VDROP_H << CALIB_SHIFT_IBAT) - + (((IBAT_VDROP_H - IBAT_VDROP_L) << + CALIB_SHIFT_IBAT) / (ibat_high - ibat_low)) + * ibat_high; + /* + * Result obtained is in mV (at a scale factor), + * we need to calculate gain and offset to get mA + */ + V2A_gain = (ADC_CH_IBAT_MAX - ADC_CH_IBAT_MIN)/ + (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V); + V2A_offset = ((ADC_CH_IBAT_MAX_V * ADC_CH_IBAT_MIN - + ADC_CH_IBAT_MAX * ADC_CH_IBAT_MIN_V) + << CALIB_SHIFT_IBAT) + / (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V); + + gpadc->cal_data[ADC_INPUT_IBAT].gain = V_gain * V2A_gain; + gpadc->cal_data[ADC_INPUT_IBAT].offset = V_offset * + V2A_gain + V2A_offset; + } else { + gpadc->cal_data[ADC_INPUT_IBAT].gain = 0; + } - gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * 19500 - - (CALIB_SCALE * (19500 - 315) / - (vmain_high - vmain_low)) * vmain_high; + dev_dbg(gpadc->dev, "IBAT gain %llu offset %llu\n", + gpadc->cal_data[ADC_INPUT_IBAT].gain, + gpadc->cal_data[ADC_INPUT_IBAT].offset); } else { - gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0; + /* Calculate gain and offset for VMAIN if all reads succeeded */ + if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[0] & 0x03) << 8) | + ((gpadc_cal[1] & 0x3F) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + + gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * + 19500 - (CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0; + } } - /* Calculate gain and offset for BTEMP if all reads succeeded */ if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) { btemp_high = (((gpadc_cal[2] & 0x01) << 9) | - (gpadc_cal[3] << 1) | - ((gpadc_cal[4] & 0x80) >> 7)); - + (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7)); btemp_low = ((gpadc_cal[4] & 0x7C) >> 2); gpadc->cal_data[ADC_INPUT_BTEMP].gain = CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low); - gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 - - (CALIB_SCALE * (1300 - 21) / - (btemp_high - btemp_low)) * btemp_high; + (CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low)) + * btemp_high; } else { gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0; } @@ -687,7 +924,6 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE * (4700 - 2380) / (vbat_high - vbat_low); - gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 - (CALIB_SCALE * (4700 - 2380) / (vbat_high - vbat_low)) * vbat_high; diff --git a/include/linux/mfd/abx500/ab8500-gpadc.h b/include/linux/mfd/abx500/ab8500-gpadc.h index 7694e7ab188..4131437ace4 100644 --- a/include/linux/mfd/abx500/ab8500-gpadc.h +++ b/include/linux/mfd/abx500/ab8500-gpadc.h @@ -12,19 +12,32 @@ /* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2 * and ADCHwSel[4:0] in GPADCCtrl3 ) */ -#define BAT_CTRL 0x01 -#define BTEMP_BALL 0x02 -#define MAIN_CHARGER_V 0x03 -#define ACC_DETECT1 0x04 -#define ACC_DETECT2 0x05 -#define ADC_AUX1 0x06 -#define ADC_AUX2 0x07 -#define MAIN_BAT_V 0x08 -#define VBUS_V 0x09 -#define MAIN_CHARGER_C 0x0A -#define USB_CHARGER_C 0x0B -#define BK_BAT_V 0x0C -#define DIE_TEMP 0x0D +#define BAT_CTRL 0x01 +#define BTEMP_BALL 0x02 +#define MAIN_CHARGER_V 0x03 +#define ACC_DETECT1 0x04 +#define ACC_DETECT2 0x05 +#define ADC_AUX1 0x06 +#define ADC_AUX2 0x07 +#define MAIN_BAT_V 0x08 +#define VBUS_V 0x09 +#define MAIN_CHARGER_C 0x0A +#define USB_CHARGER_C 0x0B +#define BK_BAT_V 0x0C +#define DIE_TEMP 0x0D +#define USB_ID 0x0E +#define XTAL_TEMP 0x12 +#define VBAT_TRUE_MEAS 0x13 +#define BAT_CTRL_AND_IBAT 0x1C +#define VBAT_MEAS_AND_IBAT 0x1D +#define VBAT_TRUE_MEAS_AND_IBAT 0x1E +#define BAT_TEMP_AND_IBAT 0x1F + +/* Virtual channel used only for ibat convertion to ampere + * Battery current conversion (ibat) cannot be requested as a single conversion + * but it is always in combination with other input requests + */ +#define IBAT_VIRTUAL_CHANNEL 0xFF #define SAMPLE_1 1 #define SAMPLE_4 4 @@ -37,7 +50,6 @@ #define ADC_SW 0 #define ADC_HW 1 - struct ab8500_gpadc; struct ab8500_gpadc *ab8500_gpadc_get(char *name); @@ -51,6 +63,9 @@ static inline int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel) int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel, u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type); +int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel, + u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type, + int *ibat); int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, int ad_value); -- cgit v1.2.3-70-g09d2 From bc6b4132bcae4b8e59766ba2dae8f377009b26d0 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 26 Feb 2013 14:02:31 +0000 Subject: mfd: ab8500-debug: Add support for the AB8540 Allow GPADC debug information to be shown when executing on an AB8540 based platform. Signed-off-by: Alexandre Bourdiol Reviewed-by: Marcus COOPER Reviewed-by: Philippe LANGLAIS Acked-by: Samuel Ortiz --- drivers/mfd/ab8500-debugfs.c | 286 +++++++++++++++++++++++++++++++- drivers/mfd/ab8500-gpadc.c | 44 +++++ include/linux/mfd/abx500/ab8500-gpadc.h | 3 + 3 files changed, 332 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 074eea9e4bf..1e44d65e177 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -1633,6 +1633,254 @@ static const struct file_operations ab8500_gpadc_die_temp_fops = { .owner = THIS_MODULE, }; +static int ab8540_gpadc_xtal_temp_print(struct seq_file *s, void *p) +{ + int xtal_temp_raw; + int xtal_temp_convert; + struct ab8500_gpadc *gpadc; + + gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + xtal_temp_raw = ab8500_gpadc_read_raw(gpadc, XTAL_TEMP, + avg_sample, trig_edge, trig_timer, conv_type); + xtal_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, XTAL_TEMP, + xtal_temp_raw); + + return seq_printf(s, "%d,0x%X\n", + xtal_temp_convert, xtal_temp_raw); +} + +static int ab8540_gpadc_xtal_temp_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab8540_gpadc_xtal_temp_print, + inode->i_private); +} + +static const struct file_operations ab8540_gpadc_xtal_temp_fops = { + .open = ab8540_gpadc_xtal_temp_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab8540_gpadc_vbat_true_meas_print(struct seq_file *s, void *p) +{ + int vbat_true_meas_raw; + int vbat_true_meas_convert; + struct ab8500_gpadc *gpadc; + + gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + vbat_true_meas_raw = ab8500_gpadc_read_raw(gpadc, VBAT_TRUE_MEAS, + avg_sample, trig_edge, trig_timer, conv_type); + vbat_true_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBAT_TRUE_MEAS, + vbat_true_meas_raw); + + return seq_printf(s, "%d,0x%X\n", + vbat_true_meas_convert, vbat_true_meas_raw); +} + +static int ab8540_gpadc_vbat_true_meas_open(struct inode *inode, + struct file *file) +{ + return single_open(file, ab8540_gpadc_vbat_true_meas_print, + inode->i_private); +} + +static const struct file_operations ab8540_gpadc_vbat_true_meas_fops = { + .open = ab8540_gpadc_vbat_true_meas_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab8540_gpadc_bat_ctrl_and_ibat_print(struct seq_file *s, void *p) +{ + int bat_ctrl_raw; + int bat_ctrl_convert; + int ibat_raw; + int ibat_convert; + struct ab8500_gpadc *gpadc; + + gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + bat_ctrl_raw = ab8500_gpadc_double_read_raw(gpadc, BAT_CTRL_AND_IBAT, + avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw); + + bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, BAT_CTRL, + bat_ctrl_raw); + ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, + ibat_raw); + + return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n", + bat_ctrl_convert, bat_ctrl_raw, + ibat_convert, ibat_raw); +} + +static int ab8540_gpadc_bat_ctrl_and_ibat_open(struct inode *inode, + struct file *file) +{ + return single_open(file, ab8540_gpadc_bat_ctrl_and_ibat_print, + inode->i_private); +} + +static const struct file_operations ab8540_gpadc_bat_ctrl_and_ibat_fops = { + .open = ab8540_gpadc_bat_ctrl_and_ibat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab8540_gpadc_vbat_meas_and_ibat_print(struct seq_file *s, void *p) +{ + int vbat_meas_raw; + int vbat_meas_convert; + int ibat_raw; + int ibat_convert; + struct ab8500_gpadc *gpadc; + + gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + vbat_meas_raw = ab8500_gpadc_double_read_raw(gpadc, VBAT_MEAS_AND_IBAT, + avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw); + vbat_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V, + vbat_meas_raw); + ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, + ibat_raw); + + return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n", + vbat_meas_convert, vbat_meas_raw, + ibat_convert, ibat_raw); +} + +static int ab8540_gpadc_vbat_meas_and_ibat_open(struct inode *inode, + struct file *file) +{ + return single_open(file, ab8540_gpadc_vbat_meas_and_ibat_print, + inode->i_private); +} + +static const struct file_operations ab8540_gpadc_vbat_meas_and_ibat_fops = { + .open = ab8540_gpadc_vbat_meas_and_ibat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab8540_gpadc_vbat_true_meas_and_ibat_print(struct seq_file *s, void *p) +{ + int vbat_true_meas_raw; + int vbat_true_meas_convert; + int ibat_raw; + int ibat_convert; + struct ab8500_gpadc *gpadc; + + gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + vbat_true_meas_raw = ab8500_gpadc_double_read_raw(gpadc, + VBAT_TRUE_MEAS_AND_IBAT, avg_sample, trig_edge, + trig_timer, conv_type, &ibat_raw); + vbat_true_meas_convert = ab8500_gpadc_ad_to_voltage(gpadc, + VBAT_TRUE_MEAS, vbat_true_meas_raw); + ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, + ibat_raw); + + return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n", + vbat_true_meas_convert, vbat_true_meas_raw, + ibat_convert, ibat_raw); +} + +static int ab8540_gpadc_vbat_true_meas_and_ibat_open(struct inode *inode, + struct file *file) +{ + return single_open(file, ab8540_gpadc_vbat_true_meas_and_ibat_print, + inode->i_private); +} + +static const struct file_operations ab8540_gpadc_vbat_true_meas_and_ibat_fops = { + .open = ab8540_gpadc_vbat_true_meas_and_ibat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab8540_gpadc_bat_temp_and_ibat_print(struct seq_file *s, void *p) +{ + int bat_temp_raw; + int bat_temp_convert; + int ibat_raw; + int ibat_convert; + struct ab8500_gpadc *gpadc; + + gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + bat_temp_raw = ab8500_gpadc_double_read_raw(gpadc, BAT_TEMP_AND_IBAT, + avg_sample, trig_edge, trig_timer, conv_type, &ibat_raw); + bat_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL, + bat_temp_raw); + ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, + ibat_raw); + + return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n", + bat_temp_convert, bat_temp_raw, + ibat_convert, ibat_raw); +} + +static int ab8540_gpadc_bat_temp_and_ibat_open(struct inode *inode, + struct file *file) +{ + return single_open(file, ab8540_gpadc_bat_temp_and_ibat_print, + inode->i_private); +} + +static const struct file_operations ab8540_gpadc_bat_temp_and_ibat_fops = { + .open = ab8540_gpadc_bat_temp_and_ibat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab8540_gpadc_otp_cal_print(struct seq_file *s, void *p) +{ + struct ab8500_gpadc *gpadc; + u16 vmain_l, vmain_h, btemp_l, btemp_h; + u16 vbat_l, vbat_h, ibat_l, ibat_h; + + gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + ab8540_gpadc_get_otp(gpadc, &vmain_l, &vmain_h, &btemp_l, &btemp_h, + &vbat_l, &vbat_h, &ibat_l, &ibat_h); + return seq_printf(s, "VMAIN_L:0x%X\n" + "VMAIN_H:0x%X\n" + "BTEMP_L:0x%X\n" + "BTEMP_H:0x%X\n" + "VBAT_L:0x%X\n" + "VBAT_H:0x%X\n" + "IBAT_L:0x%X\n" + "IBAT_H:0x%X\n" + , + vmain_l, + vmain_h, + btemp_l, + btemp_h, + vbat_l, + vbat_h, + ibat_l, + ibat_h); +} + +static int ab8540_gpadc_otp_cal_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab8540_gpadc_otp_cal_print, inode->i_private); +} + +static const struct file_operations ab8540_gpadc_otp_calib_fops = { + .open = ab8540_gpadc_otp_cal_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + static int ab8500_gpadc_avg_sample_print(struct seq_file *s, void *p) { return seq_printf(s, "%d\n", avg_sample); @@ -2386,7 +2634,43 @@ static int ab8500_debug_probe(struct platform_device *plf) ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops); if (!file) goto err; - + if (is_ab8540(ab8500)) { + file = debugfs_create_file("xtal_temp", (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_xtal_temp_fops); + if (!file) + goto err; + file = debugfs_create_file("vbattruemeas", (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, &plf->dev, + &ab8540_gpadc_vbat_true_meas_fops); + if (!file) + goto err; + file = debugfs_create_file("batctrl_and_ibat", + (S_IRUGO | S_IWUGO), ab8500_gpadc_dir, + &plf->dev, &ab8540_gpadc_bat_ctrl_and_ibat_fops); + if (!file) + goto err; + file = debugfs_create_file("vbatmeas_and_ibat", + (S_IRUGO | S_IWUGO), ab8500_gpadc_dir, + &plf->dev, + &ab8540_gpadc_vbat_meas_and_ibat_fops); + if (!file) + goto err; + file = debugfs_create_file("vbattruemeas_and_ibat", + (S_IRUGO | S_IWUGO), ab8500_gpadc_dir, + &plf->dev, + &ab8540_gpadc_vbat_true_meas_and_ibat_fops); + if (!file) + goto err; + file = debugfs_create_file("battemp_and_ibat", + (S_IRUGO | S_IWUGO), ab8500_gpadc_dir, + &plf->dev, &ab8540_gpadc_bat_temp_and_ibat_fops); + if (!file) + goto err; + file = debugfs_create_file("otp_calib", (S_IRUGO | S_IWUGO), + ab8500_gpadc_dir, &plf->dev, &ab8540_gpadc_otp_calib_fops); + if (!file) + goto err; + } file = debugfs_create_file("avg_sample", (S_IRUGO | S_IWUGO), ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_avg_sample_fops); if (!file) diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index c985b90577f..e3535c74d5f 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -135,6 +135,8 @@ enum cal_channels { struct adc_cal_data { s64 gain; s64 offset; + u16 otp_calib_hi; + u16 otp_calib_lo; }; /** @@ -829,6 +831,12 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) vmain_high = (((gpadc_cal[1] & 0xFF) << 2) | ((gpadc_cal[2] & 0xC0) >> 6)); vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi = + (u16)vmain_high; + gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo = + (u16)vmain_low; + gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * (19500 - 315) / (vmain_high - vmain_low); gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * @@ -856,6 +864,11 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) ibat_low = (((gpadc_otp4[1] & 0x01) << 5) | ((gpadc_otp4[2] & 0xF8) >> 3)); + gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi = + (u16)ibat_high; + gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo = + (u16)ibat_low; + V_gain = ((IBAT_VDROP_H - IBAT_VDROP_L) << CALIB_SHIFT_IBAT) / (ibat_high - ibat_low); @@ -892,6 +905,11 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) ((gpadc_cal[2] & 0xC0) >> 6)); vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi = + (u16)vmain_high; + gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo = + (u16)vmain_low; + gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * (19500 - 315) / (vmain_high - vmain_low); @@ -902,12 +920,16 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0; } } + /* Calculate gain and offset for BTEMP if all reads succeeded */ if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) { btemp_high = (((gpadc_cal[2] & 0x01) << 9) | (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7)); btemp_low = ((gpadc_cal[4] & 0x7C) >> 2); + gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi = (u16)btemp_high; + gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo = (u16)btemp_low; + gpadc->cal_data[ADC_INPUT_BTEMP].gain = CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low); gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 - @@ -922,6 +944,9 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]); vbat_low = ((gpadc_cal[6] & 0xFC) >> 2); + gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi = (u16)vbat_high; + gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo = (u16)vbat_low; + gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE * (4700 - 2380) / (vbat_high - vbat_low); gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 - @@ -1131,6 +1156,25 @@ static void __exit ab8500_gpadc_exit(void) platform_driver_unregister(&ab8500_gpadc_driver); } +/** + * ab8540_gpadc_get_otp() - returns OTP values + * + */ +void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc, + u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h, + u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h) +{ + *vmain_l = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_lo; + *vmain_h = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi; + *btemp_l = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo; + *btemp_h = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi; + *vbat_l = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo; + *vbat_h = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi; + *ibat_l = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo; + *ibat_h = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi; + return ; +} + subsys_initcall_sync(ab8500_gpadc_init); module_exit(ab8500_gpadc_exit); diff --git a/include/linux/mfd/abx500/ab8500-gpadc.h b/include/linux/mfd/abx500/ab8500-gpadc.h index 4131437ace4..49ded001049 100644 --- a/include/linux/mfd/abx500/ab8500-gpadc.h +++ b/include/linux/mfd/abx500/ab8500-gpadc.h @@ -68,5 +68,8 @@ int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel, int *ibat); int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, int ad_value); +void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc, + u16 *vmain_l, u16 *vmain_h, u16 *btemp_l, u16 *btemp_h, + u16 *vbat_l, u16 *vbat_h, u16 *ibat_l, u16 *ibat_h); #endif /* _AB8500_GPADC_H */ -- cgit v1.2.3-70-g09d2 From 93ff722e88530b9719cbf53be4f3197722461394 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 31 May 2012 16:16:36 +0200 Subject: ab8500-fg: Add power cut feature for ab8505 and ab8540 Add support for a power cut feature which allows user to configure when ab8505 and ab8540 based platforms should shut down system due to low battery. Signed-off-by: Lee Jones --- drivers/mfd/ab8500-core.c | 36 +++ drivers/power/ab8500_bmdata.c | 5 + drivers/power/ab8500_fg.c | 474 +++++++++++++++++++++++++++++++++++ include/linux/mfd/abx500.h | 10 + include/linux/mfd/abx500/ab8500-bm.h | 18 ++ 5 files changed, 543 insertions(+) (limited to 'include') diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index c7ff55753a8..f276352cc9e 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -113,6 +113,7 @@ #define AB8500_SWITCH_OFF_STATUS 0x00 #define AB8500_TURN_ON_STATUS 0x00 +#define AB8505_TURN_ON_STATUS_2 0x04 #define AB8500_CH_USBCH_STAT1_REG 0x02 #define VBUS_DET_DBNC100 0x02 @@ -1401,6 +1402,21 @@ static ssize_t show_turn_on_status(struct device *dev, return sprintf(buf, "%#x\n", value); } +static ssize_t show_turn_on_status_2(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + u8 value; + struct ab8500 *ab8500; + + ab8500 = dev_get_drvdata(dev); + ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK, + AB8505_TURN_ON_STATUS_2, &value); + if (ret < 0) + return ret; + return sprintf(buf, "%#x\n", (value & 0x1)); +} + static ssize_t show_ab9540_dbbrstn(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1457,6 +1473,7 @@ exit: static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL); static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL); static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL); +static DEVICE_ATTR(turn_on_status_2, S_IRUGO, show_turn_on_status_2, NULL); static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR, show_ab9540_dbbrstn, store_ab9540_dbbrstn); @@ -1467,6 +1484,11 @@ static struct attribute *ab8500_sysfs_entries[] = { NULL, }; +static struct attribute *ab8505_sysfs_entries[] = { + &dev_attr_turn_on_status_2.attr, + NULL, +}; + static struct attribute *ab9540_sysfs_entries[] = { &dev_attr_chip_id.attr, &dev_attr_switch_off_status.attr, @@ -1479,6 +1501,10 @@ static struct attribute_group ab8500_attr_group = { .attrs = ab8500_sysfs_entries, }; +static struct attribute_group ab8505_attr_group = { + .attrs = ab8505_sysfs_entries, +}; + static struct attribute_group ab9540_attr_group = { .attrs = ab9540_sysfs_entries, }; @@ -1719,6 +1745,12 @@ static int ab8500_probe(struct platform_device *pdev) else ret = sysfs_create_group(&ab8500->dev->kobj, &ab8500_attr_group); + + if ((is_ab8505(ab8500) || is_ab9540(ab8500)) && + ab8500->chip_id >= AB8500_CUT2P0) + ret = sysfs_create_group(&ab8500->dev->kobj, + &ab8505_attr_group); + if (ret) dev_err(ab8500->dev, "error creating sysfs entries\n"); @@ -1735,6 +1767,10 @@ static int ab8500_remove(struct platform_device *pdev) else sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); + if ((is_ab8505(ab8500) || is_ab9540(ab8500)) && + ab8500->chip_id >= AB8500_CUT2P0) + sysfs_remove_group(&ab8500->dev->kobj, &ab8505_attr_group); + mfd_remove_devices(ab8500->dev); return 0; diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c index 7a96c0650fb..e8759763fbe 100644 --- a/drivers/power/ab8500_bmdata.c +++ b/drivers/power/ab8500_bmdata.c @@ -407,6 +407,11 @@ static const struct abx500_fg_parameters fg = { .battok_raising_th_sel1 = 2860, .maint_thres = 95, .user_cap_limit = 15, + .pcut_enable = 1, + .pcut_max_time = 127, + .pcut_flag_time = 112, + .pcut_max_restart = 15, + .pcut_debounce_time = 2, }; static const struct abx500_maxim_parameters maxi_params = { diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 25dae4c4b0e..92f342bcf18 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2344,6 +2344,50 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) dev_err(di->dev, "BattOk init write failed.\n"); goto out; } + + if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && + abx500_get_chip_id(di->dev) >= AB8500_CUT2P0) + || is_ab8540(di->parent)) { + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_MAX_TIME_REG, di->bm->fg_params->pcut_max_time); + + if (ret) { + dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__); + goto out; + }; + + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time); + + if (ret) { + dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__); + goto out; + }; + + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart); + + if (ret) { + dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__); + goto out; + }; + + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time); + + if (ret) { + dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__); + goto out; + }; + + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable); + + if (ret) { + dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__); + goto out; + }; + } out: return ret; } @@ -2546,6 +2590,428 @@ static int ab8500_fg_sysfs_init(struct ab8500_fg *di) return ret; } + +static ssize_t ab8505_powercut_flagtime_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_FLAG_TIME_REG, ®_value); + + if (ret < 0) { + dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n"); + goto fail; + } + + return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); + +fail: + return ret; +} + +static ssize_t ab8505_powercut_flagtime_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + long unsigned reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + reg_value = simple_strtoul(buf, NULL, 10); + + if (reg_value > 0x7F) { + dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n"); + goto fail; + } + + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value); + + if (ret < 0) + dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n"); + +fail: + return count; +} + +static ssize_t ab8505_powercut_maxtime_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_MAX_TIME_REG, ®_value); + + if (ret < 0) { + dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n"); + goto fail; + } + + return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); + +fail: + return ret; + +} + +static ssize_t ab8505_powercut_maxtime_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + reg_value = simple_strtoul(buf, NULL, 10); + if (reg_value > 0x7F) { + dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n"); + goto fail; + } + + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value); + + if (ret < 0) + dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n"); + +fail: + return count; +} + +static ssize_t ab8505_powercut_restart_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_RESTART_REG, ®_value); + + if (ret < 0) { + dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n"); + goto fail; + } + + return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF)); + +fail: + return ret; +} + +static ssize_t ab8505_powercut_restart_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + reg_value = simple_strtoul(buf, NULL, 10); + if (reg_value > 0xF) { + dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n"); + goto fail; + } + + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_RESTART_REG, (u8)reg_value); + + if (ret < 0) + dev_err(dev, "Failed to set AB8505_RTC_PCUT_RESTART_REG\n"); + +fail: + return count; + +} + +static ssize_t ab8505_powercut_timer_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_TIME_REG, ®_value); + + if (ret < 0) { + dev_err(dev, "Failed to read AB8505_RTC_PCUT_TIME_REG\n"); + goto fail; + } + + return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); + +fail: + return ret; +} + +static ssize_t ab8505_powercut_restart_counter_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_RESTART_REG, ®_value); + + if (ret < 0) { + dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n"); + goto fail; + } + + return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4); + +fail: + return ret; +} + +static ssize_t ab8505_powercut_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); + + if (ret < 0) + goto fail; + + return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1)); + +fail: + return ret; +} + +static ssize_t ab8505_powercut_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + reg_value = simple_strtoul(buf, NULL, 10); + if (reg_value > 0x1) { + dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n"); + goto fail; + } + + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_CTL_STATUS_REG, (u8)reg_value); + + if (ret < 0) + dev_err(dev, "Failed to set AB8505_RTC_PCUT_CTL_STATUS_REG\n"); + +fail: + return count; +} + +static ssize_t ab8505_powercut_flag_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + + int ret; + u8 reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); + + if (ret < 0) { + dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n"); + goto fail; + } + + return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4)); + +fail: + return ret; +} + +static ssize_t ab8505_powercut_debounce_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_DEBOUNCE_REG, ®_value); + + if (ret < 0) { + dev_err(dev, "Failed to read AB8505_RTC_PCUT_DEBOUNCE_REG\n"); + goto fail; + } + + return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7)); + +fail: + return ret; +} + +static ssize_t ab8505_powercut_debounce_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + reg_value = simple_strtoul(buf, NULL, 10); + if (reg_value > 0x7) { + dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n"); + goto fail; + } + + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_DEBOUNCE_REG, (u8)reg_value); + + if (ret < 0) + dev_err(dev, "Failed to set AB8505_RTC_PCUT_DEBOUNCE_REG\n"); + +fail: + return count; +} + +static ssize_t ab8505_powercut_enable_status_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 reg_value; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, + AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); + + if (ret < 0) { + dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n"); + goto fail; + } + + return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5)); + +fail: + return ret; +} + +static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = { + __ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP), + ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write), + __ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP), + ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write), + __ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP), + ab8505_powercut_restart_read, ab8505_powercut_restart_write), + __ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL), + __ATTR(powercut_restart_counter, S_IRUGO, + ab8505_powercut_restart_counter_read, NULL), + __ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP), + ab8505_powercut_read, ab8505_powercut_write), + __ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL), + __ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP), + ab8505_powercut_debounce_read, ab8505_powercut_debounce_write), + __ATTR(powercut_enable_status, S_IRUGO, + ab8505_powercut_enable_status_read, NULL), +}; + +static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev) +{ + unsigned int i, j; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && + abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0) + || is_ab8540(di->parent)) { + for (j = 0; j < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); j++) + if (device_create_file(dev, &ab8505_fg_sysfs_psy_attrs[j])) + goto sysfs_psy_create_attrs_failed_ab8505; + } + return 0; +sysfs_psy_create_attrs_failed_ab8505: + dev_err(dev, "Failed creating sysfs psy attrs for ab8505.\n"); + while (j--) + device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]); + + return -EIO; +} + +static void ab8500_fg_sysfs_psy_remove_attrs(struct device *dev) +{ + unsigned int i; + struct power_supply *psy = dev_get_drvdata(dev); + struct ab8500_fg *di; + + di = to_ab8500_fg_device_info(psy); + + if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && + abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0) + || is_ab8540(di->parent)) { + for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) + (void)device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]); + } +} + /* Exposure to the sysfs interface <> */ #if defined(CONFIG_PM) @@ -2607,6 +3073,7 @@ static int ab8500_fg_remove(struct platform_device *pdev) ab8500_fg_sysfs_exit(di); flush_scheduled_work(); + ab8500_fg_sysfs_psy_remove_attrs(di->fg_psy.dev); power_supply_unregister(&di->fg_psy); platform_set_drvdata(pdev, NULL); return ret; @@ -2772,6 +3239,13 @@ static int ab8500_fg_probe(struct platform_device *pdev) goto free_irq; } + ret = ab8500_fg_sysfs_psy_create_attrs(di->fg_psy.dev); + if (ret) { + dev_err(di->dev, "failed to create FG psy\n"); + ab8500_fg_sysfs_exit(di); + goto free_irq; + } + /* Calibrate the fg first time */ di->flags.calibrate = true; di->calib_state = AB8500_FG_CALIB_INIT; diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 9ead60bc66b..188aedc322c 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -89,6 +89,11 @@ struct abx500_fg; * points. * @maint_thres This is the threshold where we stop reporting * battery full while in maintenance, in per cent + * @pcut_enable: Enable power cut feature in ab8505 + * @pcut_max_time: Max time threshold + * @pcut_flag_time: Flagtime threshold + * @pcut_max_restart: Max number of restarts + * @pcut_debounce_time: Sets battery debounce time */ struct abx500_fg_parameters { int recovery_sleep_timer; @@ -106,6 +111,11 @@ struct abx500_fg_parameters { int battok_raising_th_sel1; int user_cap_limit; int maint_thres; + bool pcut_enable; + u8 pcut_max_time; + u8 pcut_flag_time; + u8 pcut_max_restart; + u8 pcut_debounce_time; }; /** diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h index 8d35bfe164c..0efbe0efee7 100644 --- a/include/linux/mfd/abx500/ab8500-bm.h +++ b/include/linux/mfd/abx500/ab8500-bm.h @@ -235,6 +235,14 @@ /* Battery type */ #define BATTERY_UNKNOWN 00 +/* Registers for pcut feature in ab8505 and ab9540 */ +#define AB8505_RTC_PCUT_CTL_STATUS_REG 0x12 +#define AB8505_RTC_PCUT_TIME_REG 0x13 +#define AB8505_RTC_PCUT_MAX_TIME_REG 0x14 +#define AB8505_RTC_PCUT_FLAG_TIME_REG 0x15 +#define AB8505_RTC_PCUT_RESTART_REG 0x16 +#define AB8505_RTC_PCUT_DEBOUNCE_REG 0x17 + /** * struct res_to_temp - defines one point in a temp to res curve. To * be used in battery packs that combines the identification resistor with a @@ -283,6 +291,11 @@ struct ab8500_fg; * points. * @maint_thres This is the threshold where we stop reporting * battery full while in maintenance, in per cent + * @pcut_enable: Enable power cut feature in ab8505 + * @pcut_max_time: Max time threshold + * @pcut_flag_time: Flagtime threshold + * @pcut_max_restart: Max number of restarts + * @pcut_debunce_time: Sets battery debounce time */ struct ab8500_fg_parameters { int recovery_sleep_timer; @@ -299,6 +312,11 @@ struct ab8500_fg_parameters { int battok_raising_th_sel1; int user_cap_limit; int maint_thres; + bool pcut_enable; + u8 pcut_max_time; + u8 pcut_flag_time; + u8 pcut_max_restart; + u8 pcut_debunce_time; }; /** -- cgit v1.2.3-70-g09d2 From 0f4aa401853e07885707aedfc68c608051b0d6e4 Mon Sep 17 00:00:00 2001 From: Yang QU Date: Tue, 26 Jun 2012 19:25:52 +0800 Subject: ab8500-charger: Add backup battery charge voltages on the ab8540 Add 2.7v, 2.9v, 3.0v, 3.2v and 3.3v charging voltages for backup battery. Before that only 2.5v, 2.6v, 2.8v, 3.1v were available. Signed-off-by: Yang QU Signed-off-by: Lee Jones Reviewed-by: Maxime COQUELIN Reviewed-by: Marcus COOPER Tested-by: Xiao Mei ZHANG --- drivers/power/ab8500_charger.c | 19 +++++++++++++++++-- include/linux/mfd/abx500/ab8500-bm.h | 24 ++++++++++++++++++++---- 2 files changed, 37 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 24b30b7ea5c..fd3fa2beca2 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -2836,6 +2836,7 @@ static int ab8500_charger_usb_get_property(struct power_supply *psy, static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) { int ret = 0; + u8 bup_vch_range = 0, vbup33_vrtcn = 0; /* Setup maximum charger current and voltage for ABB cut2.0 */ if (!is_ab8500_1p1_or_earlier(di->parent)) { @@ -2945,15 +2946,29 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) } /* Backup battery voltage and current */ + if (di->bm->bkup_bat_v > BUP_VCH_SEL_3P1V) + bup_vch_range = BUP_VCH_RANGE; + if (di->bm->bkup_bat_v == BUP_VCH_SEL_3P3V) + vbup33_vrtcn = VBUP33_VRTCN; + ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, AB8500_RTC_BACKUP_CHG_REG, - di->bm->bkup_bat_v | - di->bm->bkup_bat_i); + (di->bm->bkup_bat_v & 0x3) | di->bm->bkup_bat_i); if (ret) { dev_err(di->dev, "failed to setup backup battery charging\n"); goto out; } + if (is_ab8540(di->parent)) { + ret = abx500_set_register_interruptible(di->dev, + AB8500_RTC, + AB8500_RTC_CTRL1_REG, + bup_vch_range | vbup33_vrtcn); + if (ret) { + dev_err(di->dev, "failed to setup backup battery charging\n"); + goto out; + } + } /* Enable backup battery charging */ abx500_mask_and_set_register_interruptible(di->dev, diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h index 0efbe0efee7..a73e05a0441 100644 --- a/include/linux/mfd/abx500/ab8500-bm.h +++ b/include/linux/mfd/abx500/ab8500-bm.h @@ -105,6 +105,7 @@ #define AB8500_RTC_BACKUP_CHG_REG 0x0C #define AB8500_RTC_CC_CONF_REG 0x01 #define AB8500_RTC_CTRL_REG 0x0B +#define AB8500_RTC_CTRL1_REG 0x11 /* * OTP register offsets @@ -179,10 +180,25 @@ #define BUP_ICH_SEL_300UA 0x08 #define BUP_ICH_SEL_700UA 0x0C -#define BUP_VCH_SEL_2P5V 0x00 -#define BUP_VCH_SEL_2P6V 0x01 -#define BUP_VCH_SEL_2P8V 0x02 -#define BUP_VCH_SEL_3P1V 0x03 +enum bup_vch_sel { + BUP_VCH_SEL_2P5V, + BUP_VCH_SEL_2P6V, + BUP_VCH_SEL_2P8V, + BUP_VCH_SEL_3P1V, + /* + * Note that the following 5 values 2.7v, 2.9v, 3.0v, 3.2v, 3.3v + * are only available on ab8540. You can't choose these 5 + * voltage on ab8500/ab8505/ab9540. + */ + BUP_VCH_SEL_2P7V, + BUP_VCH_SEL_2P9V, + BUP_VCH_SEL_3P0V, + BUP_VCH_SEL_3P2V, + BUP_VCH_SEL_3P3V, +}; + +#define BUP_VCH_RANGE 0x02 +#define VBUP33_VRTCN 0x01 /* Battery OVV constants */ #define BATT_OVV_ENA 0x02 -- cgit v1.2.3-70-g09d2 From 4dcdf57773fd45b483fc7613b9e51b89a57d655c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 14 Feb 2013 09:24:10 +0000 Subject: ab8500-bm: Quick re-attach charging behaviour Due to a bug in some AB8500 ASICs charger removal cannot always be detected if the removal and reinsertion is done to close in time. This patch detects above described case and handles the situation so that charging will be kept turned on. Signed-off-by: Lee Jones --- drivers/power/ab8500_charger.c | 105 +++++++++++++++++++++++++++++- drivers/power/abx500_chargalg.c | 33 ++++++++++ include/linux/mfd/abx500/ux500_chargalg.h | 1 + 3 files changed, 137 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index dcd3c6feca9..3eb23cf9ff4 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -52,6 +52,7 @@ #define VBUS_DET_DBNC100 0x02 #define VBUS_DET_DBNC1 0x01 #define OTP_ENABLE_WD 0x01 +#define DROP_COUNT_RESET 0x01 #define MAIN_CH_INPUT_CURR_SHIFT 4 #define VBUS_IN_CURR_LIM_SHIFT 4 @@ -1677,6 +1678,105 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, return ret; } +/** + * ab8500_charger_usb_check_enable() - enable usb charging + * @charger: pointer to the ux500_charger structure + * @vset: charging voltage + * @iset: charger output current + * + * Check if the VBUS charger has been disconnected and reconnected without + * AB8500 rising an interrupt. Returns 0 on success. + */ +static int ab8500_charger_usb_check_enable(struct ux500_charger *charger, + int vset, int iset) +{ + u8 usbch_ctrl1 = 0; + int ret = 0; + + struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger); + + if (!di->usb.charger_connected) + return ret; + + ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, + AB8500_USBCH_CTRL1_REG, &usbch_ctrl1); + if (ret < 0) { + dev_err(di->dev, "ab8500 read failed %d\n", __LINE__); + return ret; + } + dev_dbg(di->dev, "USB charger ctrl: 0x%02x\n", usbch_ctrl1); + + if (!(usbch_ctrl1 & USB_CH_ENA)) { + dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n"); + + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_CHARGER_CTRL, + DROP_COUNT_RESET, DROP_COUNT_RESET); + if (ret < 0) { + dev_err(di->dev, "ab8500 write failed %d\n", __LINE__); + return ret; + } + + ret = ab8500_charger_usb_en(&di->usb_chg, true, vset, iset); + if (ret < 0) { + dev_err(di->dev, "Failed to enable VBUS charger %d\n", + __LINE__); + return ret; + } + } + return ret; +} + +/** + * ab8500_charger_ac_check_enable() - enable usb charging + * @charger: pointer to the ux500_charger structure + * @vset: charging voltage + * @iset: charger output current + * + * Check if the AC charger has been disconnected and reconnected without + * AB8500 rising an interrupt. Returns 0 on success. + */ +static int ab8500_charger_ac_check_enable(struct ux500_charger *charger, + int vset, int iset) +{ + u8 mainch_ctrl1 = 0; + int ret = 0; + + struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger); + + if (!di->ac.charger_connected) + return ret; + + ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, + AB8500_MCH_CTRL1, &mainch_ctrl1); + if (ret < 0) { + dev_err(di->dev, "ab8500 read failed %d\n", __LINE__); + return ret; + } + dev_dbg(di->dev, "AC charger ctrl: 0x%02x\n", mainch_ctrl1); + + if (!(mainch_ctrl1 & MAIN_CH_ENA)) { + dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n"); + + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_CHARGER_CTRL, + DROP_COUNT_RESET, DROP_COUNT_RESET); + + if (ret < 0) { + dev_err(di->dev, "ab8500 write failed %d\n", __LINE__); + return ret; + } + + ret = ab8500_charger_ac_en(&di->usb_chg, true, vset, iset); + if (ret < 0) { + dev_err(di->dev, "failed to enable AC charger %d\n", + __LINE__); + return ret; + } + } + return ret; +} + /** * ab8500_charger_watchdog_kick() - kick charger watchdog * @di: pointer to the ab8500_charger structure @@ -1734,8 +1834,7 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger, /* Reset the main and usb drop input current measurement counter */ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER, - AB8500_CHARGER_CTRL, - 0x1); + AB8500_CHARGER_CTRL, DROP_COUNT_RESET); if (ret) { dev_err(di->dev, "%s write failed\n", __func__); return ret; @@ -3221,6 +3320,7 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->ac_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface), /* ux500_charger sub-class */ di->ac_chg.ops.enable = &ab8500_charger_ac_en; + di->ac_chg.ops.check_enable = &ab8500_charger_ac_check_enable; di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current; di->ac_chg.max_out_volt = ab8500_charger_voltage_map[ @@ -3242,6 +3342,7 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface), /* ux500_charger sub-class */ di->usb_chg.ops.enable = &ab8500_charger_usb_en; + di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable; di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current; di->usb_chg.max_out_volt = ab8500_charger_voltage_map[ diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 31507bfe549..8ab65a3a819 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -305,6 +305,30 @@ static void abx500_chargalg_state_to(struct abx500_chargalg *di, di->charge_state = state; } +static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di) +{ + switch (di->charge_state) { + case STATE_NORMAL: + case STATE_MAINTENANCE_A: + case STATE_MAINTENANCE_B: + break; + default: + return 0; + } + + if (di->chg_info.charger_type & USB_CHG) { + return di->usb_chg->ops.check_enable(di->usb_chg, + di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, + di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); + } else if ((di->chg_info.charger_type & AC_CHG) && + !(di->ac_chg->external)) { + return di->ac_chg->ops.check_enable(di->ac_chg, + di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, + di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); + } + return 0; +} + /** * abx500_chargalg_check_charger_connection() - Check charger connection change * @di: pointer to the abx500_chargalg structure @@ -1219,6 +1243,7 @@ static void abx500_chargalg_external_power_changed(struct power_supply *psy) static void abx500_chargalg_algorithm(struct abx500_chargalg *di) { int charger_status; + int ret; /* Collect data from all power_supply class devices */ class_for_each_device(power_supply_class, NULL, @@ -1229,6 +1254,14 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) abx500_chargalg_check_charger_voltage(di); charger_status = abx500_chargalg_check_charger_connection(di); + + if (is_ab8500(di->parent)) { + ret = abx500_chargalg_check_charger_enable(di); + if (ret < 0) + dev_err(di->dev, "Checking charger is enabled error" + ": Returned Value %d\n", ret); + } + /* * First check if we have a charger connected. * Also we don't allow charging of unknown batteries if configured diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h index d43ac0f3552..110d12f0954 100644 --- a/include/linux/mfd/abx500/ux500_chargalg.h +++ b/include/linux/mfd/abx500/ux500_chargalg.h @@ -17,6 +17,7 @@ struct ux500_charger; struct ux500_charger_ops { int (*enable) (struct ux500_charger *, int, int, int); + int (*check_enable) (struct ux500_charger *, int, int); int (*kick_wd) (struct ux500_charger *); int (*update_curr) (struct ux500_charger *, int); }; -- cgit v1.2.3-70-g09d2 From 8891716e24d7b0f4b1c3b4fdff641bcb1fb282c4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 13 Feb 2013 11:39:19 +0000 Subject: ab8500-bm: Charge only mode fixes for the ab9540 Fix for charging not getting enabled in charge only mode by external charger. Signed-off-by: Lee Jones --- drivers/power/ab8500_charger.c | 42 +++++++++++++++++++++++++++++++ drivers/power/abx500_chargalg.c | 14 +++++++++++ drivers/power/pm2301_charger.c | 7 ++++++ include/linux/mfd/abx500/ux500_chargalg.h | 2 ++ 4 files changed, 65 insertions(+) (limited to 'include') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 3eb23cf9ff4..f1d712308b0 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,10 @@ #define AB8500_SW_CONTROL_FALLBACK 0x03 /* Wait for enumeration before charing in us */ #define WAIT_ACA_RID_ENUMERATION (5 * 1000) +/*External charger control*/ +#define AB8500_SYS_CHARGER_CONTROL_REG 0x52 +#define EXTERNAL_CHARGER_DISABLE_REG_VAL 0x03 +#define EXTERNAL_CHARGER_ENABLE_REG_VAL 0x07 /* UsbLineStatus register - usb types */ enum ab8500_charger_link_status { @@ -1678,6 +1683,29 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, return ret; } +static int ab8500_external_charger_prepare(struct notifier_block *charger_nb, + unsigned long event, void *data) +{ + int ret; + struct device *dev = data; + /*Toggle External charger control pin*/ + ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK, + AB8500_SYS_CHARGER_CONTROL_REG, + EXTERNAL_CHARGER_DISABLE_REG_VAL); + if (ret < 0) { + dev_err(dev, "write reg failed %d\n", ret); + goto out; + } + ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK, + AB8500_SYS_CHARGER_CONTROL_REG, + EXTERNAL_CHARGER_ENABLE_REG_VAL); + if (ret < 0) + dev_err(dev, "Write reg failed %d\n", ret); + +out: + return ret; +} + /** * ab8500_charger_usb_check_enable() - enable usb charging * @charger: pointer to the ux500_charger structure @@ -3221,6 +3249,10 @@ static int ab8500_charger_suspend(struct platform_device *pdev, #define ab8500_charger_resume NULL #endif +static struct notifier_block charger_nb = { + .notifier_call = ab8500_external_charger_prepare, +}; + static int ab8500_charger_remove(struct platform_device *pdev) { struct ab8500_charger *di = platform_get_drvdata(pdev); @@ -3250,6 +3282,11 @@ static int ab8500_charger_remove(struct platform_device *pdev) /* Delete the work queue */ destroy_workqueue(di->charger_wq); + /* Unregister external charger enable notifier */ + if (!di->ac_chg.enabled) + blocking_notifier_chain_unregister( + &charger_notifier_list, &charger_nb); + flush_scheduled_work(); if (di->usb_chg.enabled) power_supply_unregister(&di->usb_chg.psy); @@ -3331,6 +3368,11 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->ac_chg.enabled = di->bm->ac_enabled; di->ac_chg.external = false; + /*notifier for external charger enabling*/ + if (!di->ac_chg.enabled) + blocking_notifier_chain_register( + &charger_notifier_list, &charger_nb); + /* USB supply */ /* power_supply base class */ di->usb_chg.psy.name = "ab8500_usb"; diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 8ab65a3a819..a876976678a 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -26,6 +26,7 @@ #include #include #include +#include /* Watchdog kick interval */ #define CHG_WD_INTERVAL (6 * HZ) @@ -243,6 +244,9 @@ struct abx500_chargalg { struct kobject chargalg_kobject; }; +/*External charger prepare notifier*/ +BLOCKING_NOTIFIER_HEAD(charger_notifier_list); + /* Main battery properties */ static enum power_supply_property abx500_chargalg_props[] = { POWER_SUPPLY_PROP_STATUS, @@ -503,6 +507,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di) static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable, int vset, int iset) { + static int abx500_chargalg_ex_ac_enable_toggle; + if (!di->ac_chg || !di->ac_chg->ops.enable) return -ENXIO; @@ -515,6 +521,14 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable, di->chg_info.ac_iset = iset; di->chg_info.ac_vset = vset; + /* Enable external charger */ + if (enable && di->ac_chg->external && + !abx500_chargalg_ex_ac_enable_toggle) { + blocking_notifier_call_chain(&charger_notifier_list, + 0, di->dev); + abx500_chargalg_ex_ac_enable_toggle++; + } + return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset); } diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index b560fa5ac4e..45ef3b9de6b 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -1059,6 +1059,13 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, ret = pm2xxx_charger_detection(pm2, &val); if ((ret == 0) && val) { + /* + * When boot is due to AC charger plug-in, + * read interrupt registers + */ + pm2xxx_reg_read(pm2, PM2XXX_REG_INT1, &val); + pm2xxx_reg_read(pm2, PM2XXX_REG_INT2, &val); + pm2xxx_reg_read(pm2, PM2XXX_REG_INT4, &val); pm2->ac.charger_connected = 1; pm2->ac_conn = true; power_supply_changed(&pm2->ac_chg.psy); diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h index 110d12f0954..fa831f1e8cf 100644 --- a/include/linux/mfd/abx500/ux500_chargalg.h +++ b/include/linux/mfd/abx500/ux500_chargalg.h @@ -41,4 +41,6 @@ struct ux500_charger { bool external; }; +extern struct blocking_notifier_head charger_notifier_list; + #endif -- cgit v1.2.3-70-g09d2 From db43e6c473b57d4e7a55c4bd6edef71f40f13eae Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 14 Feb 2013 12:39:15 +0000 Subject: ab8500-bm: Add usb power path support AB8540 supports power path function in USB charging mode for fast power up with dead and weak battery, and it could extend the battery age. When USB charging starts, if the Vbattrue is below than SW cut off voltage, power path and pre-charge should be enabled. If Vbattrue is higher than SW cut off voltage, power path and pre-charge should be disabled. This is to make sure full current to battery charge. At the end of charge, power path should be enable again to reduce charging the battery again. Signed-off-by: Lee Jones --- drivers/power/ab8500_charger.c | 81 +++++++++++++++++++++++++++++++ drivers/power/abx500_chargalg.c | 62 +++++++++++++++++++++++ include/linux/mfd/abx500.h | 1 + include/linux/mfd/abx500/ab8500-bm.h | 12 +++++ include/linux/mfd/abx500/ux500_chargalg.h | 4 ++ 5 files changed, 160 insertions(+) (limited to 'include') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 6fea4fdf870..f249a65b02e 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -1925,6 +1925,67 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger, return ret; } +/** + * ab8540_charger_power_path_enable() - enable usb power path mode + * @charger: pointer to the ux500_charger structure + * @enable: enable/disable flag + * + * Enable or disable the power path for usb mode + * Returns error code in case of failure else 0(on success) + */ +static int ab8540_charger_power_path_enable(struct ux500_charger *charger, + bool enable) +{ + int ret; + struct ab8500_charger *di; + + if (charger->psy.type == POWER_SUPPLY_TYPE_USB) + di = to_ab8500_charger_usb_device_info(charger); + else + return -ENXIO; + + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8540_USB_PP_MODE_REG, + BUS_POWER_PATH_MODE_ENA, enable); + if (ret) { + dev_err(di->dev, "%s write failed\n", __func__); + return ret; + } + + return ret; +} + + +/** + * ab8540_charger_usb_pre_chg_enable() - enable usb pre change + * @charger: pointer to the ux500_charger structure + * @enable: enable/disable flag + * + * Enable or disable the pre-chage for usb mode + * Returns error code in case of failure else 0(on success) + */ +static int ab8540_charger_usb_pre_chg_enable(struct ux500_charger *charger, + bool enable) +{ + int ret; + struct ab8500_charger *di; + + if (charger->psy.type == POWER_SUPPLY_TYPE_USB) + di = to_ab8500_charger_usb_device_info(charger); + else + return -ENXIO; + + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8540_USB_PP_CHR_REG, + BUS_POWER_PATH_PRECHG_ENA, enable); + if (ret) { + dev_err(di->dev, "%s write failed\n", __func__); + return ret; + } + + return ret; +} + static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data) { struct power_supply *psy; @@ -3201,6 +3262,23 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) if (ret < 0) dev_err(di->dev, "%s mask and set failed\n", __func__); + if (is_ab8540(di->parent)) { + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8540_USB_PP_MODE_REG, + BUS_VSYS_VOL_SELECT_MASK, BUS_VSYS_VOL_SELECT_3P6V); + if (ret) { + dev_err(di->dev, "failed to setup usb power path vsys voltage\n"); + goto out; + } + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8540_USB_PP_CHR_REG, + BUS_PP_PRECHG_CURRENT_MASK, 0); + if (ret) { + dev_err(di->dev, "failed to setup usb power path prechage current\n"); + goto out; + } + } + out: return ret; } @@ -3484,6 +3562,8 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable; di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current; + di->usb_chg.ops.pp_enable = &ab8540_charger_power_path_enable; + di->usb_chg.ops.pre_chg_enable = &ab8540_charger_usb_pre_chg_enable; di->usb_chg.max_out_volt = ab8500_charger_voltage_map[ ARRAY_SIZE(ab8500_charger_voltage_map) - 1]; di->usb_chg.max_out_curr = ab8500_charger_current_map[ @@ -3491,6 +3571,7 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.wdt_refresh = CHG_WD_INTERVAL; di->usb_chg.enabled = di->bm->usb_enabled; di->usb_chg.external = false; + di->usb_chg.power_path = di->bm->usb_power_path; di->usb_state.usb_current = -1; /* Create a work queue for the charger */ diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index a876976678a..a9b8efdafb8 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -34,6 +34,9 @@ /* End-of-charge criteria counter */ #define EOC_COND_CNT 10 +/* Plus margin for the low battery threshold */ +#define BAT_PLUS_MARGIN (100) + #define to_abx500_chargalg_device_info(x) container_of((x), \ struct abx500_chargalg, chargalg_psy); @@ -83,6 +86,7 @@ enum abx500_chargalg_states { STATE_HW_TEMP_PROTECT_INIT, STATE_HW_TEMP_PROTECT, STATE_NORMAL_INIT, + STATE_USB_PP_PRE_CHARGE, STATE_NORMAL, STATE_WAIT_FOR_RECHARGE_INIT, STATE_WAIT_FOR_RECHARGE, @@ -114,6 +118,7 @@ static const char *states[] = { "HW_TEMP_PROTECT_INIT", "HW_TEMP_PROTECT", "NORMAL_INIT", + "USB_PP_PRE_CHARGE", "NORMAL", "WAIT_FOR_RECHARGE_INIT", "WAIT_FOR_RECHARGE", @@ -560,6 +565,37 @@ static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable, return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset); } + /** + * ab8540_chargalg_usb_pp_en() - Enable/ disable USB power path + * @di: pointer to the abx500_chargalg structure + * @enable: power path enable/disable + * + * The USB power path will be enable/ disable + */ +static int ab8540_chargalg_usb_pp_en(struct abx500_chargalg *di, bool enable) +{ + if (!di->usb_chg || !di->usb_chg->ops.pp_enable) + return -ENXIO; + + return di->usb_chg->ops.pp_enable(di->usb_chg, enable); +} + +/** + * ab8540_chargalg_usb_pre_chg_en() - Enable/ disable USB pre-charge + * @di: pointer to the abx500_chargalg structure + * @enable: USB pre-charge enable/disable + * + * The USB USB pre-charge will be enable/ disable + */ +static int ab8540_chargalg_usb_pre_chg_en(struct abx500_chargalg *di, + bool enable) +{ + if (!di->usb_chg || !di->usb_chg->ops.pre_chg_enable) + return -ENXIO; + + return di->usb_chg->ops.pre_chg_enable(di->usb_chg, enable); +} + /** * abx500_chargalg_update_chg_curr() - Update charger current * @di: pointer to the abx500_chargalg structure @@ -765,6 +801,9 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di) di->batt_data.avg_curr > 0) { if (++di->eoc_cnt >= EOC_COND_CNT) { di->eoc_cnt = 0; + if ((di->chg_info.charger_type & USB_CHG) && + (di->usb_chg->power_path)) + ab8540_chargalg_usb_pp_en(di, true); di->charge_status = POWER_SUPPLY_STATUS_FULL; di->maintenance_chg = true; dev_dbg(di->dev, "EOC reached!\n"); @@ -1465,6 +1504,22 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) break; case STATE_NORMAL_INIT: + if ((di->chg_info.charger_type & USB_CHG) && + di->usb_chg->power_path) { + if (di->batt_data.volt > + (di->bm->fg_params->lowbat_threshold + + BAT_PLUS_MARGIN)) { + ab8540_chargalg_usb_pre_chg_en(di, false); + ab8540_chargalg_usb_pp_en(di, false); + } else { + ab8540_chargalg_usb_pp_en(di, true); + ab8540_chargalg_usb_pre_chg_en(di, true); + abx500_chargalg_state_to(di, + STATE_USB_PP_PRE_CHARGE); + break; + } + } + abx500_chargalg_start_charging(di, di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); @@ -1479,6 +1534,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) break; + case STATE_USB_PP_PRE_CHARGE: + if (di->batt_data.volt > + (di->bm->fg_params->lowbat_threshold + + BAT_PLUS_MARGIN)) + abx500_chargalg_state_to(di, STATE_NORMAL_INIT); + break; + case STATE_NORMAL: handle_maxim_chg_curr(di); if (di->charge_status == POWER_SUPPLY_STATUS_FULL && diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 188aedc322c..cd71d8eadf5 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -267,6 +267,7 @@ struct abx500_bm_data { bool autopower_cfg; bool ac_enabled; bool usb_enabled; + bool usb_power_path; bool no_maintenance; bool capacity_scaling; bool chg_unknown_bat; diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h index a73e05a0441..0ebf0c5d1f8 100644 --- a/include/linux/mfd/abx500/ab8500-bm.h +++ b/include/linux/mfd/abx500/ab8500-bm.h @@ -69,6 +69,8 @@ #define AB8500_USBCH_CTRL1_REG 0xC0 #define AB8500_USBCH_CTRL2_REG 0xC1 #define AB8500_USBCH_IPT_CRNTLVL_REG 0xC2 +#define AB8540_USB_PP_MODE_REG 0xC5 +#define AB8540_USB_PP_CHR_REG 0xC6 /* * Gas Gauge register offsets @@ -259,6 +261,16 @@ enum bup_vch_sel { #define AB8505_RTC_PCUT_RESTART_REG 0x16 #define AB8505_RTC_PCUT_DEBOUNCE_REG 0x17 +/* USB Power Path constants for ab8540 */ +#define BUS_VSYS_VOL_SELECT_MASK 0x06 +#define BUS_VSYS_VOL_SELECT_3P6V 0x00 +#define BUS_VSYS_VOL_SELECT_3P325V 0x02 +#define BUS_VSYS_VOL_SELECT_3P9V 0x04 +#define BUS_VSYS_VOL_SELECT_4P3V 0x06 +#define BUS_POWER_PATH_MODE_ENA 0x01 +#define BUS_PP_PRECHG_CURRENT_MASK 0x0E +#define BUS_POWER_PATH_PRECHG_ENA 0x01 + /** * struct res_to_temp - defines one point in a temp to res curve. To * be used in battery packs that combines the identification resistor with a diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h index fa831f1e8cf..234c99143bf 100644 --- a/include/linux/mfd/abx500/ux500_chargalg.h +++ b/include/linux/mfd/abx500/ux500_chargalg.h @@ -20,6 +20,8 @@ struct ux500_charger_ops { int (*check_enable) (struct ux500_charger *, int, int); int (*kick_wd) (struct ux500_charger *); int (*update_curr) (struct ux500_charger *, int); + int (*pp_enable) (struct ux500_charger *, bool); + int (*pre_chg_enable) (struct ux500_charger *, bool); }; /** @@ -30,6 +32,7 @@ struct ux500_charger_ops { * @max_out_curr maximum output charger current in mA * @enabled indicates if this charger is used or not * @external external charger unit (pm2xxx) + * @power_path USB power path support */ struct ux500_charger { struct power_supply psy; @@ -39,6 +42,7 @@ struct ux500_charger { int wdt_refresh; bool enabled; bool external; + bool power_path; }; extern struct blocking_notifier_head charger_notifier_list; -- cgit v1.2.3-70-g09d2 From 861a30da53e2c5b9823b5390c1757baaf8f6e356 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 29 Aug 2012 20:36:51 +0800 Subject: ab8500-bm: Add support for the new ab8540 platform Provide AB8540 platform specific information required to run the Battery Management subsystem on AB8540 based devices. For this to happen we see the introduction of separate platform specific data structures and a means in which to process them. Signed-off-by: Lee Jones --- drivers/power/ab8500_bmdata.c | 91 +++++++++++- drivers/power/ab8500_btemp.c | 42 +++++- drivers/power/ab8500_charger.c | 270 +++++++++++++++-------------------- include/linux/mfd/abx500.h | 10 +- include/linux/mfd/abx500/ab8500-bm.h | 5 +- 5 files changed, 248 insertions(+), 170 deletions(-) (limited to 'include') diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c index e8759763fbe..85742a6d29f 100644 --- a/drivers/power/ab8500_bmdata.c +++ b/drivers/power/ab8500_bmdata.c @@ -414,13 +414,20 @@ static const struct abx500_fg_parameters fg = { .pcut_debounce_time = 2, }; -static const struct abx500_maxim_parameters maxi_params = { +static const struct abx500_maxim_parameters ab8500_maxi_params = { .ena_maxi = true, .chg_curr = 910, .wait_cycles = 10, .charger_curr_step = 100, }; +static const struct abx500_maxim_parameters abx540_maxi_params = { + .ena_maxi = true, + .chg_curr = 3000, + .wait_cycles = 10, + .charger_curr_step = 200, +}; + static const struct abx500_bm_charger_parameters chg = { .usb_volt_max = 5500, .usb_curr_max = 1500, @@ -428,6 +435,46 @@ static const struct abx500_bm_charger_parameters chg = { .ac_curr_max = 1500, }; +/* + * This array maps the raw hex value to charger output current used by the + * AB8500 values + */ +static int ab8500_charge_output_curr_map[] = { + 100, 200, 300, 400, 500, 600, 700, 800, + 900, 1000, 1100, 1200, 1300, 1400, 1500, 1500, +}; + +static int ab8540_charge_output_curr_map[] = { + 0, 0, 0, 75, 100, 125, 150, 175, + 200, 225, 250, 275, 300, 325, 350, 375, + 400, 425, 450, 475, 500, 525, 550, 575, + 600, 625, 650, 675, 700, 725, 750, 775, + 800, 825, 850, 875, 900, 925, 950, 975, + 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175, + 1200, 1225, 1250, 1275, 1300, 1325, 1350, 1375, + 1400, 1425, 1450, 1500, 1600, 1700, 1900, 2000, +}; + +/* + * This array maps the raw hex value to charger input current used by the + * AB8500 values + */ +static int ab8500_charge_input_curr_map[] = { + 50, 98, 193, 290, 380, 450, 500, 600, + 700, 800, 900, 1000, 1100, 1300, 1400, 1500, +}; + +static int ab8540_charge_input_curr_map[] = { + 25, 50, 75, 100, 125, 150, 175, 200, + 225, 250, 275, 300, 325, 350, 375, 400, + 425, 450, 475, 500, 525, 550, 575, 600, + 625, 650, 675, 700, 725, 750, 775, 800, + 825, 850, 875, 900, 925, 950, 975, 1000, + 1025, 1050, 1075, 1100, 1125, 1150, 1175, 1200, + 1225, 1250, 1275, 1300, 1325, 1350, 1375, 1400, + 1425, 1450, 1475, 1500, 1500, 1500, 1500, 1500, +}; + struct abx500_bm_data ab8500_bm_data = { .temp_under = 3, .temp_low = 8, @@ -447,15 +494,53 @@ struct abx500_bm_data ab8500_bm_data = { .fg_res = 100, .cap_levels = &cap_levels, .bat_type = bat_type_thermistor, - .n_btypes = 3, + .n_btypes = ARRAY_SIZE(bat_type_thermistor), .batt_id = 0, .interval_charging = 5, .interval_not_charging = 120, .temp_hysteresis = 3, .gnd_lift_resistance = 34, - .maxi = &maxi_params, + .chg_output_curr = ab8500_charge_output_curr_map, + .n_chg_out_curr = ARRAY_SIZE(ab8500_charge_output_curr_map), + .maxi = &ab8500_maxi_params, .chg_params = &chg, .fg_params = &fg, + .chg_input_curr = ab8500_charge_input_curr_map, + .n_chg_in_curr = ARRAY_SIZE(ab8500_charge_input_curr_map), +}; + +struct abx500_bm_data ab8540_bm_data = { + .temp_under = 3, + .temp_low = 8, + .temp_high = 43, + .temp_over = 48, + .main_safety_tmr_h = 4, + .temp_interval_chg = 20, + .temp_interval_nochg = 120, + .usb_safety_tmr_h = 4, + .bkup_bat_v = BUP_VCH_SEL_2P6V, + .bkup_bat_i = BUP_ICH_SEL_150UA, + .no_maintenance = false, + .capacity_scaling = false, + .adc_therm = ABx500_ADC_THERM_BATCTRL, + .chg_unknown_bat = false, + .enable_overshoot = false, + .fg_res = 100, + .cap_levels = &cap_levels, + .bat_type = bat_type_thermistor, + .n_btypes = ARRAY_SIZE(bat_type_thermistor), + .batt_id = 0, + .interval_charging = 5, + .interval_not_charging = 120, + .temp_hysteresis = 3, + .gnd_lift_resistance = 0, + .maxi = &abx540_maxi_params, + .chg_params = &chg, + .fg_params = &fg, + .chg_output_curr = ab8540_charge_output_curr_map, + .n_chg_out_curr = ARRAY_SIZE(ab8540_charge_output_curr_map), + .chg_input_curr = ab8540_charge_input_curr_map, + .n_chg_in_curr = ARRAY_SIZE(ab8540_charge_input_curr_map), }; int ab8500_bm_of_probe(struct device *dev, diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index 91ad3edf619..7336dcf45f7 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -42,6 +42,9 @@ #define BTEMP_BATCTRL_CURR_SRC_16UA 16 #define BTEMP_BATCTRL_CURR_SRC_18UA 18 +#define BTEMP_BATCTRL_CURR_SRC_60UA 60 +#define BTEMP_BATCTRL_CURR_SRC_120UA 120 + #define to_ab8500_btemp_device_info(x) container_of((x), \ struct ab8500_btemp, btemp_psy); @@ -216,7 +219,12 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, /* Only do this for batteries with internal NTC */ if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) { - if (is_ab9540(di->parent) || is_ab8505(di->parent)) { + if (is_ab8540(di->parent)) { + if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_60UA) + curr = BAT_CTRL_60U_ENA; + else + curr = BAT_CTRL_120U_ENA; + } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_16UA) curr = BAT_CTRL_16U_ENA; else @@ -257,7 +265,14 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, } else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) { dev_dbg(di->dev, "Disable BATCTRL curr source\n"); - if (is_ab9540(di->parent) || is_ab8505(di->parent)) { + if (is_ab8540(di->parent)) { + /* Write 0 to the curr bits */ + ret = abx500_mask_and_set_register_interruptible( + di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA, + ~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA)); + } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { /* Write 0 to the curr bits */ ret = abx500_mask_and_set_register_interruptible( di->dev, @@ -314,7 +329,13 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, * if we got an error above */ disable_curr_source: - if (is_ab9540(di->parent) || is_ab8505(di->parent)) { + if (is_ab8540(di->parent)) { + /* Write 0 to the curr bits */ + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA, + ~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA)); + } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { /* Write 0 to the curr bits */ ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, @@ -541,7 +562,9 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) { int res; u8 i; - if (is_ab9540(di->parent) || is_ab8505(di->parent)) + if (is_ab8540(di->parent)) + di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA; + else if (is_ab9540(di->parent) || is_ab8505(di->parent)) di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA; else di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA; @@ -582,9 +605,14 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) * detected type is Type 1, else we use the 7uA source */ if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && - di->bm->batt_id == 1) { - if (is_ab9540(di->parent) || is_ab8505(di->parent)) { - dev_dbg(di->dev, "Set BATCTRL current source to 16uA\n"); + di->bm->batt_id == 1) { + if (is_ab8540(di->parent)) { + dev_dbg(di->dev, + "Set BATCTRL current source to 60uA\n"); + di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA; + } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { + dev_dbg(di->dev, + "Set BATCTRL current source to 16uA\n"); di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA; } else { dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n"); diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index f249a65b02e..6089ee7bc60 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -57,7 +57,9 @@ #define MAIN_CH_INPUT_CURR_SHIFT 4 #define VBUS_IN_CURR_LIM_SHIFT 4 +#define AB8540_VBUS_IN_CURR_LIM_SHIFT 2 #define AUTO_VBUS_IN_CURR_LIM_SHIFT 4 +#define AB8540_AUTO_VBUS_IN_CURR_MASK 0x3F #define VBUS_IN_CURR_LIM_RETRY_SET_TIME 30 /* seconds */ #define LED_INDICATOR_PWM_ENA 0x01 @@ -82,6 +84,7 @@ #define AB8500_USB_LINK_STATUS 0x78 #define AB8505_USB_LINK_STATUS 0xF8 #define AB8500_STD_HOST_SUSP 0x18 +#define USB_LINK_STATUS_SHIFT 3 /* Watchdog timeout constant */ #define WD_TIMER 0x30 /* 4min */ @@ -751,8 +754,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, "VBUS has collapsed\n"); ret = -ENXIO; break; - } - if (is_ab9540(di->parent) || is_ab8505(di->parent)) { + } else { dev_dbg(di->dev, "USB Type - Charging not allowed\n"); di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05; @@ -807,30 +809,22 @@ static int ab8500_charger_read_usb_type(struct ab8500_charger *di) dev_err(di->dev, "%s ab8500 read failed\n", __func__); return ret; } - if (is_ab8500(di->parent)) { + if (is_ab8500(di->parent)) ret = abx500_get_register_interruptible(di->dev, AB8500_USB, - AB8500_USB_LINE_STAT_REG, &val); - } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { - ret = abx500_get_register_interruptible(di->dev, - AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val); - } else { - dev_err(di->dev, "%s unsupported analog baseband\n", __func__); - return -ENXIO; - } + AB8500_USB_LINE_STAT_REG, &val); + else + ret = abx500_get_register_interruptible(di->dev, + AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val); if (ret < 0) { dev_err(di->dev, "%s ab8500 read failed\n", __func__); return ret; } /* get the USB type */ - if (is_ab8500(di->parent)) { - val = (val & AB8500_USB_LINK_STATUS) >> 3; - } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { - val = (val & AB8505_USB_LINK_STATUS) >> 3; - } else { - dev_err(di->dev, "%s unsupported analog baseband\n", __func__); - return -ENXIO; - } + if (is_ab8500(di->parent)) + val = (val & AB8500_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT; + else + val = (val & AB8505_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT; ret = ab8500_charger_max_usb_curr(di, (enum ab8500_charger_link_status) val); @@ -866,16 +860,12 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di) return ret; } - if (is_ab8500(di->parent)) { + if (is_ab8500(di->parent)) ret = abx500_get_register_interruptible(di->dev, AB8500_USB, AB8500_USB_LINE_STAT_REG, &val); - } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { + else ret = abx500_get_register_interruptible(di->dev, AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val); - } else { - dev_err(di->dev, "%s unsupported analog baseband\n", __func__); - return -ENXIO; - } if (ret < 0) { dev_err(di->dev, "%s ab8500 read failed\n", __func__); return ret; @@ -889,14 +879,12 @@ static int ab8500_charger_detect_usb_type(struct ab8500_charger *di) */ /* get the USB type */ - if (is_ab8500(di->parent)) { - val = (val & AB8500_USB_LINK_STATUS) >> 3; - } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { - val = (val & AB8505_USB_LINK_STATUS) >> 3; - } else { - dev_err(di->dev, "%s unsupported analog baseband\n", __func__); - return -ENXIO; - } + if (is_ab8500(di->parent)) + val = (val & AB8500_USB_LINK_STATUS) >> + USB_LINK_STATUS_SHIFT; + else + val = (val & AB8505_USB_LINK_STATUS) >> + USB_LINK_STATUS_SHIFT; if (val) break; } @@ -991,51 +979,6 @@ static int ab8500_charger_voltage_map[] = { 4600 , }; -/* - * This array maps the raw hex value to charger current used by the AB8500 - * Values taken from the UM0836 - */ -static int ab8500_charger_current_map[] = { - 100 , - 200 , - 300 , - 400 , - 500 , - 600 , - 700 , - 800 , - 900 , - 1000 , - 1100 , - 1200 , - 1300 , - 1400 , - 1500 , -}; - -/* - * This array maps the raw hex value to VBUS input current used by the AB8500 - * Values taken from the UM0836 - */ -static int ab8500_charger_vbus_in_curr_map[] = { - USB_CH_IP_CUR_LVL_0P05, - USB_CH_IP_CUR_LVL_0P09, - USB_CH_IP_CUR_LVL_0P19, - USB_CH_IP_CUR_LVL_0P29, - USB_CH_IP_CUR_LVL_0P38, - USB_CH_IP_CUR_LVL_0P45, - USB_CH_IP_CUR_LVL_0P5, - USB_CH_IP_CUR_LVL_0P6, - USB_CH_IP_CUR_LVL_0P7, - USB_CH_IP_CUR_LVL_0P8, - USB_CH_IP_CUR_LVL_0P9, - USB_CH_IP_CUR_LVL_1P0, - USB_CH_IP_CUR_LVL_1P1, - USB_CH_IP_CUR_LVL_1P3, - USB_CH_IP_CUR_LVL_1P4, - USB_CH_IP_CUR_LVL_1P5, -}; - static int ab8500_voltage_to_regval(int voltage) { int i; @@ -1057,41 +1000,41 @@ static int ab8500_voltage_to_regval(int voltage) return -1; } -static int ab8500_current_to_regval(int curr) +static int ab8500_current_to_regval(struct ab8500_charger *di, int curr) { int i; - if (curr < ab8500_charger_current_map[0]) + if (curr < di->bm->chg_output_curr[0]) return 0; - for (i = 0; i < ARRAY_SIZE(ab8500_charger_current_map); i++) { - if (curr < ab8500_charger_current_map[i]) + for (i = 0; i < di->bm->n_chg_out_curr; i++) { + if (curr < di->bm->chg_output_curr[i]) return i - 1; } /* If not last element, return error */ - i = ARRAY_SIZE(ab8500_charger_current_map) - 1; - if (curr == ab8500_charger_current_map[i]) + i = di->bm->n_chg_out_curr - 1; + if (curr == di->bm->chg_output_curr[i]) return i; else return -1; } -static int ab8500_vbus_in_curr_to_regval(int curr) +static int ab8500_vbus_in_curr_to_regval(struct ab8500_charger *di, int curr) { int i; - if (curr < ab8500_charger_vbus_in_curr_map[0]) + if (curr < di->bm->chg_input_curr[0]) return 0; - for (i = 0; i < ARRAY_SIZE(ab8500_charger_vbus_in_curr_map); i++) { - if (curr < ab8500_charger_vbus_in_curr_map[i]) + for (i = 0; i < di->bm->n_chg_in_curr; i++) { + if (curr < di->bm->chg_input_curr[i]) return i - 1; } /* If not last element, return error */ - i = ARRAY_SIZE(ab8500_charger_vbus_in_curr_map) - 1; - if (curr == ab8500_charger_vbus_in_curr_map[i]) + i = di->bm->n_chg_in_curr - 1; + if (curr == di->bm->chg_input_curr[i]) return i; else return -1; @@ -1169,7 +1112,7 @@ static int ab8500_charger_set_current(struct ab8500_charger *di, int ich, int reg) { int ret = 0; - int auto_curr_index, curr_index, prev_curr_index, shift_value, i; + int curr_index, prev_curr_index, shift_value, i; u8 reg_value; u32 step_udelay; bool no_stepping = false; @@ -1187,39 +1130,27 @@ static int ab8500_charger_set_current(struct ab8500_charger *di, case AB8500_MCH_IPT_CURLVL_REG: shift_value = MAIN_CH_INPUT_CURR_SHIFT; prev_curr_index = (reg_value >> shift_value); - curr_index = ab8500_current_to_regval(ich); + curr_index = ab8500_current_to_regval(di, ich); step_udelay = STEP_UDELAY; if (!di->ac.charger_connected) no_stepping = true; break; case AB8500_USBCH_IPT_CRNTLVL_REG: - shift_value = VBUS_IN_CURR_LIM_SHIFT; + if (is_ab8540(di->parent)) + shift_value = AB8540_VBUS_IN_CURR_LIM_SHIFT; + else + shift_value = VBUS_IN_CURR_LIM_SHIFT; prev_curr_index = (reg_value >> shift_value); - curr_index = ab8500_vbus_in_curr_to_regval(ich); + curr_index = ab8500_vbus_in_curr_to_regval(di, ich); step_udelay = STEP_UDELAY * 100; - ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, - AB8500_CH_USBCH_STAT2_REG, ®_value); - if (ret < 0) { - dev_err(di->dev, "%s read failed\n", __func__); - goto exit_set_current; - } - auto_curr_index = - reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT; - - dev_dbg(di->dev, "%s Auto VBUS curr is %d mA\n", - __func__, - ab8500_charger_vbus_in_curr_map[auto_curr_index]); - - prev_curr_index = min(prev_curr_index, auto_curr_index); - if (!di->usb.charger_connected) no_stepping = true; break; case AB8500_CH_OPT_CRNTLVL_REG: shift_value = 0; prev_curr_index = (reg_value >> shift_value); - curr_index = ab8500_current_to_regval(ich); + curr_index = ab8500_current_to_regval(di, ich); step_udelay = STEP_UDELAY; if (curr_index && (curr_index - prev_curr_index) > 1) step_udelay *= 100; @@ -1459,8 +1390,8 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger, /* Check if the requested voltage or current is valid */ volt_index = ab8500_voltage_to_regval(vset); - curr_index = ab8500_current_to_regval(iset); - input_curr_index = ab8500_current_to_regval( + curr_index = ab8500_current_to_regval(di, iset); + input_curr_index = ab8500_current_to_regval(di, di->bm->chg_params->ac_curr_max); if (volt_index < 0 || curr_index < 0 || input_curr_index < 0) { dev_err(di->dev, @@ -1631,7 +1562,7 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger, /* Check if the requested voltage or current is valid */ volt_index = ab8500_voltage_to_regval(vset); - curr_index = ab8500_current_to_regval(ich_out); + curr_index = ab8500_current_to_regval(di, ich_out); if (volt_index < 0 || curr_index < 0) { dev_err(di->dev, "Charger voltage or current too high, " @@ -2396,18 +2327,21 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work) else dev_dbg(di->dev, "Error reading USB link status\n"); - if (is_ab9540(di->parent) || is_ab8505(di->parent)) - link_status = AB8505_USB_LINK_STATUS; - else + if (is_ab8500(di->parent)) link_status = AB8500_USB_LINK_STATUS; + else + link_status = AB8505_USB_LINK_STATUS; if (detected_chargers & USB_PW_CONN) { - if (((val & link_status) >> 3) == USB_STAT_NOT_VALID_LINK && + if (((val & link_status) >> USB_LINK_STATUS_SHIFT) == + USB_STAT_NOT_VALID_LINK && di->invalid_charger_detect_state == 0) { - dev_dbg(di->dev, "Invalid charger detected, state= 0\n"); + dev_dbg(di->dev, + "Invalid charger detected, state= 0\n"); /*Enable charger*/ abx500_mask_and_set_register_interruptible(di->dev, - AB8500_CHARGER, AB8500_USBCH_CTRL1_REG, 0x01, 0x01); + AB8500_CHARGER, AB8500_USBCH_CTRL1_REG, + USB_CH_ENA, USB_CH_ENA); /*Enable charger detection*/ abx500_mask_and_set_register_interruptible(di->dev, AB8500_USB, AB8500_MCH_IPT_CURLVL_REG, 0x01, 0x01); @@ -2417,15 +2351,17 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work) } if (di->invalid_charger_detect_state == 1) { - dev_dbg(di->dev, "Invalid charger detected, state= 1\n"); + dev_dbg(di->dev, + "Invalid charger detected, state= 1\n"); /*Stop charger detection*/ abx500_mask_and_set_register_interruptible(di->dev, AB8500_USB, AB8500_MCH_IPT_CURLVL_REG, 0x01, 0x00); /*Check link status*/ - ret = abx500_get_register_interruptible(di->dev, AB8500_USB, + ret = abx500_get_register_interruptible(di->dev, + AB8500_USB, AB8500_USB_LINE_STAT_REG, &val); dev_dbg(di->dev, "USB link status= 0x%02x\n", - (val & link_status) >> 3); + (val & link_status) >> USB_LINK_STATUS_SHIFT); di->invalid_charger_detect_state = 2; } } else { @@ -2741,7 +2677,7 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work) { struct ab8500_charger *di = container_of(work, struct ab8500_charger, vbus_drop_end_work.work); - int ret; + int ret, curr; u8 reg_value; di->flags.vbus_drop_end = false; @@ -2749,32 +2685,41 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work) /* Reset the drop counter */ abx500_set_register_interruptible(di->dev, AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01); - ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, - AB8500_CH_USBCH_STAT2_REG, - ®_value); + + if (is_ab8540(di->parent)) + ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, + AB8540_CH_USBCH_STAT3_REG, ®_value); + else + ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, + AB8500_CH_USBCH_STAT2_REG, ®_value); if (ret < 0) { - dev_err(di->dev, "%s ab8500 read failed\n", __func__); - } else { - int curr = ab8500_charger_vbus_in_curr_map[ + dev_err(di->dev, "%s read failed\n", __func__); + return; + } + + if (is_ab8540(di->parent)) + curr = di->bm->chg_input_curr[ + reg_value & AB8540_AUTO_VBUS_IN_CURR_MASK]; + else + curr = di->bm->chg_input_curr[ reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT]; - if (di->max_usb_in_curr.calculated_max != curr) { - /* USB source is collapsing */ - di->max_usb_in_curr.calculated_max = curr; - dev_dbg(di->dev, - "VBUS input current limiting to %d mA\n", - di->max_usb_in_curr.calculated_max); - } else { - /* - * USB source can not give more than this amount. - * Taking more will collapse the source. - */ - di->max_usb_in_curr.set_max = - di->max_usb_in_curr.calculated_max; - dev_dbg(di->dev, - "VBUS input current limited to %d mA\n", - di->max_usb_in_curr.set_max); - return; - } + + if (di->max_usb_in_curr.calculated_max != curr) { + /* USB source is collapsing */ + di->max_usb_in_curr.calculated_max = curr; + dev_dbg(di->dev, + "VBUS input current limiting to %d mA\n", + di->max_usb_in_curr.calculated_max); + } else { + /* + * USB source can not give more than this amount. + * Taking more will collapse the source. + */ + di->max_usb_in_curr.set_max = + di->max_usb_in_curr.calculated_max; + dev_dbg(di->dev, + "VBUS input current limited to %d mA\n", + di->max_usb_in_curr.set_max); } if (di->usb.charger_connected) @@ -3134,9 +3079,14 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) goto out; } - ret = abx500_set_register_interruptible(di->dev, - AB8500_CHARGER, - AB8500_CH_OPT_CRNTLVL_MAX_REG, CH_OP_CUR_LVL_1P6); + if (is_ab8540(di->parent)) + ret = abx500_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG, + CH_OP_CUR_LVL_2P); + else + ret = abx500_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG, + CH_OP_CUR_LVL_1P6); if (ret) { dev_err(di->dev, "failed to set CH_OPT_CRNTLVL_MAX_REG\n"); @@ -3144,7 +3094,8 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) } } - if (is_ab9540_2p0(di->parent) || is_ab8505_2p0(di->parent)) + if (is_ab9540_2p0(di->parent) || is_ab9540_3p0(di->parent) + || is_ab8505_2p0(di->parent) || is_ab8540(di->parent)) ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_CHARGER, AB8500_USBCH_CTRL2_REG, @@ -3250,7 +3201,8 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) AB8500_RTC_CTRL1_REG, bup_vch_range | vbup33_vrtcn); if (ret) { - dev_err(di->dev, "failed to setup backup battery charging\n"); + dev_err(di->dev, + "failed to setup backup battery charging\n"); goto out; } } @@ -3267,14 +3219,16 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) AB8500_CHARGER, AB8540_USB_PP_MODE_REG, BUS_VSYS_VOL_SELECT_MASK, BUS_VSYS_VOL_SELECT_3P6V); if (ret) { - dev_err(di->dev, "failed to setup usb power path vsys voltage\n"); + dev_err(di->dev, + "failed to setup usb power path vsys voltage\n"); goto out; } ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_CHARGER, AB8540_USB_PP_CHR_REG, BUS_PP_PRECHG_CURRENT_MASK, 0); if (ret) { - dev_err(di->dev, "failed to setup usb power path prechage current\n"); + dev_err(di->dev, + "failed to setup usb power path prechage current\n"); goto out; } } @@ -3537,8 +3491,8 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current; di->ac_chg.max_out_volt = ab8500_charger_voltage_map[ ARRAY_SIZE(ab8500_charger_voltage_map) - 1]; - di->ac_chg.max_out_curr = ab8500_charger_current_map[ - ARRAY_SIZE(ab8500_charger_current_map) - 1]; + di->ac_chg.max_out_curr = + di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1]; di->ac_chg.wdt_refresh = CHG_WD_INTERVAL; di->ac_chg.enabled = di->bm->ac_enabled; di->ac_chg.external = false; @@ -3566,8 +3520,8 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.ops.pre_chg_enable = &ab8540_charger_usb_pre_chg_enable; di->usb_chg.max_out_volt = ab8500_charger_voltage_map[ ARRAY_SIZE(ab8500_charger_voltage_map) - 1]; - di->usb_chg.max_out_curr = ab8500_charger_current_map[ - ARRAY_SIZE(ab8500_charger_current_map) - 1]; + di->usb_chg.max_out_curr = + di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1]; di->usb_chg.wdt_refresh = CHG_WD_INTERVAL; di->usb_chg.enabled = di->bm->usb_enabled; di->usb_chg.external = false; diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index cd71d8eadf5..33b0253569a 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -246,7 +246,11 @@ struct abx500_bm_charger_parameters { * @interval_not_charging charge alg cycle period time when not charging (sec) * @temp_hysteresis temperature hysteresis * @gnd_lift_resistance Battery ground to phone ground resistance (mOhm) - * @maxi: maximization parameters + * @n_chg_out_curr number of elements in array chg_output_curr + * @n_chg_in_curr number of elements in array chg_input_curr + * @chg_output_curr charger output current level map + * @chg_input_curr charger input current level map + * @maxi maximization parameters * @cap_levels capacity in percent for the different capacity levels * @bat_type table of supported battery types * @chg_params charger parameters @@ -281,6 +285,10 @@ struct abx500_bm_data { int interval_not_charging; int temp_hysteresis; int gnd_lift_resistance; + int n_chg_out_curr; + int n_chg_in_curr; + int *chg_output_curr; + int *chg_input_curr; const struct abx500_maxim_parameters *maxi; const struct abx500_bm_capacity_levels *cap_levels; struct abx500_battery_type *bat_type; diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h index 0ebf0c5d1f8..ee1c1626c88 100644 --- a/include/linux/mfd/abx500/ab8500-bm.h +++ b/include/linux/mfd/abx500/ab8500-bm.h @@ -33,7 +33,7 @@ #define AB8500_CH_STATUS2_REG 0x01 #define AB8500_CH_USBCH_STAT1_REG 0x02 #define AB8500_CH_USBCH_STAT2_REG 0x03 -#define AB8500_CH_FSM_STAT_REG 0x04 +#define AB8540_CH_USBCH_STAT3_REG 0x04 #define AB8500_CH_STAT_REG 0x05 /* @@ -157,6 +157,7 @@ #define CH_OP_CUR_LVL_1P4 0x0D #define CH_OP_CUR_LVL_1P5 0x0E #define CH_OP_CUR_LVL_1P6 0x0F +#define CH_OP_CUR_LVL_2P 0x3F /* BTEMP High thermal limits */ #define BTEMP_HIGH_TH_57_0 0x00 @@ -246,6 +247,8 @@ enum bup_vch_sel { #define BAT_CTRL_20U_ENA 0x02 #define BAT_CTRL_18U_ENA 0x01 #define BAT_CTRL_16U_ENA 0x02 +#define BAT_CTRL_60U_ENA 0x01 +#define BAT_CTRL_120U_ENA 0x02 #define BAT_CTRL_CMP_ENA 0x04 #define FORCE_BAT_CTRL_CMP_HIGH 0x08 #define BAT_CTRL_PULL_UP_ENA 0x10 -- cgit v1.2.3-70-g09d2 From b3ea5f451e4e435b650e34142f8552002dc21297 Mon Sep 17 00:00:00 2001 From: Marcus Cooper Date: Wed, 29 Aug 2012 17:56:19 +0200 Subject: ab8500-charger: Add UsbLineCtrl2 reference When the state of USB Charge detection is changed then the calls use a define for another register in other bank. This change creates a new define for the correct register and removes the magic numbers that are present. Signed-off-by: Marcus Cooper Signed-off-by: Lee Jones Reviewed-by: Hakan BERG Reviewed-by: Jonas ABERG --- drivers/power/ab8500_charger.c | 11 +++++++---- include/linux/mfd/abx500/ab8500-bm.h | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index bf8b479914c..64accb235d2 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -54,6 +54,7 @@ #define VBUS_DET_DBNC1 0x01 #define OTP_ENABLE_WD 0x01 #define DROP_COUNT_RESET 0x01 +#define USB_CH_DET 0x01 #define MAIN_CH_INPUT_CURR_SHIFT 4 #define VBUS_IN_CURR_LIM_SHIFT 4 @@ -2348,8 +2349,9 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work) AB8500_CHARGER, AB8500_USBCH_CTRL1_REG, USB_CH_ENA, USB_CH_ENA); /*Enable charger detection*/ - abx500_mask_and_set_register_interruptible(di->dev, AB8500_USB, - AB8500_MCH_IPT_CURLVL_REG, 0x01, 0x01); + abx500_mask_and_set_register_interruptible(di->dev, + AB8500_USB, AB8500_USB_LINE_CTRL2_REG, + USB_CH_DET, USB_CH_DET); di->invalid_charger_detect_state = 1; /*exit and wait for new link status interrupt.*/ return; @@ -2359,8 +2361,9 @@ static void ab8500_charger_usb_link_status_work(struct work_struct *work) dev_dbg(di->dev, "Invalid charger detected, state= 1\n"); /*Stop charger detection*/ - abx500_mask_and_set_register_interruptible(di->dev, AB8500_USB, - AB8500_MCH_IPT_CURLVL_REG, 0x01, 0x00); + abx500_mask_and_set_register_interruptible(di->dev, + AB8500_USB, AB8500_USB_LINE_CTRL2_REG, + USB_CH_DET, 0x00); /*Check link status*/ if (is_ab8500(di->parent)) ret = abx500_get_register_interruptible(di->dev, diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h index ee1c1626c88..f5214dc651f 100644 --- a/include/linux/mfd/abx500/ab8500-bm.h +++ b/include/linux/mfd/abx500/ab8500-bm.h @@ -23,6 +23,7 @@ * Bank : 0x5 */ #define AB8500_USB_LINE_STAT_REG 0x80 +#define AB8500_USB_LINE_CTRL2_REG 0x82 #define AB8500_USB_LINK1_STAT_REG 0x94 /* -- cgit v1.2.3-70-g09d2 From f4095a0f06476e5914f2c58b4e96258b2e2ba6b7 Mon Sep 17 00:00:00 2001 From: M BenZoubeir Date: Thu, 13 Sep 2012 10:34:18 +0200 Subject: pm2301-charger: Adjust interrupt handler behavior Signed-off-by: M BenZoubeir Signed-off-by: Lee Jones Reviewed-by: Philippe LANGLAIS --- drivers/power/pm2301_charger.c | 45 ++++++++++++++++++++++-------------------- include/linux/pm2301_charger.h | 2 +- 2 files changed, 25 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index b8eafc3850d..eed8a89ba4f 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -493,14 +493,16 @@ static irqreturn_t pm2xxx_irq_int(int irq, void *data) struct pm2xxx_interrupts *interrupt = pm2->pm2_int; int i; - for (i = 0; i < PM2XXX_NUM_INT_REG; i++) { - pm2xxx_reg_read(pm2, + do { + for (i = 0; i < PM2XXX_NUM_INT_REG; i++) { + pm2xxx_reg_read(pm2, pm2xxx_interrupt_registers[i], &(interrupt->reg[i])); - if (interrupt->reg[i] > 0) - interrupt->handler[i](pm2, interrupt->reg[i]); - } + if (interrupt->reg[i] > 0) + interrupt->handler[i](pm2, interrupt->reg[i]); + } + } while (gpio_get_value(pm2->pdata->gpio_irq_number) == 0); return IRQ_HANDLED; } @@ -951,6 +953,7 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, struct pm2xxx_charger *pm2; int ret = 0; u8 val; + int i; pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL); if (!pm2) { @@ -1062,24 +1065,25 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, } /* Register interrupts */ - ret = request_threaded_irq(pm2->pdata->irq_number, NULL, + ret = request_threaded_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), + NULL, pm2xxx_charger_irq[0].isr, pm2->pdata->irq_type, pm2xxx_charger_irq[0].name, pm2); if (ret != 0) { dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n", - pm2xxx_charger_irq[0].name, pm2->pdata->irq_number, ret); + pm2xxx_charger_irq[0].name, + gpio_to_irq(pm2->pdata->gpio_irq_number), ret); goto unregister_pm2xxx_charger; } /* pm interrupt can wake up system */ - ret = enable_irq_wake(pm2->pdata->irq_number); + ret = enable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); if (ret) { dev_err(pm2->dev, "failed to set irq wake\n"); goto unregister_pm2xxx_interrupt; } - /*Initialize lock*/ mutex_init(&pm2->lock); /* @@ -1099,16 +1103,16 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, } set_lpn_pin(pm2); + + /* read interrupt registers */ + for (i = 0; i < PM2XXX_NUM_INT_REG; i++) + pm2xxx_reg_read(pm2, + pm2xxx_interrupt_registers[i], + &val); + ret = pm2xxx_charger_detection(pm2, &val); if ((ret == 0) && val) { - /* - * When boot is due to AC charger plug-in, - * read interrupt registers - */ - pm2xxx_reg_read(pm2, PM2XXX_REG_INT1, &val); - pm2xxx_reg_read(pm2, PM2XXX_REG_INT2, &val); - pm2xxx_reg_read(pm2, PM2XXX_REG_INT4, &val); pm2->ac.charger_connected = 1; ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON, AB8500_MAIN_CH_DET); @@ -1122,10 +1126,10 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, free_gpio: gpio_free(pm2->lpn_pin); disable_pm2_irq_wake: - disable_irq_wake(pm2->pdata->irq_number); + disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); unregister_pm2xxx_interrupt: /* disable interrupt */ - free_irq(pm2->pdata->irq_number, pm2); + free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2); unregister_pm2xxx_charger: /* unregister power supply */ power_supply_unregister(&pm2->ac_chg.psy); @@ -1148,10 +1152,10 @@ static int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client) pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0); /* Disable wake by pm interrupt */ - disable_irq_wake(pm2->pdata->irq_number); + disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number)); /* Disable interrupts */ - free_irq(pm2->pdata->irq_number, pm2); + free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2); /* Delete the work queue */ destroy_workqueue(pm2->charger_wq); @@ -1163,7 +1167,6 @@ static int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client) power_supply_unregister(&pm2->ac_chg.psy); - /*Free GPIO60*/ gpio_free(pm2->lpn_pin); kfree(pm2); diff --git a/include/linux/pm2301_charger.h b/include/linux/pm2301_charger.h index fc3f026922a..85c16defe11 100644 --- a/include/linux/pm2301_charger.h +++ b/include/linux/pm2301_charger.h @@ -48,7 +48,7 @@ struct pm2xxx_charger_platform_data { size_t num_supplicants; int i2c_bus; const char *label; - int irq_number; + int gpio_irq_number; unsigned int lpn_gpio; int irq_type; }; -- cgit v1.2.3-70-g09d2 From e943789edbb1f9de71b129d9992489eb79ed341f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Feb 2013 21:38:08 +0100 Subject: mac80211: provide ieee80211_sta_eosp() The irqsafe version ieee80211_sta_eosp_irqsafe() exists, but drivers must not mix calls to any irqsafe/non-irqsafe function. Both ath9k and iwlwifi, the likely first users of this interface, use non-irqsafe RX/TX/TX status so must also use a non-irqsafe version of this function. Since no driver uses the _irqsafe() version, remove that. Signed-off-by: Johannes Berg --- Documentation/DocBook/80211.tmpl | 2 +- include/net/mac80211.h | 25 ++++++++++++++----------- net/mac80211/ieee80211_i.h | 5 ----- net/mac80211/main.c | 14 -------------- net/mac80211/sta_info.c | 20 +++----------------- 5 files changed, 18 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 284ced7a228..0f6a3edcd44 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -437,7 +437,7 @@ !Finclude/net/mac80211.h ieee80211_get_buffered_bc !Finclude/net/mac80211.h ieee80211_beacon_get -!Finclude/net/mac80211.h ieee80211_sta_eosp_irqsafe +!Finclude/net/mac80211.h ieee80211_sta_eosp !Finclude/net/mac80211.h ieee80211_frame_release_type !Finclude/net/mac80211.h ieee80211_sta_ps_transition !Finclude/net/mac80211.h ieee80211_sta_ps_transition_ni diff --git a/include/net/mac80211.h b/include/net/mac80211.h index cdd7cea1fd4..8c0ca11a39c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1946,14 +1946,14 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * filter those response frames except in the case of frames that * are buffered in the driver -- those must remain buffered to avoid * reordering. Because it is possible that no frames are released - * in this case, the driver must call ieee80211_sta_eosp_irqsafe() + * in this case, the driver must call ieee80211_sta_eosp() * to indicate to mac80211 that the service period ended anyway. * * Finally, if frames from multiple TIDs are released from mac80211 * but the driver might reorder them, it must clear & set the flags * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP) * and also take care of the EOSP and MORE_DATA bits in the frame. - * The driver may also use ieee80211_sta_eosp_irqsafe() in this case. + * The driver may also use ieee80211_sta_eosp() in this case. */ /** @@ -2506,7 +2506,7 @@ enum ieee80211_roc_type { * setting the EOSP flag in the QoS header of the frames. Also, when the * service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP * on the last frame in the SP. Alternatively, it may call the function - * ieee80211_sta_eosp_irqsafe() to inform mac80211 of the end of the SP. + * ieee80211_sta_eosp() to inform mac80211 of the end of the SP. * This callback must be atomic. * @allow_buffered_frames: Prepare device to allow the given number of frames * to go out to the given station. The frames will be sent by mac80211 @@ -2517,7 +2517,7 @@ enum ieee80211_roc_type { * them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag * on the last frame and clear it on all others and also handle the EOSP * bit in the QoS header correctly. Alternatively, it can also call the - * ieee80211_sta_eosp_irqsafe() function. + * ieee80211_sta_eosp() function. * The @tids parameter is a bitmap and tells the driver which TIDs the * frames will be on; it will at most have two bits set. * This callback must be atomic. @@ -3857,14 +3857,17 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, * %IEEE80211_TX_STATUS_EOSP bit and call this function instead. * This applies for PS-Poll as well as uAPSD. * - * Note that there is no non-_irqsafe version right now as - * it wasn't needed, but just like _tx_status() and _rx() - * must not be mixed in irqsafe/non-irqsafe versions, this - * function must not be mixed with those either. Use the - * all irqsafe, or all non-irqsafe, don't mix! If you need - * the non-irqsafe version of this, you need to add it. + * Note that just like with _tx_status() and _rx() drivers must + * not mix calls to irqsafe/non-irqsafe versions, this function + * must not be mixed with those either. Use the all irqsafe, or + * all non-irqsafe, don't mix! + * + * NB: the _irqsafe version of this function doesn't exist, no + * driver needs it right now. Don't call this function if + * you'd need the _irqsafe version, look at the git history + * and restore the _irqsafe version! */ -void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta); +void ieee80211_sta_eosp(struct ieee80211_sta *pubsta); /** * ieee80211_iter_keys - iterate keys programmed into the device diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f4433f081e7..95beb18588f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -800,11 +800,6 @@ enum sdata_queue_type { enum { IEEE80211_RX_MSG = 1, IEEE80211_TX_STATUS_MSG = 2, - IEEE80211_EOSP_MSG = 3, -}; - -struct skb_eosp_msg_data { - u8 sta[ETH_ALEN], iface[ETH_ALEN]; }; enum queue_stop_reason { diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 5a53aa5ede8..5531c89909d 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -226,8 +226,6 @@ u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) static void ieee80211_tasklet_handler(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *) data; - struct sta_info *sta, *tmp; - struct skb_eosp_msg_data *eosp_data; struct sk_buff *skb; while ((skb = skb_dequeue(&local->skb_queue)) || @@ -243,18 +241,6 @@ static void ieee80211_tasklet_handler(unsigned long data) skb->pkt_type = 0; ieee80211_tx_status(&local->hw, skb); break; - case IEEE80211_EOSP_MSG: - eosp_data = (void *)skb->cb; - for_each_sta_info(local, eosp_data->sta, sta, tmp) { - /* skip wrong virtual interface */ - if (memcmp(eosp_data->iface, - sta->sdata->vif.addr, ETH_ALEN)) - continue; - clear_sta_flag(sta, WLAN_STA_SP); - break; - } - dev_kfree_skb(skb); - break; default: WARN(1, "mac80211: Packet is of unknown type %d\n", skb->pkt_type); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 3644ad79688..852bf45fcfa 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1390,30 +1390,16 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_sta_block_awake); -void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta) +void ieee80211_sta_eosp(struct ieee80211_sta *pubsta) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); struct ieee80211_local *local = sta->local; - struct sk_buff *skb; - struct skb_eosp_msg_data *data; trace_api_eosp(local, pubsta); - skb = alloc_skb(0, GFP_ATOMIC); - if (!skb) { - /* too bad ... but race is better than loss */ - clear_sta_flag(sta, WLAN_STA_SP); - return; - } - - data = (void *)skb->cb; - memcpy(data->sta, pubsta->addr, ETH_ALEN); - memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN); - skb->pkt_type = IEEE80211_EOSP_MSG; - skb_queue_tail(&local->skb_queue, skb); - tasklet_schedule(&local->tasklet); + clear_sta_flag(sta, WLAN_STA_SP); } -EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe); +EXPORT_SYMBOL(ieee80211_sta_eosp); void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, u8 tid, bool buffered) -- cgit v1.2.3-70-g09d2 From 9684819b5a29e62acd8265a92d8f3454de9bb71e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 27 Feb 2013 16:38:17 +0100 Subject: HID: ll_driver: Extend the interface with idle requests Some drivers send the idle command directly to underlying device, creating an unwanted dependency on the underlying transport layer. This patch adds hid_hw_idle() to the interface, thereby removing usbhid from the lion share of the drivers. Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 15 +++++++++++++++ include/linux/hid.h | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 420466bc481..effcd3d6f5c 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1253,6 +1253,20 @@ static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int r } } +static int usbhid_idle(struct hid_device *hid, int report, int idle, + int reqtype) +{ + struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_interface *intf = to_usb_interface(hid->dev.parent); + struct usb_host_interface *interface = intf->cur_altsetting; + int ifnum = interface->desc.bInterfaceNumber; + + if (reqtype != HID_REQ_SET_IDLE) + return -EINVAL; + + return hid_set_idle(dev, ifnum, report, idle); +} + static struct hid_ll_driver usb_hid_driver = { .parse = usbhid_parse, .start = usbhid_start, @@ -1263,6 +1277,7 @@ static struct hid_ll_driver usb_hid_driver = { .hidinput_input_event = usb_hidinput_input_event, .request = usbhid_request, .wait = usbhid_wait_io, + .idle = usbhid_idle, }; static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/include/linux/hid.h b/include/linux/hid.h index 7071eb3d36c..863744c38dd 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -664,6 +664,7 @@ struct hid_driver { * shouldn't allocate anything to not leak memory * @request: send report request to device (e.g. feature report) * @wait: wait for buffered io to complete (send/recv reports) + * @idle: send idle request to device */ struct hid_ll_driver { int (*start)(struct hid_device *hdev); @@ -683,6 +684,7 @@ struct hid_ll_driver { struct hid_report *report, int reqtype); int (*wait)(struct hid_device *hdev); + int (*idle)(struct hid_device *hdev, int report, int idle, int reqtype); }; @@ -906,6 +908,23 @@ static inline void hid_hw_request(struct hid_device *hdev, hdev->ll_driver->request(hdev, report, reqtype); } +/** + * hid_hw_idle - send idle request to device + * + * @hdev: hid device + * @report: report to control + * @idle: idle state + * @reqtype: hid request type + */ +static inline int hid_hw_idle(struct hid_device *hdev, int report, int idle, + int reqtype) +{ + if (hdev->ll_driver->idle) + return hdev->ll_driver->idle(hdev, report, idle, reqtype); + + return 0; +} + /** * hid_hw_wait - wait for buffered io to complete * -- cgit v1.2.3-70-g09d2 From d2348fb6fdc6d671ad45b62db237f76c8c115603 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Sat, 2 Mar 2013 11:10:11 +0100 Subject: tick: Dynamically set broadcast irq affinity When a cpu goes to a deep idle state where its local timer is shutdown, it notifies the time frame work to use the broadcast timer instead. Unfortunately, the broadcast device could wake up any CPU, including an idle one which is not concerned by the wake up at all. So in the worst case an idle CPU will wake up to send an IPI to the CPU whose timer expired. Provide an opt-in feature CLOCK_EVT_FEAT_DYNIRQ which tells the core that is should set the interrupt affinity of the broadcast interrupt to the cpu which has the earliest expiry time. This avoids unnecessary spurious wakeups and IPIs. [ tglx: Adopted to cpumask rework, silenced an uninitialized warning, massaged changelog ] Signed-off-by: Daniel Lezcano Cc: viresh.kumar@linaro.org Cc: jacob.jun.pan@linux.intel.com Cc: linux-arm-kernel@lists.infradead.org Cc: santosh.shilimkar@ti.com Cc: linaro-kernel@lists.linaro.org Cc: patches@linaro.org Cc: rickard.andersson@stericsson.com Cc: vincent.guittot@linaro.org Cc: linus.walleij@stericsson.com Cc: john.stultz@linaro.org Link: http://lkml.kernel.org/r/1362219013-18173-3-git-send-email-daniel.lezcano@linaro.org Signed-off-by: Thomas Gleixner --- include/linux/clockchips.h | 5 +++++ kernel/time/tick-broadcast.c | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index 66346521cb6..494d33ea78f 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -55,6 +55,11 @@ enum clock_event_nofitiers { #define CLOCK_EVT_FEAT_C3STOP 0x000008 #define CLOCK_EVT_FEAT_DUMMY 0x000010 +/* + * Core shall set the interrupt affinity dynamically in broadcast mode + */ +#define CLOCK_EVT_FEAT_DYNIRQ 0x000020 + /** * struct clock_event_device - clock event device descriptor * @event_handler: Assigned by the framework to be called by the low diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 70dd98ce18d..380910db715 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -401,13 +401,34 @@ struct cpumask *tick_get_broadcast_oneshot_mask(void) return tick_broadcast_oneshot_mask; } -static int tick_broadcast_set_event(struct clock_event_device *bc, +/* + * Set broadcast interrupt affinity + */ +static void tick_broadcast_set_affinity(struct clock_event_device *bc, + const struct cpumask *cpumask) +{ + if (!(bc->features & CLOCK_EVT_FEAT_DYNIRQ)) + return; + + if (cpumask_equal(bc->cpumask, cpumask)) + return; + + bc->cpumask = cpumask; + irq_set_affinity(bc->irq, bc->cpumask); +} + +static int tick_broadcast_set_event(struct clock_event_device *bc, int cpu, ktime_t expires, int force) { + int ret; + if (bc->mode != CLOCK_EVT_MODE_ONESHOT) clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); - return clockevents_program_event(bc, expires, force); + ret = clockevents_program_event(bc, expires, force); + if (!ret) + tick_broadcast_set_affinity(bc, cpumask_of(cpu)); + return ret; } int tick_resume_broadcast_oneshot(struct clock_event_device *bc) @@ -436,7 +457,7 @@ static void tick_handle_oneshot_broadcast(struct clock_event_device *dev) { struct tick_device *td; ktime_t now, next_event; - int cpu; + int cpu, next_cpu = 0; raw_spin_lock(&tick_broadcast_lock); again: @@ -447,10 +468,12 @@ again: /* Find all expired events */ for_each_cpu(cpu, tick_broadcast_oneshot_mask) { td = &per_cpu(tick_cpu_device, cpu); - if (td->evtdev->next_event.tv64 <= now.tv64) + if (td->evtdev->next_event.tv64 <= now.tv64) { cpumask_set_cpu(cpu, tmpmask); - else if (td->evtdev->next_event.tv64 < next_event.tv64) + } else if (td->evtdev->next_event.tv64 < next_event.tv64) { next_event.tv64 = td->evtdev->next_event.tv64; + next_cpu = cpu; + } } /* @@ -473,7 +496,7 @@ again: * Rearm the broadcast device. If event expired, * repeat the above */ - if (tick_broadcast_set_event(dev, next_event, 0)) + if (tick_broadcast_set_event(dev, next_cpu, next_event, 0)) goto again; } raw_spin_unlock(&tick_broadcast_lock); @@ -515,7 +538,7 @@ void tick_broadcast_oneshot_control(unsigned long reason) if (!cpumask_test_and_set_cpu(cpu, tick_broadcast_oneshot_mask)) { clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN); if (dev->next_event.tv64 < bc->next_event.tv64) - tick_broadcast_set_event(bc, dev->next_event, 1); + tick_broadcast_set_event(bc, cpu, dev->next_event, 1); } } else { if (cpumask_test_and_clear_cpu(cpu, tick_broadcast_oneshot_mask)) { @@ -581,7 +604,7 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); tick_broadcast_init_next_event(tmpmask, tick_next_period); - tick_broadcast_set_event(bc, tick_next_period, 1); + tick_broadcast_set_event(bc, cpu, tick_next_period, 1); } else bc->next_event.tv64 = KTIME_MAX; } else { -- cgit v1.2.3-70-g09d2 From 56dd9470d7c8734f055da2a6bac553caf4a468eb Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 24 Feb 2013 00:23:25 +0100 Subject: context_tracking: Move exception handling to generic code Exceptions handling on context tracking should share common treatment: on entry we exit user mode if the exception triggered in that context. Then on exception exit we return to that previous context. Generalize this to avoid duplication across archs. Signed-off-by: Frederic Weisbecker Cc: Li Zhong Cc: Kevin Hilman Cc: Mats Liljegren Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Steven Rostedt Cc: Namhyung Kim Cc: Andrew Morton Cc: Thomas Gleixner Cc: Paul E. McKenney --- arch/x86/include/asm/context_tracking.h | 21 --------------------- arch/x86/kernel/kvm.c | 2 +- arch/x86/kernel/traps.c | 3 +-- arch/x86/mm/fault.c | 2 +- include/linux/context_tracking.h | 17 ++++++++++++++++- 5 files changed, 19 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/context_tracking.h b/arch/x86/include/asm/context_tracking.h index 1616562683e..1fe49704b14 100644 --- a/arch/x86/include/asm/context_tracking.h +++ b/arch/x86/include/asm/context_tracking.h @@ -1,31 +1,10 @@ #ifndef _ASM_X86_CONTEXT_TRACKING_H #define _ASM_X86_CONTEXT_TRACKING_H -#ifndef __ASSEMBLY__ -#include -#include - -static inline void exception_enter(struct pt_regs *regs) -{ - user_exit(); -} - -static inline void exception_exit(struct pt_regs *regs) -{ -#ifdef CONFIG_CONTEXT_TRACKING - if (user_mode(regs)) - user_enter(); -#endif -} - -#else /* __ASSEMBLY__ */ - #ifdef CONFIG_CONTEXT_TRACKING # define SCHEDULE_USER call schedule_user #else # define SCHEDULE_USER call schedule #endif -#endif /* !__ASSEMBLY__ */ - #endif diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index b686a904d7c..e8bb0d61ecd 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -20,6 +20,7 @@ * Authors: Anthony Liguori */ +#include #include #include #include @@ -43,7 +44,6 @@ #include #include #include -#include static int kvmapf = 1; diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 68bda7a8415..ecc4ccbdd0c 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -55,8 +56,6 @@ #include #include #include -#include - #include #ifdef CONFIG_X86_64 diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 2b97525246d..f946e6ce331 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -13,12 +13,12 @@ #include /* perf_sw_event */ #include /* hstate_index_to_shift */ #include /* prefetchw */ +#include /* exception_enter(), ... */ #include /* dotraplinkage, ... */ #include /* pgd_*(), ... */ #include /* kmemcheck_*(), ... */ #include /* VSYSCALL_START */ -#include /* exception_enter(), ... */ /* * Page fault error code bits: diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index b28d161c109..5a69273e93e 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -1,10 +1,11 @@ #ifndef _LINUX_CONTEXT_TRACKING_H #define _LINUX_CONTEXT_TRACKING_H -#ifdef CONFIG_CONTEXT_TRACKING #include #include +#include +#ifdef CONFIG_CONTEXT_TRACKING struct context_tracking { /* * When active is false, probes are unset in order @@ -33,12 +34,26 @@ static inline bool context_tracking_active(void) extern void user_enter(void); extern void user_exit(void); + +static inline void exception_enter(struct pt_regs *regs) +{ + user_exit(); +} + +static inline void exception_exit(struct pt_regs *regs) +{ + if (user_mode(regs)) + user_enter(); +} + extern void context_tracking_task_switch(struct task_struct *prev, struct task_struct *next); #else static inline bool context_tracking_in_user(void) { return false; } static inline void user_enter(void) { } static inline void user_exit(void) { } +static inline void exception_enter(struct pt_regs *regs) { } +static inline void exception_exit(struct pt_regs *regs) { } static inline void context_tracking_task_switch(struct task_struct *prev, struct task_struct *next) { } #endif /* !CONFIG_CONTEXT_TRACKING */ -- cgit v1.2.3-70-g09d2 From 6c1e0256fad84a843d915414e4b5973b7443d48d Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Sun, 24 Feb 2013 01:19:14 +0100 Subject: context_tracking: Restore correct previous context state on exception exit On exception exit, we restore the previous context tracking state based on the regs of the interrupted frame. Iff that frame is in user mode as stated by user_mode() helper, we restore the context tracking user mode. However there is a tiny chunck of low level arch code after we pass through user_enter() and until the CPU eventually resumes userspace. If an exception happens in this tiny area, exception_enter() correctly exits the context tracking user mode but exception_exit() won't restore it because of the value returned by user_mode(regs). As a result we may return to userspace with the wrong context tracking state. To fix this, change exception_enter() to return the context tracking state prior to its call and pass this saved state to exception_exit(). This restores the real context tracking state of the interrupted frame. (May be this patch was suggested to me, I don't recall exactly. If so, sorry for the missing credit). Signed-off-by: Frederic Weisbecker Cc: Li Zhong Cc: Kevin Hilman Cc: Mats Liljegren Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Steven Rostedt Cc: Namhyung Kim Cc: Andrew Morton Cc: Thomas Gleixner Cc: Paul E. McKenney --- arch/x86/kernel/kvm.c | 6 ++-- arch/x86/kernel/traps.c | 65 +++++++++++++++++++++++++--------------- arch/x86/mm/fault.c | 6 ++-- include/linux/context_tracking.h | 19 +++++++----- 4 files changed, 61 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index e8bb0d61ecd..cd6d9a5a42f 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -254,16 +254,18 @@ EXPORT_SYMBOL_GPL(kvm_read_and_reset_pf_reason); dotraplinkage void __kprobes do_async_page_fault(struct pt_regs *regs, unsigned long error_code) { + enum ctx_state prev_state; + switch (kvm_read_and_reset_pf_reason()) { default: do_page_fault(regs, error_code); break; case KVM_PV_REASON_PAGE_NOT_PRESENT: /* page is swapped out by the host. */ - exception_enter(regs); + prev_state = exception_enter(); exit_idle(); kvm_async_pf_task_wait((u32)read_cr2()); - exception_exit(regs); + exception_exit(prev_state); break; case KVM_PV_REASON_PAGE_READY: rcu_irq_enter(); diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index ecc4ccbdd0c..ff6d2271cbe 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -175,34 +175,38 @@ do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, #define DO_ERROR(trapnr, signr, str, name) \ dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ { \ - exception_enter(regs); \ + enum ctx_state prev_state; \ + \ + prev_state = exception_enter(); \ if (notify_die(DIE_TRAP, str, regs, error_code, \ trapnr, signr) == NOTIFY_STOP) { \ - exception_exit(regs); \ + exception_exit(prev_state); \ return; \ } \ conditional_sti(regs); \ do_trap(trapnr, signr, str, regs, error_code, NULL); \ - exception_exit(regs); \ + exception_exit(prev_state); \ } #define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ { \ siginfo_t info; \ + enum ctx_state prev_state; \ + \ info.si_signo = signr; \ info.si_errno = 0; \ info.si_code = sicode; \ info.si_addr = (void __user *)siaddr; \ - exception_enter(regs); \ + prev_state = exception_enter(); \ if (notify_die(DIE_TRAP, str, regs, error_code, \ trapnr, signr) == NOTIFY_STOP) { \ - exception_exit(regs); \ + exception_exit(prev_state); \ return; \ } \ conditional_sti(regs); \ do_trap(trapnr, signr, str, regs, error_code, &info); \ - exception_exit(regs); \ + exception_exit(prev_state); \ } DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV, @@ -225,14 +229,16 @@ DO_ERROR_INFO(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check, /* Runs on IST stack */ dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code) { - exception_enter(regs); + enum ctx_state prev_state; + + prev_state = exception_enter(); if (notify_die(DIE_TRAP, "stack segment", regs, error_code, X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) { preempt_conditional_sti(regs); do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL); preempt_conditional_cli(regs); } - exception_exit(regs); + exception_exit(prev_state); } dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) @@ -240,7 +246,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) static const char str[] = "double fault"; struct task_struct *tsk = current; - exception_enter(regs); + exception_enter(); /* Return not checked because double check cannot be ignored */ notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV); @@ -260,8 +266,9 @@ dotraplinkage void __kprobes do_general_protection(struct pt_regs *regs, long error_code) { struct task_struct *tsk; + enum ctx_state prev_state; - exception_enter(regs); + prev_state = exception_enter(); conditional_sti(regs); #ifdef CONFIG_X86_32 @@ -299,12 +306,14 @@ do_general_protection(struct pt_regs *regs, long error_code) force_sig(SIGSEGV, tsk); exit: - exception_exit(regs); + exception_exit(prev_state); } /* May run on IST stack. */ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_code) { + enum ctx_state prev_state; + #ifdef CONFIG_DYNAMIC_FTRACE /* * ftrace must be first, everything else may cause a recursive crash. @@ -314,7 +323,7 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co ftrace_int3_handler(regs)) return; #endif - exception_enter(regs); + prev_state = exception_enter(); #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, SIGTRAP) == NOTIFY_STOP) @@ -335,7 +344,7 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co preempt_conditional_cli(regs); debug_stack_usage_dec(); exit: - exception_exit(regs); + exception_exit(prev_state); } #ifdef CONFIG_X86_64 @@ -392,11 +401,12 @@ asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs) dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) { struct task_struct *tsk = current; + enum ctx_state prev_state; int user_icebp = 0; unsigned long dr6; int si_code; - exception_enter(regs); + prev_state = exception_enter(); get_debugreg(dr6, 6); @@ -466,7 +476,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) debug_stack_usage_dec(); exit: - exception_exit(regs); + exception_exit(prev_state); } /* @@ -560,17 +570,21 @@ void math_error(struct pt_regs *regs, int error_code, int trapnr) dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code) { - exception_enter(regs); + enum ctx_state prev_state; + + prev_state = exception_enter(); math_error(regs, error_code, X86_TRAP_MF); - exception_exit(regs); + exception_exit(prev_state); } dotraplinkage void do_simd_coprocessor_error(struct pt_regs *regs, long error_code) { - exception_enter(regs); + enum ctx_state prev_state; + + prev_state = exception_enter(); math_error(regs, error_code, X86_TRAP_XF); - exception_exit(regs); + exception_exit(prev_state); } dotraplinkage void @@ -638,7 +652,9 @@ EXPORT_SYMBOL_GPL(math_state_restore); dotraplinkage void __kprobes do_device_not_available(struct pt_regs *regs, long error_code) { - exception_enter(regs); + enum ctx_state prev_state; + + prev_state = exception_enter(); BUG_ON(use_eager_fpu()); #ifdef CONFIG_MATH_EMULATION @@ -649,7 +665,7 @@ do_device_not_available(struct pt_regs *regs, long error_code) info.regs = regs; math_emulate(&info); - exception_exit(regs); + exception_exit(prev_state); return; } #endif @@ -657,15 +673,16 @@ do_device_not_available(struct pt_regs *regs, long error_code) #ifdef CONFIG_X86_32 conditional_sti(regs); #endif - exception_exit(regs); + exception_exit(prev_state); } #ifdef CONFIG_X86_32 dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) { siginfo_t info; + enum ctx_state prev_state; - exception_enter(regs); + prev_state = exception_enter(); local_irq_enable(); info.si_signo = SIGILL; @@ -677,7 +694,7 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code, &info); } - exception_exit(regs); + exception_exit(prev_state); } #endif diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index f946e6ce331..fa8c02de0d2 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -1222,7 +1222,9 @@ good_area: dotraplinkage void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) { - exception_enter(regs); + enum ctx_state prev_state; + + prev_state = exception_enter(); __do_page_fault(regs, error_code); - exception_exit(regs); + exception_exit(prev_state); } diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index 5a69273e93e..365f4a61bf0 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -5,7 +5,6 @@ #include #include -#ifdef CONFIG_CONTEXT_TRACKING struct context_tracking { /* * When active is false, probes are unset in order @@ -14,12 +13,13 @@ struct context_tracking { * may be further optimized using static keys. */ bool active; - enum { + enum ctx_state { IN_KERNEL = 0, IN_USER, } state; }; +#ifdef CONFIG_CONTEXT_TRACKING DECLARE_PER_CPU(struct context_tracking, context_tracking); static inline bool context_tracking_in_user(void) @@ -35,14 +35,19 @@ static inline bool context_tracking_active(void) extern void user_enter(void); extern void user_exit(void); -static inline void exception_enter(struct pt_regs *regs) +static inline enum ctx_state exception_enter(void) { + enum ctx_state prev_ctx; + + prev_ctx = this_cpu_read(context_tracking.state); user_exit(); + + return prev_ctx; } -static inline void exception_exit(struct pt_regs *regs) +static inline void exception_exit(enum ctx_state prev_ctx) { - if (user_mode(regs)) + if (prev_ctx == IN_USER) user_enter(); } @@ -52,8 +57,8 @@ extern void context_tracking_task_switch(struct task_struct *prev, static inline bool context_tracking_in_user(void) { return false; } static inline void user_enter(void) { } static inline void user_exit(void) { } -static inline void exception_enter(struct pt_regs *regs) { } -static inline void exception_exit(struct pt_regs *regs) { } +static inline enum ctx_state exception_enter(void) { return 0; } +static inline void exception_exit(enum ctx_state prev_ctx) { } static inline void context_tracking_task_switch(struct task_struct *prev, struct task_struct *next) { } #endif /* !CONFIG_CONTEXT_TRACKING */ -- cgit v1.2.3-70-g09d2 From 9fbc42eac1f6917081dc3b39922b2f1c57fdff28 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 25 Feb 2013 17:25:39 +0100 Subject: cputime: Dynamically scale cputime for full dynticks accounting The full dynticks cputime accounting is able to account either using the tick or the context tracking subsystem. This way the housekeeping CPU can keep the low overhead tick based solution. This latter mode has a low jiffies resolution granularity and need to be scaled against CFS precise runtime accounting to improve its result. We are doing this for CONFIG_TICK_CPU_ACCOUNTING, now we also need to expand it to full dynticks accounting dynamic off-case as well. Signed-off-by: Frederic Weisbecker Cc: Li Zhong Cc: Kevin Hilman Cc: Mats Liljegren Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Steven Rostedt Cc: Namhyung Kim Cc: Andrew Morton Cc: Thomas Gleixner Cc: Paul E. McKenney --- include/linux/sched.h | 4 +- kernel/fork.c | 2 +- kernel/sched/cputime.c | 154 +++++++++++++++++++++++++------------------------ 3 files changed, 83 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index d35d2b6ddbf..8d1b6034d80 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -570,7 +570,7 @@ struct signal_struct { cputime_t utime, stime, cutime, cstime; cputime_t gtime; cputime_t cgtime; -#ifndef CONFIG_VIRT_CPU_ACCOUNTING +#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE struct cputime prev_cputime; #endif unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw; @@ -1327,7 +1327,7 @@ struct task_struct { cputime_t utime, stime, utimescaled, stimescaled; cputime_t gtime; -#ifndef CONFIG_VIRT_CPU_ACCOUNTING +#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE struct cputime prev_cputime; #endif #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN diff --git a/kernel/fork.c b/kernel/fork.c index 8d932b1c905..f3146ed4907 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1230,7 +1230,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->utime = p->stime = p->gtime = 0; p->utimescaled = p->stimescaled = 0; -#ifndef CONFIG_VIRT_CPU_ACCOUNTING +#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE p->prev_cputime.utime = p->prev_cputime.stime = 0; #endif #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index ed12cbb135f..024fe1998ad 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -388,82 +388,10 @@ static inline void irqtime_account_process_tick(struct task_struct *p, int user_ struct rq *rq) {} #endif /* CONFIG_IRQ_TIME_ACCOUNTING */ -#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE -/* - * Account a single tick of cpu time. - * @p: the process that the cpu time gets accounted to - * @user_tick: indicates if the tick is a user or a system tick - */ -void account_process_tick(struct task_struct *p, int user_tick) -{ - cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy); - struct rq *rq = this_rq(); - - if (vtime_accounting_enabled()) - return; - - if (sched_clock_irqtime) { - irqtime_account_process_tick(p, user_tick, rq); - return; - } - - if (steal_account_process_tick()) - return; - - if (user_tick) - account_user_time(p, cputime_one_jiffy, one_jiffy_scaled); - else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET)) - account_system_time(p, HARDIRQ_OFFSET, cputime_one_jiffy, - one_jiffy_scaled); - else - account_idle_time(cputime_one_jiffy); -} - -/* - * Account multiple ticks of steal time. - * @p: the process from which the cpu time has been stolen - * @ticks: number of stolen ticks - */ -void account_steal_ticks(unsigned long ticks) -{ - account_steal_time(jiffies_to_cputime(ticks)); -} - -/* - * Account multiple ticks of idle time. - * @ticks: number of stolen ticks - */ -void account_idle_ticks(unsigned long ticks) -{ - - if (sched_clock_irqtime) { - irqtime_account_idle_ticks(ticks); - return; - } - - account_idle_time(jiffies_to_cputime(ticks)); -} -#endif /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */ - /* * Use precise platform statistics if available: */ #ifdef CONFIG_VIRT_CPU_ACCOUNTING -void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st) -{ - *ut = p->utime; - *st = p->stime; -} - -void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st) -{ - struct task_cputime cputime; - - thread_group_cputime(p, &cputime); - - *ut = cputime.utime; - *st = cputime.stime; -} #ifndef __ARCH_HAS_VTIME_TASK_SWITCH void vtime_task_switch(struct task_struct *prev) @@ -518,8 +446,80 @@ void vtime_account_irq_enter(struct task_struct *tsk) } EXPORT_SYMBOL_GPL(vtime_account_irq_enter); #endif /* __ARCH_HAS_VTIME_ACCOUNT */ +#endif /* CONFIG_VIRT_CPU_ACCOUNTING */ + + +#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE +void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st) +{ + *ut = p->utime; + *st = p->stime; +} -#else /* !CONFIG_VIRT_CPU_ACCOUNTING */ +void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st) +{ + struct task_cputime cputime; + + thread_group_cputime(p, &cputime); + + *ut = cputime.utime; + *st = cputime.stime; +} +#else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */ +/* + * Account a single tick of cpu time. + * @p: the process that the cpu time gets accounted to + * @user_tick: indicates if the tick is a user or a system tick + */ +void account_process_tick(struct task_struct *p, int user_tick) +{ + cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy); + struct rq *rq = this_rq(); + + if (vtime_accounting_enabled()) + return; + + if (sched_clock_irqtime) { + irqtime_account_process_tick(p, user_tick, rq); + return; + } + + if (steal_account_process_tick()) + return; + + if (user_tick) + account_user_time(p, cputime_one_jiffy, one_jiffy_scaled); + else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET)) + account_system_time(p, HARDIRQ_OFFSET, cputime_one_jiffy, + one_jiffy_scaled); + else + account_idle_time(cputime_one_jiffy); +} + +/* + * Account multiple ticks of steal time. + * @p: the process from which the cpu time has been stolen + * @ticks: number of stolen ticks + */ +void account_steal_ticks(unsigned long ticks) +{ + account_steal_time(jiffies_to_cputime(ticks)); +} + +/* + * Account multiple ticks of idle time. + * @ticks: number of stolen ticks + */ +void account_idle_ticks(unsigned long ticks) +{ + + if (sched_clock_irqtime) { + irqtime_account_idle_ticks(ticks); + return; + } + + account_idle_time(jiffies_to_cputime(ticks)); +} static cputime_t scale_stime(cputime_t stime, cputime_t rtime, cputime_t total) { @@ -545,6 +545,12 @@ static void cputime_adjust(struct task_cputime *curr, { cputime_t rtime, stime, total; + if (vtime_accounting_enabled()) { + *ut = curr->utime; + *st = curr->stime; + return; + } + stime = curr->stime; total = stime + curr->utime; @@ -597,7 +603,7 @@ void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime thread_group_cputime(p, &cputime); cputime_adjust(&cputime, &p->signal->prev_cputime, ut, st); } -#endif /* !CONFIG_VIRT_CPU_ACCOUNTING */ +#endif /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */ #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN static unsigned long long vtime_delta(struct task_struct *tsk) -- cgit v1.2.3-70-g09d2 From 090096bf3db1c281ddd034573260045888a68fea Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 6 Mar 2013 15:39:42 +0000 Subject: net: generic fdb support for drivers without ndo_fdb_ If the driver does not support the ndo_op use the generic handler for it. This should work in the majority of cases. Eventually the fdb_dflt_add call gets translated into a __dev_set_rx_mode() call which should handle hardware support for filtering via the IFF_UNICAST_FLT flag. Namely IFF_UNICAST_FLT indicates if the hardware can do unicast address filtering. If no support is available the device is put into promisc mode. Signed-off-by: Vlad Yasevich Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 9 ++++++ net/core/rtnetlink.c | 81 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 489dd7bb28e..f28544b2f9a 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -69,6 +69,15 @@ extern int ndo_dflt_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, int idx); +extern int ndo_dflt_fdb_add(struct ndmsg *ndm, + struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, + u16 flags); +extern int ndo_dflt_fdb_del(struct ndmsg *ndm, + struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr); extern int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev, u16 mode); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b376410ff25..f95b6fbc29e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2048,6 +2048,38 @@ errout: rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); } +/** + * ndo_dflt_fdb_add - default netdevice operation to add an FDB entry + */ +int ndo_dflt_fdb_add(struct ndmsg *ndm, + struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, + u16 flags) +{ + int err = -EINVAL; + + /* If aging addresses are supported device will need to + * implement its own handler for this. + */ + if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) { + pr_info("%s: FDB only supports static addresses\n", dev->name); + return err; + } + + if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) + err = dev_uc_add_excl(dev, addr); + else if (is_multicast_ether_addr(addr)) + err = dev_mc_add_excl(dev, addr); + + /* Only return duplicate errors if NLM_F_EXCL is set */ + if (err == -EEXIST && !(flags & NLM_F_EXCL)) + err = 0; + + return err; +} +EXPORT_SYMBOL(ndo_dflt_fdb_add); + static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); @@ -2100,10 +2132,13 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) } /* Embedded bridge, macvlan, and any other device support */ - if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_add) { - err = dev->netdev_ops->ndo_fdb_add(ndm, tb, - dev, addr, - nlh->nlmsg_flags); + if ((ndm->ndm_flags & NTF_SELF)) { + if (dev->netdev_ops->ndo_fdb_add) + err = dev->netdev_ops->ndo_fdb_add(ndm, tb, dev, addr, + nlh->nlmsg_flags); + else + err = ndo_dflt_fdb_add(ndm, tb, dev, addr, + nlh->nlmsg_flags); if (!err) { rtnl_fdb_notify(dev, addr, RTM_NEWNEIGH); @@ -2114,6 +2149,35 @@ out: return err; } +/** + * ndo_dflt_fdb_del - default netdevice operation to delete an FDB entry + */ +int ndo_dflt_fdb_del(struct ndmsg *ndm, + struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr) +{ + int err = -EOPNOTSUPP; + + /* If aging addresses are supported device will need to + * implement its own handler for this. + */ + if (ndm->ndm_state & NUD_PERMANENT) { + pr_info("%s: FDB only supports static addresses\n", dev->name); + return -EINVAL; + } + + if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) + err = dev_uc_del(dev, addr); + else if (is_multicast_ether_addr(addr)) + err = dev_mc_del(dev, addr); + else + err = -EINVAL; + + return err; +} +EXPORT_SYMBOL(ndo_dflt_fdb_del); + static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); @@ -2171,8 +2235,11 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) } /* Embedded bridge, macvlan, and any other device support */ - if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) { - err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr); + if (ndm->ndm_flags & NTF_SELF) { + if (dev->netdev_ops->ndo_fdb_del) + err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr); + else + err = ndo_dflt_fdb_del(ndm, tb, dev, addr); if (!err) { rtnl_fdb_notify(dev, addr, RTM_DELNEIGH); @@ -2257,6 +2324,8 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) if (dev->netdev_ops->ndo_fdb_dump) idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx); + else + ndo_dflt_fdb_dump(skb, cb, dev, idx); } rcu_read_unlock(); -- cgit v1.2.3-70-g09d2 From b2fb4f54ecd47c42413d54b4666b06cf93c05abf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 6 Mar 2013 12:58:01 +0000 Subject: tcp: uninline tcp_prequeue() tcp_prequeue() became too big to be inlined. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 45 +-------------------------------------------- net/ipv4/tcp_ipv4.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index cf0694d4ad6..a2baa5e4ba3 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1030,50 +1030,7 @@ static inline void tcp_prequeue_init(struct tcp_sock *tp) #endif } -/* Packet is added to VJ-style prequeue for processing in process - * context, if a reader task is waiting. Apparently, this exciting - * idea (VJ's mail "Re: query about TCP header on tcp-ip" of 07 Sep 93) - * failed somewhere. Latency? Burstiness? Well, at least now we will - * see, why it failed. 8)8) --ANK - * - * NOTE: is this not too big to inline? - */ -static inline bool tcp_prequeue(struct sock *sk, struct sk_buff *skb) -{ - struct tcp_sock *tp = tcp_sk(sk); - - if (sysctl_tcp_low_latency || !tp->ucopy.task) - return false; - - if (skb->len <= tcp_hdrlen(skb) && - skb_queue_len(&tp->ucopy.prequeue) == 0) - return false; - - __skb_queue_tail(&tp->ucopy.prequeue, skb); - tp->ucopy.memory += skb->truesize; - if (tp->ucopy.memory > sk->sk_rcvbuf) { - struct sk_buff *skb1; - - BUG_ON(sock_owned_by_user(sk)); - - while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) { - sk_backlog_rcv(sk, skb1); - NET_INC_STATS_BH(sock_net(sk), - LINUX_MIB_TCPPREQUEUEDROPPED); - } - - tp->ucopy.memory = 0; - } else if (skb_queue_len(&tp->ucopy.prequeue) == 1) { - wake_up_interruptible_sync_poll(sk_sleep(sk), - POLLIN | POLLRDNORM | POLLRDBAND); - if (!inet_csk_ack_scheduled(sk)) - inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, - (3 * tcp_rto_min(sk)) / 4, - TCP_RTO_MAX); - } - return true; -} - +extern bool tcp_prequeue(struct sock *sk, struct sk_buff *skb); #undef STATE_TRACE diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 4a8ec457310..8cdee120a50 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1950,6 +1950,50 @@ void tcp_v4_early_demux(struct sk_buff *skb) } } +/* Packet is added to VJ-style prequeue for processing in process + * context, if a reader task is waiting. Apparently, this exciting + * idea (VJ's mail "Re: query about TCP header on tcp-ip" of 07 Sep 93) + * failed somewhere. Latency? Burstiness? Well, at least now we will + * see, why it failed. 8)8) --ANK + * + */ +bool tcp_prequeue(struct sock *sk, struct sk_buff *skb) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (sysctl_tcp_low_latency || !tp->ucopy.task) + return false; + + if (skb->len <= tcp_hdrlen(skb) && + skb_queue_len(&tp->ucopy.prequeue) == 0) + return false; + + __skb_queue_tail(&tp->ucopy.prequeue, skb); + tp->ucopy.memory += skb->truesize; + if (tp->ucopy.memory > sk->sk_rcvbuf) { + struct sk_buff *skb1; + + BUG_ON(sock_owned_by_user(sk)); + + while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) { + sk_backlog_rcv(sk, skb1); + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPPREQUEUEDROPPED); + } + + tp->ucopy.memory = 0; + } else if (skb_queue_len(&tp->ucopy.prequeue) == 1) { + wake_up_interruptible_sync_poll(sk_sleep(sk), + POLLIN | POLLRDNORM | POLLRDBAND); + if (!inet_csk_ack_scheduled(sk)) + inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, + (3 * tcp_rto_min(sk)) / 4, + TCP_RTO_MAX); + } + return true; +} +EXPORT_SYMBOL(tcp_prequeue); + /* * From tcp_input.c */ -- cgit v1.2.3-70-g09d2 From 7f0e44ac9f7f12a2519bfed9ea4df3c1471bd8bb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 7 Mar 2013 04:20:32 +0000 Subject: ipv6 flowlabel: add __rcu annotations Commit 18367681a10b (ipv6 flowlabel: Convert np->ipv6_fl_list to RCU.) omitted proper __rcu annotations. Signed-off-by: Eric Dumazet Cc: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/ipv6.h | 8 ++++---- net/ipv6/ip6_flowlabel.c | 11 ++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 64d12e77719..988c9f28f0f 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -217,7 +217,7 @@ struct ipv6_txoptions { }; struct ip6_flowlabel { - struct ip6_flowlabel *next; + struct ip6_flowlabel __rcu *next; __be32 label; atomic_t users; struct in6_addr dst; @@ -238,9 +238,9 @@ struct ip6_flowlabel { #define IPV6_FLOWLABEL_MASK cpu_to_be32(0x000FFFFF) struct ipv6_fl_socklist { - struct ipv6_fl_socklist *next; - struct ip6_flowlabel *fl; - struct rcu_head rcu; + struct ipv6_fl_socklist __rcu *next; + struct ip6_flowlabel *fl; + struct rcu_head rcu; }; extern struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label); diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index b973ed3d06c..46e88433ec7 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -144,7 +144,9 @@ static void ip6_fl_gc(unsigned long dummy) spin_lock(&ip6_fl_lock); for (i=0; i<=FL_HASH_MASK; i++) { - struct ip6_flowlabel *fl, **flp; + struct ip6_flowlabel *fl; + struct ip6_flowlabel __rcu **flp; + flp = &fl_ht[i]; while ((fl = rcu_dereference_protected(*flp, lockdep_is_held(&ip6_fl_lock))) != NULL) { @@ -179,7 +181,9 @@ static void __net_exit ip6_fl_purge(struct net *net) spin_lock(&ip6_fl_lock); for (i = 0; i <= FL_HASH_MASK; i++) { - struct ip6_flowlabel *fl, **flp; + struct ip6_flowlabel *fl; + struct ip6_flowlabel __rcu **flp; + flp = &fl_ht[i]; while ((fl = rcu_dereference_protected(*flp, lockdep_is_held(&ip6_fl_lock))) != NULL) { @@ -506,7 +510,8 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) struct ipv6_pinfo *np = inet6_sk(sk); struct in6_flowlabel_req freq; struct ipv6_fl_socklist *sfl1=NULL; - struct ipv6_fl_socklist *sfl, **sflp; + struct ipv6_fl_socklist *sfl; + struct ipv6_fl_socklist __rcu **sflp; struct ip6_flowlabel *fl, *fl1 = NULL; -- cgit v1.2.3-70-g09d2 From e383c467ceeee3b040444d6dcd27f331d72b1426 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 8 Mar 2013 13:44:26 +0100 Subject: ASoC: core: Drop unused "dapm" field form soc_enum struct This field was added in commit 2e72f8e ("ASoC: New enum type: value_enum"), but has never been used since. Considering that the soc_enum struct is usually shared between all instances of a CODEC, it also doesn't make much sense to have a pointer to DAPM specific data in it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index a6a059ca387..c84062b2222 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1086,7 +1086,6 @@ struct soc_enum { unsigned int mask; const char * const *texts; const unsigned int *values; - void *dapm; }; /* codec IO */ -- cgit v1.2.3-70-g09d2 From a93f8e76a446e0a146a169cc2cc82bf1e145ad35 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 8 Mar 2013 13:44:27 +0100 Subject: ASoC: core: Remove unused "n_widgets" field from snd_soc_dapm struct Commit 497098be ("ASoC: dapm: Remove bodges for no-widget CODECs") removed the last user of the n_widgets field. Currently it is incremented for each widget added, but the value is never used, so we can remove it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 - sound/soc/soc-dapm.c | 1 - 2 files changed, 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e1ef63d4a5c..d4c004929ae 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -565,7 +565,6 @@ struct snd_soc_dapm_update { /* DAPM context */ struct snd_soc_dapm_context { - int n_widgets; /* number of widgets in this context */ enum snd_soc_bias_level bias_level; enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 1d6a9b3ceb2..625d4824abb 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3123,7 +3123,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, break; } - dapm->n_widgets++; w->dapm = dapm; w->codec = dapm->codec; w->platform = dapm->platform; -- cgit v1.2.3-70-g09d2 From 4fa89346fbc34750f96ec0c1b2b59b15596ab333 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 8 Mar 2013 13:52:09 +0100 Subject: ALSA: ASoC: add codec driver for TI TAS5086 This patch adds a driver for TI's TA5086 6-channel PWM processor. This chip has a very unusual register layout, specifically because the registers are of unequal size, and multi-byte registers require bulk writes to take effect. Regmap does not support these kind of mappings. Currently, the driver does not touch any of the registers >= 0x20, so it doesn't matter, because the register map is mapped to an 8-bit array. In case more features will be added in the future that require access to higher registers, the entire regmap H/W I/O routines have to be open-coded. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/ti,tas5086.txt | 32 ++ include/sound/tas5086.h | 7 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tas5086.c | 601 +++++++++++++++++++++ 5 files changed, 646 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/ti,tas5086.txt create mode 100644 include/sound/tas5086.h create mode 100644 sound/soc/codecs/tas5086.c (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/ti,tas5086.txt b/Documentation/devicetree/bindings/sound/ti,tas5086.txt new file mode 100644 index 00000000000..8ea4f5b4818 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,tas5086.txt @@ -0,0 +1,32 @@ +Texas Instruments TAS5086 6-channel PWM Processor + +Required properties: + + - compatible: Should contain "ti,tas5086". + - reg: The i2c address. Should contain <0x1b>. + +Optional properties: + + - reset-gpio: A GPIO spec to define which pin is connected to the + chip's !RESET pin. If specified, the driver will + assert a hardware reset at probe time. + + - ti,charge-period: This property should contain the time in microseconds + that closely matches the external single-ended + split-capacitor charge period. The hardware chip + waits for this period of time before starting the + PWM signals. This helps reduce pops and clicks. + + When not specified, the hardware default of 1300ms + is retained. + +Examples: + + i2c_bus { + tas5086@1b { + compatible = "ti,tas5086"; + reg = <0x1b>; + reset-gpio = <&gpio 23 0>; + ti,charge-period = <156000>; + }; + }; diff --git a/include/sound/tas5086.h b/include/sound/tas5086.h new file mode 100644 index 00000000000..aac481b7db8 --- /dev/null +++ b/include/sound/tas5086.h @@ -0,0 +1,7 @@ +#ifndef _SND_SOC_CODEC_TAS5086_H_ +#define _SND_SOC_CODEC_TAS5086_H_ + +#define TAS5086_CLK_IDX_MCLK 0 +#define TAS5086_CLK_IDX_SCLK 1 + +#endif /* _SND_SOC_CODEC_TAS5086_H_ */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 45b72561c61..86b35245e55 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -63,6 +63,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STA32X if I2C select SND_SOC_STA529 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS + select SND_SOC_TAS5086 if I2C select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC32X4 if I2C @@ -320,6 +321,9 @@ config SND_SOC_STA529 config SND_SOC_STAC9766 tristate +config SND_SOC_TAS5086 + tristate + config SND_SOC_TLV320AIC23 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 6a3b3c3b8b4..8077bc2b4f4 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -55,6 +55,7 @@ snd-soc-ssm2602-objs := ssm2602.o snd-soc-sta32x-objs := sta32x.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o +snd-soc-tas5086-objs := tas5086.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o @@ -177,6 +178,7 @@ obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o +obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c new file mode 100644 index 00000000000..008bea4d620 --- /dev/null +++ b/sound/soc/codecs/tas5086.c @@ -0,0 +1,601 @@ +/* + * TAS5086 ASoC codec driver + * + * Copyright (c) 2013 Daniel Mack + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * TODO: + * - implement DAPM and input muxing + * - implement modulation limit + * - implement non-default PWM start + * + * Note that this chip has a very unusual register layout, specifically + * because the registers are of unequal size, and multi-byte registers + * require bulk writes to take effect. Regmap does not support that kind + * of devices. + * + * Currently, the driver does not touch any of the registers >= 0x20, so + * it doesn't matter because the entire map can be accessed as 8-bit + * array. In case more features will be added in the future + * that require access to higher registers, the entire regmap H/W I/O + * routines have to be open-coded. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAS5086_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define TAS5086_PCM_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) + +/* + * TAS5086 registers + */ +#define TAS5086_CLOCK_CONTROL 0x00 /* Clock control register */ +#define TAS5086_CLOCK_RATE(val) (val << 5) +#define TAS5086_CLOCK_RATE_MASK (0x7 << 5) +#define TAS5086_CLOCK_RATIO(val) (val << 2) +#define TAS5086_CLOCK_RATIO_MASK (0x7 << 2) +#define TAS5086_CLOCK_SCLK_RATIO_48 (1 << 1) +#define TAS5086_CLOCK_VALID (1 << 0) + +#define TAS5086_DEEMPH_MASK 0x03 +#define TAS5086_SOFT_MUTE_ALL 0x3f + +#define TAS5086_DEV_ID 0x01 /* Device ID register */ +#define TAS5086_ERROR_STATUS 0x02 /* Error status register */ +#define TAS5086_SYS_CONTROL_1 0x03 /* System control register 1 */ +#define TAS5086_SERIAL_DATA_IF 0x04 /* Serial data interface register */ +#define TAS5086_SYS_CONTROL_2 0x05 /* System control register 2 */ +#define TAS5086_SOFT_MUTE 0x06 /* Soft mute register */ +#define TAS5086_MASTER_VOL 0x07 /* Master volume */ +#define TAS5086_CHANNEL_VOL(X) (0x08 + (X)) /* Channel 1-6 volume */ +#define TAS5086_VOLUME_CONTROL 0x09 /* Volume control register */ +#define TAS5086_MOD_LIMIT 0x10 /* Modulation limit register */ +#define TAS5086_PWM_START 0x18 /* PWM start register */ +#define TAS5086_SURROUND 0x19 /* Surround register */ +#define TAS5086_SPLIT_CAP_CHARGE 0x1a /* Split cap charge period register */ +#define TAS5086_OSC_TRIM 0x1b /* Oscillator trim register */ +#define TAS5086_BKNDERR 0x1c + +/* + * Default TAS5086 power-up configuration + */ +static const struct reg_default tas5086_reg_defaults[] = { + { 0x00, 0x6c }, + { 0x01, 0x03 }, + { 0x02, 0x00 }, + { 0x03, 0xa0 }, + { 0x04, 0x05 }, + { 0x05, 0x60 }, + { 0x06, 0x00 }, + { 0x07, 0xff }, + { 0x08, 0x30 }, + { 0x09, 0x30 }, + { 0x0a, 0x30 }, + { 0x0b, 0x30 }, + { 0x0c, 0x30 }, + { 0x0d, 0x30 }, + { 0x0e, 0xb1 }, + { 0x0f, 0x00 }, + { 0x10, 0x02 }, + { 0x11, 0x00 }, + { 0x12, 0x00 }, + { 0x13, 0x00 }, + { 0x14, 0x00 }, + { 0x15, 0x00 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, + { 0x18, 0x3f }, + { 0x19, 0x00 }, + { 0x1a, 0x18 }, + { 0x1b, 0x82 }, + { 0x1c, 0x05 }, +}; + +static bool tas5086_accessible_reg(struct device *dev, unsigned int reg) +{ + return !((reg == 0x0f) || (reg >= 0x11 && reg <= 0x17)); +} + +static bool tas5086_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS5086_DEV_ID: + case TAS5086_ERROR_STATUS: + return true; + } + + return false; +} + +static bool tas5086_writeable_reg(struct device *dev, unsigned int reg) +{ + return tas5086_accessible_reg(dev, reg) && (reg != TAS5086_DEV_ID); +} + +struct tas5086_private { + struct regmap *regmap; + unsigned int mclk, sclk; + unsigned int format; + bool deemph; + /* Current sample rate for de-emphasis control */ + int rate; + /* GPIO driving Reset pin, if any */ + int gpio_nreset; +}; + +static int tas5086_deemph[] = { 0, 32000, 44100, 48000 }; + +static int tas5086_set_deemph(struct snd_soc_codec *codec) +{ + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + int i, val = 0; + + if (priv->deemph) + for (i = 0; i < ARRAY_SIZE(tas5086_deemph); i++) + if (tas5086_deemph[i] == priv->rate) + val = i; + + return regmap_update_bits(priv->regmap, TAS5086_SYS_CONTROL_1, + TAS5086_DEEMPH_MASK, val); +} + +static int tas5086_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = priv->deemph; + + return 0; +} + +static int tas5086_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + + priv->deemph = ucontrol->value.enumerated.item[0]; + + return tas5086_set_deemph(codec); +} + + +static int tas5086_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case TAS5086_CLK_IDX_MCLK: + priv->mclk = freq; + break; + case TAS5086_CLK_IDX_SCLK: + priv->sclk = freq; + break; + } + + return 0; +} + +static int tas5086_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + + /* The TAS5086 can only be slave to all clocks */ + if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "Invalid clocking mode\n"); + return -EINVAL; + } + + /* we need to refer to the data format from hw_params() */ + priv->format = format; + + return 0; +} + +static const int tas5086_sample_rates[] = { + 32000, 38000, 44100, 48000, 88200, 96000, 176400, 192000 +}; + +static const int tas5086_ratios[] = { + 64, 128, 192, 256, 384, 512 +}; + +static int index_in_array(const int *array, int len, int needle) +{ + int i; + + for (i = 0; i < len; i++) + if (array[i] == needle) + return i; + + return -ENOENT; +} + +static int tas5086_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + unsigned int val; + int ret; + + priv->rate = params_rate(params); + + /* Look up the sample rate and refer to the offset in the list */ + val = index_in_array(tas5086_sample_rates, + ARRAY_SIZE(tas5086_sample_rates), priv->rate); + + if (val < 0) { + dev_err(codec->dev, "Invalid sample rate\n"); + return -EINVAL; + } + + ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, + TAS5086_CLOCK_RATE_MASK, + TAS5086_CLOCK_RATE(val)); + if (ret < 0) + return ret; + + /* MCLK / Fs ratio */ + val = index_in_array(tas5086_ratios, ARRAY_SIZE(tas5086_ratios), + priv->mclk / priv->rate); + if (val < 0) { + dev_err(codec->dev, "Inavlid MCLK / Fs ratio\n"); + return -EINVAL; + } + + ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, + TAS5086_CLOCK_RATIO_MASK, + TAS5086_CLOCK_RATIO(val)); + if (ret < 0) + return ret; + + + ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, + TAS5086_CLOCK_SCLK_RATIO_48, + (priv->sclk == 48 * priv->rate) ? + TAS5086_CLOCK_SCLK_RATIO_48 : 0); + if (ret < 0) + return ret; + + /* + * The chip has a very unituitive register mapping and muxes information + * about data format and sample depth into the same register, but not on + * a logical bit-boundary. Hence, we have to refer to the format passed + * in the set_dai_fmt() callback and set up everything from here. + * + * First, determine the 'base' value, using the format ... + */ + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + val = 0x00; + break; + case SND_SOC_DAIFMT_I2S: + val = 0x03; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = 0x06; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + /* ... then add the offset for the sample bit depth. */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + val += 0; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val += 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + val += 2; + break; + default: + dev_err(codec->dev, "Invalid bit width\n"); + return -EINVAL; + }; + + ret = regmap_write(priv->regmap, TAS5086_SERIAL_DATA_IF, val); + if (ret < 0) + return ret; + + /* clock is considered valid now */ + ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, + TAS5086_CLOCK_VALID, TAS5086_CLOCK_VALID); + if (ret < 0) + return ret; + + return tas5086_set_deemph(codec); +} + +static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + + if (mute) + val = TAS5086_SOFT_MUTE_ALL; + + return regmap_write(priv->regmap, TAS5086_SOFT_MUTE, val); +} + +/* TAS5086 controls */ +static const DECLARE_TLV_DB_SCALE(tas5086_dac_tlv, -10350, 50, 1); + +static const struct snd_kcontrol_new tas5086_controls[] = { + SOC_SINGLE_TLV("Master Playback Volume", TAS5086_MASTER_VOL, + 0, 0xff, 1, tas5086_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume", + TAS5086_CHANNEL_VOL(0), TAS5086_CHANNEL_VOL(1), + 0, 0xff, 1, tas5086_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume", + TAS5086_CHANNEL_VOL(2), TAS5086_CHANNEL_VOL(3), + 0, 0xff, 1, tas5086_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume", + TAS5086_CHANNEL_VOL(4), TAS5086_CHANNEL_VOL(5), + 0, 0xff, 1, tas5086_dac_tlv), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + tas5086_get_deemph, tas5086_put_deemph), +}; + +static const struct snd_soc_dai_ops tas5086_dai_ops = { + .hw_params = tas5086_hw_params, + .set_sysclk = tas5086_set_dai_sysclk, + .set_fmt = tas5086_set_dai_fmt, + .mute_stream = tas5086_mute_stream, +}; + +static struct snd_soc_dai_driver tas5086_dai = { + .name = "tas5086-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 6, + .rates = TAS5086_PCM_RATES, + .formats = TAS5086_PCM_FORMATS, + }, + .ops = &tas5086_dai_ops, +}; + +#ifdef CONFIG_PM +static int tas5086_soc_resume(struct snd_soc_codec *codec) +{ + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + + /* Restore codec state */ + return regcache_sync(priv->regmap); +} +#else +#define tas5086_soc_resume NULL +#endif /* CONFIG_PM */ + +#ifdef CONFIG_OF +static const struct of_device_id tas5086_dt_ids[] = { + { .compatible = "ti,tas5086", }, + { } +}; +MODULE_DEVICE_TABLE(of, tas5086_dt_ids); +#endif + +/* charge period values in microseconds */ +static const int tas5086_charge_period[] = { + 13000, 16900, 23400, 31200, 41600, 54600, 72800, 96200, + 130000, 156000, 234000, 312000, 416000, 546000, 728000, 962000, + 1300000, 169000, 2340000, 3120000, 4160000, 5460000, 7280000, 9620000, +}; + +static int tas5086_probe(struct snd_soc_codec *codec) +{ + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + int charge_period = 1300000; /* hardware default is 1300 ms */ + int i, ret; + + if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) { + struct device_node *of_node = codec->dev->of_node; + of_property_read_u32(of_node, "ti,charge-period", &charge_period); + } + + /* lookup and set split-capacitor charge period */ + if (charge_period == 0) { + regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0); + } else { + i = index_in_array(tas5086_charge_period, + ARRAY_SIZE(tas5086_charge_period), + charge_period); + if (i >= 0) + regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, + i + 0x08); + else + dev_warn(codec->dev, + "Invalid split-cap charge period of %d ns.\n", + charge_period); + } + + /* enable factory trim */ + ret = regmap_write(priv->regmap, TAS5086_OSC_TRIM, 0x00); + if (ret < 0) + return ret; + + /* start all channels */ + ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x20); + if (ret < 0) + return ret; + + /* set master volume to 0 dB */ + ret = regmap_write(priv->regmap, TAS5086_MASTER_VOL, 0x30); + if (ret < 0) + return ret; + + /* mute all channels for now */ + ret = regmap_write(priv->regmap, TAS5086_SOFT_MUTE, + TAS5086_SOFT_MUTE_ALL); + if (ret < 0) + return ret; + + return 0; +} + +static int tas5086_remove(struct snd_soc_codec *codec) +{ + struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); + + if (gpio_is_valid(priv->gpio_nreset)) + /* Set codec to the reset state */ + gpio_set_value(priv->gpio_nreset, 0); + + return 0; +}; + +static struct snd_soc_codec_driver soc_codec_dev_tas5086 = { + .probe = tas5086_probe, + .remove = tas5086_remove, + .resume = tas5086_soc_resume, + .controls = tas5086_controls, + .num_controls = ARRAY_SIZE(tas5086_controls), +}; + +static const struct i2c_device_id tas5086_i2c_id[] = { + { "tas5086", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id); + +static const struct regmap_config tas5086_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ARRAY_SIZE(tas5086_reg_defaults), + .reg_defaults = tas5086_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5086_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = tas5086_volatile_reg, + .writeable_reg = tas5086_writeable_reg, + .readable_reg = tas5086_accessible_reg, +}; + +static int tas5086_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tas5086_private *priv; + struct device *dev = &i2c->dev; + int gpio_nreset = -EINVAL; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init_i2c(i2c, &tas5086_regmap); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, priv); + + if (of_match_device(of_match_ptr(tas5086_dt_ids), dev)) { + struct device_node *of_node = dev->of_node; + gpio_nreset = of_get_named_gpio(of_node, "reset-gpio", 0); + } + + if (gpio_is_valid(gpio_nreset)) + if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset")) + gpio_nreset = -EINVAL; + + if (gpio_is_valid(gpio_nreset)) { + /* Reset codec - minimum assertion time is 400ns */ + gpio_direction_output(gpio_nreset, 0); + udelay(1); + gpio_set_value(gpio_nreset, 1); + + /* Codec needs ~15ms to wake up */ + msleep(15); + } + + priv->gpio_nreset = gpio_nreset; + + /* The TAS5086 always returns 0x03 in its TAS5086_DEV_ID register */ + ret = regmap_read(priv->regmap, TAS5086_DEV_ID, &i); + if (ret < 0) + return ret; + + if (i != 0x3) { + dev_err(dev, + "Failed to identify TAS5086 codec (got %02x)\n", i); + return -ENODEV; + } + + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tas5086, + &tas5086_dai, 1); +} + +static int tas5086_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + return 0; +} + +static struct i2c_driver tas5086_i2c_driver = { + .driver = { + .name = "tas5086", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tas5086_dt_ids), + }, + .id_table = tas5086_i2c_id, + .probe = tas5086_i2c_probe, + .remove = tas5086_i2c_remove, +}; + +static int __init tas5086_modinit(void) +{ + return i2c_add_driver(&tas5086_i2c_driver); +} +module_init(tas5086_modinit); + +static void __exit tas5086_modexit(void) +{ + i2c_del_driver(&tas5086_i2c_driver); +} +module_exit(tas5086_modexit); + +MODULE_AUTHOR("Daniel Mack "); +MODULE_DESCRIPTION("Texas Instruments TAS5086 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From bed71748346ae0807c7f7a2913965508dbd61403 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 30 Jan 2013 11:50:56 -0300 Subject: Bluetooth: Rename hci_acl_disconn As hci_acl_disconn function basically sends the HCI Disconnect Command and it is used to disconnect ACL, SCO and LE links, renaming it to hci_disconnect is more suitable. Signed-off-by: Andre Guedes Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_conn.c | 4 ++-- net/bluetooth/hci_core.c | 2 +- net/bluetooth/hci_event.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 90cf75afcb0..787d3b9bbd5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -574,7 +574,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, return NULL; } -void hci_acl_disconn(struct hci_conn *conn, __u8 reason); +void hci_disconnect(struct hci_conn *conn, __u8 reason); void hci_setup_sync(struct hci_conn *conn, __u16 handle); void hci_sco_setup(struct hci_conn *conn, __u8 status); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 4925a02ae7e..b9f90169940 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -117,7 +117,7 @@ static void hci_acl_create_connection_cancel(struct hci_conn *conn) hci_send_cmd(conn->hdev, HCI_OP_CREATE_CONN_CANCEL, sizeof(cp), &cp); } -void hci_acl_disconn(struct hci_conn *conn, __u8 reason) +void hci_disconnect(struct hci_conn *conn, __u8 reason) { struct hci_cp_disconnect cp; @@ -253,7 +253,7 @@ static void hci_conn_disconnect(struct hci_conn *conn) hci_amp_disconn(conn, reason); break; default: - hci_acl_disconn(conn, reason); + hci_disconnect(conn, reason); break; } } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 60793e7b768..4cb46c24a74 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2398,7 +2398,7 @@ static void hci_link_tx_to(struct hci_dev *hdev, __u8 type) if (c->type == type && c->sent) { BT_ERR("%s killing stalled connection %pMR", hdev->name, &c->dst); - hci_acl_disconn(c, HCI_ERROR_REMOTE_USER_TERM); + hci_disconnect(c, HCI_ERROR_REMOTE_USER_TERM); } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 477726a6351..5892e54835a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2399,7 +2399,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); if (ev->status && conn->state == BT_CONNECTED) { - hci_acl_disconn(conn, HCI_ERROR_AUTH_FAILURE); + hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE); hci_conn_put(conn); goto unlock; } @@ -3472,7 +3472,7 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev, clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); if (ev->status && conn->state == BT_CONNECTED) { - hci_acl_disconn(conn, HCI_ERROR_AUTH_FAILURE); + hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE); hci_conn_put(conn); goto unlock; } -- cgit v1.2.3-70-g09d2 From be9f97f04565a6c438b7521ad679870d25645475 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 24 Feb 2013 19:36:52 +0100 Subject: Bluetooth: change bt_sock_unregister() to return void There is no reason a caller ever wants to check the return type of this call. _Iff_ a user successfully called bt_sock_register(), they're allowed to call bt_sock_unregister(). All other calls in the kernel (device_del, device_unregister, kfree(), ..) that are logically equivalent return void. Lets not make callers think they have to check the return type of this call and instead simply return void. We guarantee that after bt_sock_unregister() is called, the socket type _is_ unregistered. If that is not what the caller wants, they're using the wrong function, anyway. Signed-off-by: David Herrmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/bluetooth.h | 2 +- net/bluetooth/af_bluetooth.c | 15 +++------------ 2 files changed, 4 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 9531beee09b..5f51bef13e6 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -232,7 +232,7 @@ struct bt_sock_list { }; int bt_sock_register(int proto, const struct net_proto_family *ops); -int bt_sock_unregister(int proto); +void bt_sock_unregister(int proto); void bt_sock_link(struct bt_sock_list *l, struct sock *s); void bt_sock_unlink(struct bt_sock_list *l, struct sock *s); int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index d3ee69b35a7..81598e588f7 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -92,23 +92,14 @@ int bt_sock_register(int proto, const struct net_proto_family *ops) } EXPORT_SYMBOL(bt_sock_register); -int bt_sock_unregister(int proto) +void bt_sock_unregister(int proto) { - int err = 0; - if (proto < 0 || proto >= BT_MAX_PROTO) - return -EINVAL; + return; write_lock(&bt_proto_lock); - - if (!bt_proto[proto]) - err = -ENOENT; - else - bt_proto[proto] = NULL; - + bt_proto[proto] = NULL; write_unlock(&bt_proto_lock); - - return err; } EXPORT_SYMBOL(bt_sock_unregister); -- cgit v1.2.3-70-g09d2 From 8ff52f7d04d9cc31f1e81dcf9a2ba6335ed34905 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 28 Feb 2013 14:21:55 +0000 Subject: Bluetooth: Return RFCOMM session ptrs to avoid freed session Unfortunately, the design retains local copies of the s RFCOMM session pointer in various code blocks and this invites the erroneous access to a freed RFCOMM session structure. Therefore, return the RFCOMM session pointer back up the call stack to avoid accessing a freed RFCOMM session structure. When the RFCOMM session is deleted, NULL is passed up the call stack. If active DLCs exist when the rfcomm session is terminating, avoid a memory leak of rfcomm_dlc structures by ensuring that rfcomm_session_close() is used instead of rfcomm_session_del(). Signed-off-by: Dean Jenkins Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/rfcomm.h | 3 +- net/bluetooth/rfcomm/core.c | 106 ++++++++++++++++++++++------------------- 2 files changed, 58 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index e2e3ecad100..a4e38ead228 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -278,7 +278,8 @@ void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, static inline void rfcomm_session_hold(struct rfcomm_session *s) { - atomic_inc(&s->refcnt); + if (s) + atomic_inc(&s->refcnt); } /* ---- RFCOMM sockets ---- */ diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index d9e97cf1792..2b5c543638b 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -69,7 +69,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, u8 sec_level, int *err); static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst); -static void rfcomm_session_del(struct rfcomm_session *s); +static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s); /* ---- RFCOMM frame parsing macros ---- */ #define __get_dlci(b) ((b & 0xfc) >> 2) @@ -108,10 +108,12 @@ static void rfcomm_schedule(void) wake_up_process(rfcomm_thread); } -static void rfcomm_session_put(struct rfcomm_session *s) +static struct rfcomm_session *rfcomm_session_put(struct rfcomm_session *s) { - if (atomic_dec_and_test(&s->refcnt)) - rfcomm_session_del(s); + if (s && atomic_dec_and_test(&s->refcnt)) + s = rfcomm_session_del(s); + + return s; } /* ---- RFCOMM FCS computation ---- */ @@ -631,7 +633,7 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) return s; } -static void rfcomm_session_del(struct rfcomm_session *s) +static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s) { int state = s->state; @@ -648,6 +650,8 @@ static void rfcomm_session_del(struct rfcomm_session *s) if (state != BT_LISTEN) module_put(THIS_MODULE); + + return NULL; } static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) @@ -666,7 +670,8 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) return NULL; } -static void rfcomm_session_close(struct rfcomm_session *s, int err) +static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s, + int err) { struct rfcomm_dlc *d; struct list_head *p, *n; @@ -685,7 +690,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err) } rfcomm_session_clear_timer(s); - rfcomm_session_put(s); + return rfcomm_session_put(s); } static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, @@ -737,8 +742,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, if (*err == 0 || *err == -EINPROGRESS) return s; - rfcomm_session_del(s); - return NULL; + return rfcomm_session_del(s); failed: sock_release(sock); @@ -1127,7 +1131,7 @@ static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) } /* ---- RFCOMM frame reception ---- */ -static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) +static struct rfcomm_session *rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) { BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); @@ -1136,7 +1140,7 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); if (!d) { rfcomm_send_dm(s, dlci); - return 0; + return s; } switch (d->state) { @@ -1172,25 +1176,14 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) break; case BT_DISCONN: - /* rfcomm_session_put is called later so don't do - * anything here otherwise we will mess up the session - * reference counter: - * - * (a) when we are the initiator dlc_unlink will drive - * the reference counter to 0 (there is no initial put - * after session_add) - * - * (b) when we are not the initiator rfcomm_rx_process - * will explicitly call put to balance the initial hold - * done after session add. - */ + s = rfcomm_session_close(s, ECONNRESET); break; } } - return 0; + return s; } -static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) +static struct rfcomm_session *rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) { int err = 0; @@ -1215,12 +1208,13 @@ static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) err = ECONNRESET; s->state = BT_CLOSED; - rfcomm_session_close(s, err); + s = rfcomm_session_close(s, err); } - return 0; + return s; } -static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) +static struct rfcomm_session *rfcomm_recv_disc(struct rfcomm_session *s, + u8 dlci) { int err = 0; @@ -1250,10 +1244,9 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) err = ECONNRESET; s->state = BT_CLOSED; - rfcomm_session_close(s, err); + s = rfcomm_session_close(s, err); } - - return 0; + return s; } void rfcomm_dlc_accept(struct rfcomm_dlc *d) @@ -1674,11 +1667,18 @@ drop: return 0; } -static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) +static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s, + struct sk_buff *skb) { struct rfcomm_hdr *hdr = (void *) skb->data; u8 type, dlci, fcs; + if (!s) { + /* no session, so free socket data */ + kfree_skb(skb); + return s; + } + dlci = __get_dlci(hdr->addr); type = __get_type(hdr->ctrl); @@ -1689,7 +1689,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) if (__check_fcs(skb->data, type, fcs)) { BT_ERR("bad checksum in packet"); kfree_skb(skb); - return -EILSEQ; + return s; } if (__test_ea(hdr->len)) @@ -1705,22 +1705,23 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) case RFCOMM_DISC: if (__test_pf(hdr->ctrl)) - rfcomm_recv_disc(s, dlci); + s = rfcomm_recv_disc(s, dlci); break; case RFCOMM_UA: if (__test_pf(hdr->ctrl)) - rfcomm_recv_ua(s, dlci); + s = rfcomm_recv_ua(s, dlci); break; case RFCOMM_DM: - rfcomm_recv_dm(s, dlci); + s = rfcomm_recv_dm(s, dlci); break; case RFCOMM_UIH: - if (dlci) - return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb); - + if (dlci) { + rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb); + return s; + } rfcomm_recv_mcc(s, skb); break; @@ -1729,7 +1730,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) break; } kfree_skb(skb); - return 0; + return s; } /* ---- Connection and data processing ---- */ @@ -1866,7 +1867,7 @@ static void rfcomm_process_dlcs(struct rfcomm_session *s) } } -static void rfcomm_process_rx(struct rfcomm_session *s) +static struct rfcomm_session *rfcomm_process_rx(struct rfcomm_session *s) { struct socket *sock = s->sock; struct sock *sk = sock->sk; @@ -1878,17 +1879,20 @@ static void rfcomm_process_rx(struct rfcomm_session *s) while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) - rfcomm_recv_frame(s, skb); + s = rfcomm_recv_frame(s, skb); else kfree_skb(skb); } - if (sk->sk_state == BT_CLOSED) { + if (s && (sk->sk_state == BT_CLOSED)) { if (!s->initiator) - rfcomm_session_put(s); + s = rfcomm_session_put(s); - rfcomm_session_close(s, sk->sk_err); + if (s) + s = rfcomm_session_close(s, sk->sk_err); } + + return s; } static void rfcomm_accept_connection(struct rfcomm_session *s) @@ -1925,7 +1929,7 @@ static void rfcomm_accept_connection(struct rfcomm_session *s) sock_release(nsock); } -static void rfcomm_check_connection(struct rfcomm_session *s) +static struct rfcomm_session *rfcomm_check_connection(struct rfcomm_session *s) { struct sock *sk = s->sock->sk; @@ -1944,9 +1948,10 @@ static void rfcomm_check_connection(struct rfcomm_session *s) case BT_CLOSED: s->state = BT_CLOSED; - rfcomm_session_close(s, sk->sk_err); + s = rfcomm_session_close(s, sk->sk_err); break; } + return s; } static void rfcomm_process_sessions(void) @@ -1975,15 +1980,16 @@ static void rfcomm_process_sessions(void) switch (s->state) { case BT_BOUND: - rfcomm_check_connection(s); + s = rfcomm_check_connection(s); break; default: - rfcomm_process_rx(s); + s = rfcomm_process_rx(s); break; } - rfcomm_process_dlcs(s); + if (s) + rfcomm_process_dlcs(s); rfcomm_session_put(s); } -- cgit v1.2.3-70-g09d2 From 08c30aca9e698faddebd34f81e1196295f9dc063 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 28 Feb 2013 14:21:56 +0000 Subject: Bluetooth: Remove RFCOMM session refcnt Previous commits have improved the handling of the RFCOMM session timer and the RFCOMM session pointers such that freed RFCOMM session structures should no longer be erroneously accessed. The RFCOMM session refcnt now has no purpose and will be deleted by this commit. Note that the RFCOMM session is now deleted as soon as the RFCOMM control channel link is no longer required. This makes the lifetime of the RFCOMM session deterministic and absolute. Previously with the refcnt, there was uncertainty about when the session structure would be deleted because the relative refcnt prevented the session structure from being deleted at will. It was noted that the refcnt could malfunction under very heavy real-time processor loading in embedded SMP environments. This could cause premature RFCOMM session deletion or double session deletion that could result in kernel crashes. Removal of the refcnt prevents this issue. There are 4 connection / disconnection RFCOMM session scenarios: host initiated control link ---> host disconnected control link host initiated ctrl link ---> remote device disconnected ctrl link remote device initiated ctrl link ---> host disconnected ctrl link remote device initiated ctrl link ---> remote device disc'ed ctrl link The control channel connection procedures are independent of the disconnection procedures. Strangely, the RFCOMM session refcnt was applying special treatment so erroneously combining connection and disconnection events. This commit fixes this issue by removing some session code that used the "initiator" member of the session structure that was intended for use with the data channels. Signed-off-by: Dean Jenkins Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/rfcomm.h | 7 ------- net/bluetooth/rfcomm/core.c | 43 +++++------------------------------------- 2 files changed, 5 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index a4e38ead228..7afd4199d6b 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -158,7 +158,6 @@ struct rfcomm_session { struct timer_list timer; unsigned long state; unsigned long flags; - atomic_t refcnt; int initiator; /* Default DLC parameters */ @@ -276,12 +275,6 @@ static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst); -static inline void rfcomm_session_hold(struct rfcomm_session *s) -{ - if (s) - atomic_inc(&s->refcnt); -} - /* ---- RFCOMM sockets ---- */ struct sockaddr_rc { sa_family_t rc_family; diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 2b5c543638b..75b7bbd8acb 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -108,14 +108,6 @@ static void rfcomm_schedule(void) wake_up_process(rfcomm_thread); } -static struct rfcomm_session *rfcomm_session_put(struct rfcomm_session *s) -{ - if (s && atomic_dec_and_test(&s->refcnt)) - s = rfcomm_session_del(s); - - return s; -} - /* ---- RFCOMM FCS computation ---- */ /* reversed, 8-bit, poly=0x07 */ @@ -251,16 +243,14 @@ static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout) { BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout); - if (!mod_timer(&s->timer, jiffies + timeout)) - rfcomm_session_hold(s); + mod_timer(&s->timer, jiffies + timeout); } static void rfcomm_session_clear_timer(struct rfcomm_session *s) { BT_DBG("session %p state %ld", s, s->state); - if (del_timer_sync(&s->timer)) - rfcomm_session_put(s); + del_timer_sync(&s->timer); } /* ---- RFCOMM DLCs ---- */ @@ -338,8 +328,6 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) { BT_DBG("dlc %p session %p", d, s); - rfcomm_session_hold(s); - rfcomm_session_clear_timer(s); rfcomm_dlc_hold(d); list_add(&d->list, &s->dlcs); @@ -358,8 +346,6 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) if (list_empty(&s->dlcs)) rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT); - - rfcomm_session_put(s); } static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) @@ -678,8 +664,6 @@ static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s, BT_DBG("session %p state %ld err %d", s, s->state, err); - rfcomm_session_hold(s); - s->state = BT_CLOSED; /* Close all dlcs */ @@ -690,7 +674,7 @@ static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s, } rfcomm_session_clear_timer(s); - return rfcomm_session_put(s); + return rfcomm_session_del(s); } static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, @@ -1884,13 +1868,8 @@ static struct rfcomm_session *rfcomm_process_rx(struct rfcomm_session *s) kfree_skb(skb); } - if (s && (sk->sk_state == BT_CLOSED)) { - if (!s->initiator) - s = rfcomm_session_put(s); - - if (s) - s = rfcomm_session_close(s, sk->sk_err); - } + if (s && (sk->sk_state == BT_CLOSED)) + s = rfcomm_session_close(s, sk->sk_err); return s; } @@ -1917,8 +1896,6 @@ static void rfcomm_accept_connection(struct rfcomm_session *s) s = rfcomm_session_add(nsock, BT_OPEN); if (s) { - rfcomm_session_hold(s); - /* We should adjust MTU on incoming sessions. * L2CAP MTU minus UIH header and FCS. */ s->mtu = min(l2cap_pi(nsock->sk)->chan->omtu, @@ -1967,7 +1944,6 @@ static void rfcomm_process_sessions(void) if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) { s->state = BT_DISCONN; rfcomm_send_disc(s, 0); - rfcomm_session_put(s); continue; } @@ -1976,8 +1952,6 @@ static void rfcomm_process_sessions(void) continue; } - rfcomm_session_hold(s); - switch (s->state) { case BT_BOUND: s = rfcomm_check_connection(s); @@ -1990,8 +1964,6 @@ static void rfcomm_process_sessions(void) if (s) rfcomm_process_dlcs(s); - - rfcomm_session_put(s); } rfcomm_unlock(); @@ -2041,7 +2013,6 @@ static int rfcomm_add_listener(bdaddr_t *ba) if (!s) goto failed; - rfcomm_session_hold(s); return 0; failed: sock_release(sock); @@ -2099,8 +2070,6 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) if (!s) return; - rfcomm_session_hold(s); - list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); @@ -2132,8 +2101,6 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) set_bit(RFCOMM_AUTH_REJECT, &d->flags); } - rfcomm_session_put(s); - rfcomm_schedule(); } -- cgit v1.2.3-70-g09d2 From 3119ae9599e5cdc1b9838563905c500b582ab6a5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:44 +0200 Subject: Bluetooth: Add initial skeleton for asynchronous HCI requests This patch adds the initial definitions and functions for asynchronous HCI requests. Asynchronous requests are essentially a group of HCI commands together with an optional completion callback. The request is tracked through the already existing command queue by having the necessary context information as part of the control buffer of each skb. The only information needed in the skb control buffer is a flag for indicating that the skb is the start of a request as well as the optional complete callback that should be used when the request is complete (this will be found in the last skb of the request). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/bluetooth.h | 10 ++++++++++ include/net/bluetooth/hci_core.h | 8 ++++++++ net/bluetooth/hci_core.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 5f51bef13e6..ed6e9552252 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -260,12 +260,22 @@ struct l2cap_ctrl { __u8 retries; }; +struct hci_dev; + +typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status); + +struct hci_req_ctrl { + bool start; + hci_req_complete_t complete; +}; + struct bt_skb_cb { __u8 pkt_type; __u8 incoming; __u16 expect; __u8 force_active; struct l2cap_ctrl control; + struct hci_req_ctrl req; }; #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 787d3b9bbd5..7191217c6bd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1041,6 +1041,14 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); +struct hci_request { + struct hci_dev *hdev; + struct sk_buff_head cmd_q; +}; + +void hci_req_init(struct hci_request *req, struct hci_dev *hdev); +int hci_req_run(struct hci_request *req, hci_req_complete_t complete); + int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param); void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags); void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6ab38fecf1f..94b08aa9a08 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2439,6 +2439,36 @@ static int hci_send_frame(struct sk_buff *skb) return hdev->send(skb); } +void hci_req_init(struct hci_request *req, struct hci_dev *hdev) +{ + skb_queue_head_init(&req->cmd_q); + req->hdev = hdev; +} + +int hci_req_run(struct hci_request *req, hci_req_complete_t complete) +{ + struct hci_dev *hdev = req->hdev; + struct sk_buff *skb; + unsigned long flags; + + BT_DBG("length %u", skb_queue_len(&req->cmd_q)); + + /* Do not allow empty requests */ + if (skb_queue_empty(&req->cmd_q)) + return -EINVAL; + + skb = skb_peek_tail(&req->cmd_q); + bt_cb(skb)->req.complete = complete; + + spin_lock_irqsave(&hdev->cmd_q.lock, flags); + skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q); + spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); + + queue_work(hdev->workqueue, &hdev->cmd_work); + + return 0; +} + /* Send HCI command */ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) { -- cgit v1.2.3-70-g09d2 From 71c76a170e979d60e01bd093c9b79e3adeb710cc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:46 +0200 Subject: Bluetooth: Introduce new hci_req_add function This function is analogous to hci_send_cmd() but instead of directly queuing the command to hdev->cmd_q it adds it to the local queue of the asynchronous HCI request being build (inside struct hci_request). This is the main function used for building asynchronous requests and there should be one or more calls to it between calls to hci_req_init and hci_req_run. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7191217c6bd..67fe661259b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1048,6 +1048,7 @@ struct hci_request { void hci_req_init(struct hci_request *req, struct hci_dev *hdev); int hci_req_run(struct hci_request *req, hci_req_complete_t complete); +int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param); int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param); void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d2edcc4643c..6e6a9dd8a15 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2517,6 +2517,28 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) return 0; } +/* Queue a command to an asynchronous HCI request */ +int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) +{ + struct hci_dev *hdev = req->hdev; + struct sk_buff *skb; + + BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); + + skb = hci_prepare_cmd(hdev, opcode, plen, param); + if (!skb) { + BT_ERR("%s no memory for command", hdev->name); + return -ENOMEM; + } + + if (skb_queue_empty(&req->cmd_q)) + bt_cb(skb)->req.start = true; + + skb_queue_tail(&req->cmd_q, skb); + + return 0; +} + /* Get data from the previously sent command */ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode) { -- cgit v1.2.3-70-g09d2 From 9238f36a5a5097018b90baa42c473d2f916a46f5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:48 +0200 Subject: Bluetooth: Add request cmd_complete and cmd_status functions This patch introduces functions to process the HCI request state when receiving HCI Command Status or Command Complete events. Some HCI commands, like Inquiry do not result in a Command complete event so special handling is needed for them. Inquiry is a particularly important one since it is the only forseeable "non-cmd_complete" command that will make good use of the request functionality, and its completion is either indicated by an Inquiry Complete event of a successful Command Complete for HCI_Inquiry_Cancel. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 + net/bluetooth/hci_core.c | 85 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 7 ++++ 3 files changed, 94 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 67fe661259b..d732d6894ad 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1049,6 +1049,8 @@ struct hci_request { void hci_req_init(struct hci_request *req, struct hci_dev *hdev); int hci_req_run(struct hci_request *req, hci_req_complete_t complete); int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param); +void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status); +void hci_req_cmd_status(struct hci_dev *hdev, u16 opcode, u8 status); int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param); void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 4f8142bdf65..0ada2ec36e7 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3208,6 +3208,91 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb) kfree_skb(skb); } +static bool hci_req_is_complete(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = skb_peek(&hdev->cmd_q); + if (!skb) + return true; + + return bt_cb(skb)->req.start; +} + +void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) +{ + hci_req_complete_t req_complete = NULL; + struct sk_buff *skb; + unsigned long flags; + + BT_DBG("opcode 0x%04x status 0x%02x", opcode, status); + + /* Check that the completed command really matches the last one + * that was sent. + */ + if (!hci_sent_cmd_data(hdev, opcode)) + return; + + /* If the command succeeded and there's still more commands in + * this request the request is not yet complete. + */ + if (!status && !hci_req_is_complete(hdev)) + return; + + /* If this was the last command in a request the complete + * callback would be found in hdev->sent_cmd instead of the + * command queue (hdev->cmd_q). + */ + if (hdev->sent_cmd) { + req_complete = bt_cb(hdev->sent_cmd)->req.complete; + if (req_complete) + goto call_complete; + } + + /* Remove all pending commands belonging to this request */ + spin_lock_irqsave(&hdev->cmd_q.lock, flags); + while ((skb = __skb_dequeue(&hdev->cmd_q))) { + if (bt_cb(skb)->req.start) { + __skb_queue_head(&hdev->cmd_q, skb); + break; + } + + req_complete = bt_cb(skb)->req.complete; + kfree_skb(skb); + } + spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); + +call_complete: + if (req_complete) + req_complete(hdev, status); +} + +void hci_req_cmd_status(struct hci_dev *hdev, u16 opcode, u8 status) +{ + hci_req_complete_t req_complete = NULL; + + BT_DBG("opcode 0x%04x status 0x%02x", opcode, status); + + if (status) { + hci_req_cmd_complete(hdev, opcode, status); + return; + } + + /* No need to handle success status if there are more commands */ + if (!hci_req_is_complete(hdev)) + return; + + if (hdev->sent_cmd) + req_complete = bt_cb(hdev->sent_cmd)->req.complete; + + /* If the request doesn't have a complete callback or there + * are other commands/requests in the hdev queue we consider + * this request as completed. + */ + if (!req_complete || !skb_queue_empty(&hdev->cmd_q)) + hci_req_cmd_complete(hdev, opcode, status); +} + static void hci_rx_work(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 14e872aa0d2..8b878a3bdf6 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -53,6 +53,7 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_dev_unlock(hdev); + hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status); hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); hci_conn_check_pending(hdev); @@ -1692,6 +1693,7 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, status); + hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status); hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); @@ -2254,6 +2256,7 @@ static void hci_qos_setup_complete_evt(struct hci_dev *hdev, static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_cmd_complete *ev = (void *) skb->data; + u8 status = skb->data[sizeof(*ev)]; __u16 opcode; skb_pull(skb, sizeof(*ev)); @@ -2497,6 +2500,8 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (ev->opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); + hci_req_cmd_complete(hdev, ev->opcode, status); + if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) @@ -2590,6 +2595,8 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) if (ev->opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); + hci_req_cmd_status(hdev, ev->opcode, ev->status); + if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) -- cgit v1.2.3-70-g09d2 From 42c6b129cd8c2aa5012a78ec39672e7052cc677a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:49 +0200 Subject: Bluetooth: Use async requests internally in hci_req_sync This patch converts the hci_req_sync() procedure to internaly use the asynchronous HCI requests. The hci_req_sync mechanism relies on hci_req_complete() calls from hci_event.c into hci_core.c whenever a HCI command completes. This is very similar to what asynchronous requests do and makes the conversion fairly straight forward by converting hci_req_complete into a request complete callback. By this change hci_req_complete (renamed to hci_req_sync_complete) becomes private to hci_core.c and all calls to it can be removed from hci_event.c. The commands in each hci_req_sync procedure are collected into their own request by passing the hci_request pointer to the request callback (instead of the hci_dev pointer). The one slight exception is the HCI init request which has the special handling of HCI driver specific initialization commands. These commands are run in their own request prior to the "main" init request. One other extra change that this patch must contain is the handling of spontaneous HCI reset complete events that some controllers exhibit. These were previously handled in the hci_req_complete function but the right place for them now becomes the hci_req_cmd_complete function. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 - net/bluetooth/hci_core.c | 271 ++++++++++++++++++++++----------------- net/bluetooth/hci_event.c | 78 +---------- 3 files changed, 156 insertions(+), 195 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d732d6894ad..3f124f43fcb 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1164,8 +1164,6 @@ struct hci_sec_filter { #define hci_req_lock(d) mutex_lock(&d->req_lock) #define hci_req_unlock(d) mutex_unlock(&d->req_lock) -void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result); - void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0ada2ec36e7..6218eced153 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -57,36 +57,9 @@ static void hci_notify(struct hci_dev *hdev, int event) /* ---- HCI requests ---- */ -void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result) +static void hci_req_sync_complete(struct hci_dev *hdev, u8 result) { - BT_DBG("%s command 0x%4.4x result 0x%2.2x", hdev->name, cmd, result); - - /* If this is the init phase check if the completed command matches - * the last init command, and if not just return. - */ - if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd) { - struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data; - u16 opcode = __le16_to_cpu(sent->opcode); - struct sk_buff *skb; - - /* Some CSR based controllers generate a spontaneous - * reset complete event during init and any pending - * command will never be completed. In such a case we - * need to resend whatever was the last sent - * command. - */ - - if (cmd != HCI_OP_RESET || opcode == HCI_OP_RESET) - return; - - skb = skb_clone(hdev->sent_cmd, GFP_ATOMIC); - if (skb) { - skb_queue_head(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - } - - return; - } + BT_DBG("%s result 0x%2.2x", hdev->name, result); if (hdev->req_status == HCI_REQ_PEND) { hdev->req_result = result; @@ -108,26 +81,36 @@ static void hci_req_cancel(struct hci_dev *hdev, int err) /* Execute request and wait for completion. */ static int __hci_req_sync(struct hci_dev *hdev, - void (*req)(struct hci_dev *hdev, unsigned long opt), + void (*func)(struct hci_request *req, + unsigned long opt), unsigned long opt, __u32 timeout) { + struct hci_request req; DECLARE_WAITQUEUE(wait, current); int err = 0; BT_DBG("%s start", hdev->name); + hci_req_init(&req, hdev); + hdev->req_status = HCI_REQ_PEND; add_wait_queue(&hdev->req_wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); - req(hdev, opt); + func(&req, opt); - /* If the request didn't send any commands return immediately */ - if (skb_queue_empty(&hdev->cmd_q) && atomic_read(&hdev->cmd_cnt)) { + err = hci_req_run(&req, hci_req_sync_complete); + if (err < 0) { hdev->req_status = 0; remove_wait_queue(&hdev->req_wait_q, &wait); - return err; + /* req_run will fail if the request did not add any + * commands to the queue, something that can happen when + * a request with conditionals doesn't trigger any + * commands to be sent. This is normal behavior and + * should not trigger an error return. + */ + return 0; } schedule_timeout(timeout); @@ -159,7 +142,8 @@ static int __hci_req_sync(struct hci_dev *hdev, } static int hci_req_sync(struct hci_dev *hdev, - void (*req)(struct hci_dev *hdev, unsigned long opt), + void (*req)(struct hci_request *req, + unsigned long opt), unsigned long opt, __u32 timeout) { int ret; @@ -175,72 +159,80 @@ static int hci_req_sync(struct hci_dev *hdev, return ret; } -static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) +static void hci_reset_req(struct hci_request *req, unsigned long opt) { - BT_DBG("%s %ld", hdev->name, opt); + BT_DBG("%s %ld", req->hdev->name, opt); /* Reset device */ - set_bit(HCI_RESET, &hdev->flags); - hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); + set_bit(HCI_RESET, &req->hdev->flags); + hci_req_add(req, HCI_OP_RESET, 0, NULL); } -static void bredr_init(struct hci_dev *hdev) +static void bredr_init(struct hci_request *req) { - hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED; + req->hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED; /* Read Local Supported Features */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); /* Read Local Version */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL); /* Read BD Address */ - hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); + hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL); } -static void amp_init(struct hci_dev *hdev) +static void amp_init(struct hci_request *req) { - hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED; + req->hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED; /* Read Local Version */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL); /* Read Local AMP Info */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); /* Read Data Blk size */ - hci_send_cmd(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL); + hci_req_add(req, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL); } -static void hci_init1_req(struct hci_dev *hdev, unsigned long opt) +static void hci_init1_req(struct hci_request *req, unsigned long opt) { + struct hci_dev *hdev = req->hdev; + struct hci_request init_req; struct sk_buff *skb; BT_DBG("%s %ld", hdev->name, opt); /* Driver initialization */ + hci_req_init(&init_req, hdev); + /* Special commands */ while ((skb = skb_dequeue(&hdev->driver_init))) { bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; skb->dev = (void *) hdev; - skb_queue_tail(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); + if (skb_queue_empty(&init_req.cmd_q)) + bt_cb(skb)->req.start = true; + + skb_queue_tail(&init_req.cmd_q, skb); } skb_queue_purge(&hdev->driver_init); + hci_req_run(&init_req, NULL); + /* Reset */ if (!test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) - hci_reset_req(hdev, 0); + hci_reset_req(req, 0); switch (hdev->dev_type) { case HCI_BREDR: - bredr_init(hdev); + bredr_init(req); break; case HCI_AMP: - amp_init(hdev); + amp_init(req); break; default: @@ -249,53 +241,53 @@ static void hci_init1_req(struct hci_dev *hdev, unsigned long opt) } } -static void bredr_setup(struct hci_dev *hdev) +static void bredr_setup(struct hci_request *req) { struct hci_cp_delete_stored_link_key cp; __le16 param; __u8 flt_type; /* Read Buffer Size (ACL mtu, max pkt, etc.) */ - hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); + hci_req_add(req, HCI_OP_READ_BUFFER_SIZE, 0, NULL); /* Read Class of Device */ - hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); + hci_req_add(req, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); /* Read Local Name */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_NAME, 0, NULL); /* Read Voice Setting */ - hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); + hci_req_add(req, HCI_OP_READ_VOICE_SETTING, 0, NULL); /* Clear Event Filters */ flt_type = HCI_FLT_CLEAR_ALL; - hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); + hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &flt_type); /* Connection accept timeout ~20 secs */ param = __constant_cpu_to_le16(0x7d00); - hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); + hci_req_add(req, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); bacpy(&cp.bdaddr, BDADDR_ANY); cp.delete_all = 0x01; - hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); } -static void le_setup(struct hci_dev *hdev) +static void le_setup(struct hci_request *req) { /* Read LE Buffer Size */ - hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); + hci_req_add(req, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); /* Read LE Local Supported Features */ - hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL); + hci_req_add(req, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL); /* Read LE Advertising Channel TX Power */ - hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); + hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); /* Read LE White List Size */ - hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); + hci_req_add(req, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); /* Read LE Supported States */ - hci_send_cmd(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL); + hci_req_add(req, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL); } static u8 hci_get_inquiry_mode(struct hci_dev *hdev) @@ -326,17 +318,19 @@ static u8 hci_get_inquiry_mode(struct hci_dev *hdev) return 0x00; } -static void hci_setup_inquiry_mode(struct hci_dev *hdev) +static void hci_setup_inquiry_mode(struct hci_request *req) { u8 mode; - mode = hci_get_inquiry_mode(hdev); + mode = hci_get_inquiry_mode(req->hdev); - hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); + hci_req_add(req, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); } -static void hci_setup_event_mask(struct hci_dev *hdev) +static void hci_setup_event_mask(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; + /* The second byte is 0xff instead of 0x9f (two reserved bits * disabled) since a Broadcom 1.2 dongle doesn't respond to the * command otherwise. @@ -392,67 +386,70 @@ static void hci_setup_event_mask(struct hci_dev *hdev) if (lmp_le_capable(hdev)) events[7] |= 0x20; /* LE Meta-Event */ - hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); + hci_req_add(req, HCI_OP_SET_EVENT_MASK, sizeof(events), events); if (lmp_le_capable(hdev)) { memset(events, 0, sizeof(events)); events[0] = 0x1f; - hci_send_cmd(hdev, HCI_OP_LE_SET_EVENT_MASK, - sizeof(events), events); + hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, + sizeof(events), events); } } -static void hci_init2_req(struct hci_dev *hdev, unsigned long opt) +static void hci_init2_req(struct hci_request *req, unsigned long opt) { + struct hci_dev *hdev = req->hdev; + if (lmp_bredr_capable(hdev)) - bredr_setup(hdev); + bredr_setup(req); if (lmp_le_capable(hdev)) - le_setup(hdev); + le_setup(req); - hci_setup_event_mask(hdev); + hci_setup_event_mask(req); if (hdev->hci_ver > BLUETOOTH_VER_1_1) - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); if (lmp_ssp_capable(hdev)) { if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { u8 mode = 0x01; - hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, - sizeof(mode), &mode); + hci_req_add(req, HCI_OP_WRITE_SSP_MODE, + sizeof(mode), &mode); } else { struct hci_cp_write_eir cp; memset(hdev->eir, 0, sizeof(hdev->eir)); memset(&cp, 0, sizeof(cp)); - hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp); } } if (lmp_inq_rssi_capable(hdev)) - hci_setup_inquiry_mode(hdev); + hci_setup_inquiry_mode(req); if (lmp_inq_tx_pwr_capable(hdev)) - hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); + hci_req_add(req, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); if (lmp_ext_feat_capable(hdev)) { struct hci_cp_read_local_ext_features cp; cp.page = 0x01; - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), - &cp); + hci_req_add(req, HCI_OP_READ_LOCAL_EXT_FEATURES, + sizeof(cp), &cp); } if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { u8 enable = 1; - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), - &enable); + hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), + &enable); } } -static void hci_setup_link_policy(struct hci_dev *hdev) +static void hci_setup_link_policy(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; struct hci_cp_write_def_link_policy cp; u16 link_policy = 0; @@ -466,11 +463,12 @@ static void hci_setup_link_policy(struct hci_dev *hdev) link_policy |= HCI_LP_PARK; cp.policy = cpu_to_le16(link_policy); - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(cp), &cp); } -static void hci_set_le_support(struct hci_dev *hdev) +static void hci_set_le_support(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; struct hci_cp_write_le_host_supported cp; memset(&cp, 0, sizeof(cp)); @@ -481,17 +479,19 @@ static void hci_set_le_support(struct hci_dev *hdev) } if (cp.le != lmp_host_le_capable(hdev)) - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), - &cp); + hci_req_add(req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), + &cp); } -static void hci_init3_req(struct hci_dev *hdev, unsigned long opt) +static void hci_init3_req(struct hci_request *req, unsigned long opt) { + struct hci_dev *hdev = req->hdev; + if (hdev->commands[5] & 0x10) - hci_setup_link_policy(hdev); + hci_setup_link_policy(req); if (lmp_le_capable(hdev)) - hci_set_le_support(hdev); + hci_set_le_support(req); } static int __hci_init(struct hci_dev *hdev) @@ -516,44 +516,44 @@ static int __hci_init(struct hci_dev *hdev) return __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT); } -static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) +static void hci_scan_req(struct hci_request *req, unsigned long opt) { __u8 scan = opt; - BT_DBG("%s %x", hdev->name, scan); + BT_DBG("%s %x", req->hdev->name, scan); /* Inquiry and Page scans */ - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } -static void hci_auth_req(struct hci_dev *hdev, unsigned long opt) +static void hci_auth_req(struct hci_request *req, unsigned long opt) { __u8 auth = opt; - BT_DBG("%s %x", hdev->name, auth); + BT_DBG("%s %x", req->hdev->name, auth); /* Authentication */ - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth); + hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth); } -static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt) +static void hci_encrypt_req(struct hci_request *req, unsigned long opt) { __u8 encrypt = opt; - BT_DBG("%s %x", hdev->name, encrypt); + BT_DBG("%s %x", req->hdev->name, encrypt); /* Encryption */ - hci_send_cmd(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt); + hci_req_add(req, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt); } -static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt) +static void hci_linkpol_req(struct hci_request *req, unsigned long opt) { __le16 policy = cpu_to_le16(opt); - BT_DBG("%s %x", hdev->name, policy); + BT_DBG("%s %x", req->hdev->name, policy); /* Default link policy */ - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy); + hci_req_add(req, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy); } /* Get HCI device by index. @@ -790,9 +790,10 @@ static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) return copied; } -static void hci_inq_req(struct hci_dev *hdev, unsigned long opt) +static void hci_inq_req(struct hci_request *req, unsigned long opt) { struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt; + struct hci_dev *hdev = req->hdev; struct hci_cp_inquiry cp; BT_DBG("%s", hdev->name); @@ -804,7 +805,7 @@ static void hci_inq_req(struct hci_dev *hdev, unsigned long opt) memcpy(&cp.lap, &ir->lap, 3); cp.length = ir->length; cp.num_rsp = ir->num_rsp; - hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp); } int hci_inquiry(void __user *arg) @@ -1845,7 +1846,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) return mgmt_device_unblocked(hdev, bdaddr, type); } -static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt) +static void le_scan_param_req(struct hci_request *req, unsigned long opt) { struct le_scan_params *param = (struct le_scan_params *) opt; struct hci_cp_le_set_scan_param cp; @@ -1855,10 +1856,10 @@ static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt) cp.interval = cpu_to_le16(param->interval); cp.window = cpu_to_le16(param->window); - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp); } -static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt) +static void le_scan_enable_req(struct hci_request *req, unsigned long opt) { struct hci_cp_le_set_scan_enable cp; @@ -1866,7 +1867,7 @@ static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt) cp.enable = 1; cp.filter_dup = 1; - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); } static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, @@ -3219,6 +3220,28 @@ static bool hci_req_is_complete(struct hci_dev *hdev) return bt_cb(skb)->req.start; } +static void hci_resend_last(struct hci_dev *hdev) +{ + struct hci_command_hdr *sent; + struct sk_buff *skb; + u16 opcode; + + if (!hdev->sent_cmd) + return; + + sent = (void *) hdev->sent_cmd->data; + opcode = __le16_to_cpu(sent->opcode); + if (opcode == HCI_OP_RESET) + return; + + skb = skb_clone(hdev->sent_cmd, GFP_KERNEL); + if (!skb) + return; + + skb_queue_head(&hdev->cmd_q, skb); + queue_work(hdev->workqueue, &hdev->cmd_work); +} + void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) { hci_req_complete_t req_complete = NULL; @@ -3227,11 +3250,21 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) BT_DBG("opcode 0x%04x status 0x%02x", opcode, status); - /* Check that the completed command really matches the last one - * that was sent. + /* If the completed command doesn't match the last one that was + * sent we need to do special handling of it. */ - if (!hci_sent_cmd_data(hdev, opcode)) + if (!hci_sent_cmd_data(hdev, opcode)) { + /* Some CSR based controllers generate a spontaneous + * reset complete event during init and any pending + * command will never be completed. In such a case we + * need to resend whatever was the last sent + * command. + */ + if (test_bit(HCI_INIT, &hdev->flags) && opcode == HCI_OP_RESET) + hci_resend_last(hdev); + return; + } /* If the command succeeded and there's still more commands in * this request the request is not yet complete. diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8b878a3bdf6..0dd85a0c05f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -54,7 +54,6 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status); - hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); hci_conn_check_pending(hdev); } @@ -184,8 +183,6 @@ static void hci_cc_write_def_link_policy(struct hci_dev *hdev, if (!status) hdev->link_policy = get_unaligned_le16(sent); - - hci_req_complete(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, status); } static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) @@ -196,8 +193,6 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_RESET, &hdev->flags); - hci_req_complete(hdev, HCI_OP_RESET, status); - /* Reset all non-persistent flags */ hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PENDING_CLASS) | BIT(HCI_PERIODIC_INQ)); @@ -232,8 +227,6 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) if (!status && !test_bit(HCI_INIT, &hdev->flags)) hci_update_ad(hdev); - - hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status); } static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -271,8 +264,6 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_auth_enable_complete(hdev, status); - - hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status); } static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) @@ -294,8 +285,6 @@ static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) else clear_bit(HCI_ENCRYPT, &hdev->flags); } - - hci_req_complete(hdev, HCI_OP_WRITE_ENCRYPT_MODE, status); } static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) @@ -344,7 +333,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) done: hci_dev_unlock(hdev); - hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); } static void hci_cc_read_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) @@ -441,8 +429,6 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status); } static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) @@ -480,7 +466,7 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) - goto done; + return; hdev->hci_ver = rp->hci_ver; hdev->hci_rev = __le16_to_cpu(rp->hci_rev); @@ -490,9 +476,6 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s manufacturer 0x%4.4x hci ver %d:%d", hdev->name, hdev->manufacturer, hdev->hci_ver, hdev->hci_rev); - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_VERSION, rp->status); } static void hci_cc_read_local_commands(struct hci_dev *hdev, @@ -504,8 +487,6 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev, if (!rp->status) memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); - - hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status); } static void hci_cc_read_local_features(struct hci_dev *hdev, @@ -572,7 +553,7 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) - goto done; + return; switch (rp->page) { case 0: @@ -582,9 +563,6 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev, memcpy(hdev->host_features, rp->features, 8); break; } - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status); } static void hci_cc_read_flow_control_mode(struct hci_dev *hdev, @@ -594,12 +572,8 @@ static void hci_cc_read_flow_control_mode(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (rp->status) - return; - - hdev->flow_ctl_mode = rp->mode; - - hci_req_complete(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, rp->status); + if (!rp->status) + hdev->flow_ctl_mode = rp->mode; } static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) @@ -636,8 +610,6 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) if (!rp->status) bacpy(&hdev->bdaddr, &rp->bdaddr); - - hci_req_complete(hdev, HCI_OP_READ_BD_ADDR, rp->status); } static void hci_cc_read_data_block_size(struct hci_dev *hdev, @@ -658,8 +630,6 @@ static void hci_cc_read_data_block_size(struct hci_dev *hdev, BT_DBG("%s blk mtu %d cnt %d len %d", hdev->name, hdev->block_mtu, hdev->block_cnt, hdev->block_len); - - hci_req_complete(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, rp->status); } static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) @@ -667,8 +637,6 @@ static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status); } static void hci_cc_read_local_amp_info(struct hci_dev *hdev, @@ -692,8 +660,6 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev, hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to); hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to); - hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status); - a2mp_rsp: a2mp_send_getinfo_rsp(hdev); } @@ -741,8 +707,6 @@ static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status); } static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) @@ -750,8 +714,6 @@ static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_SET_EVENT_MASK, status); } static void hci_cc_write_inquiry_mode(struct hci_dev *hdev, @@ -760,8 +722,6 @@ static void hci_cc_write_inquiry_mode(struct hci_dev *hdev, __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_WRITE_INQUIRY_MODE, status); } static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, @@ -773,8 +733,6 @@ static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, if (!rp->status) hdev->inq_tx_power = rp->tx_power; - - hci_req_complete(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, rp->status); } static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) @@ -782,8 +740,6 @@ static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status); } static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -845,8 +801,6 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, hdev->le_cnt = hdev->le_pkts; BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts); - - hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); } static void hci_cc_le_read_local_features(struct hci_dev *hdev, @@ -858,8 +812,6 @@ static void hci_cc_le_read_local_features(struct hci_dev *hdev, if (!rp->status) memcpy(hdev->le_features, rp->features, 8); - - hci_req_complete(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, rp->status); } static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, @@ -874,8 +826,6 @@ static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, if (!test_bit(HCI_INIT, &hdev->flags)) hci_update_ad(hdev); } - - hci_req_complete(hdev, HCI_OP_LE_READ_ADV_TX_POWER, rp->status); } static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) @@ -883,8 +833,6 @@ static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_LE_SET_EVENT_MASK, status); } static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -985,8 +933,6 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) if (!test_bit(HCI_INIT, &hdev->flags)) hci_update_ad(hdev); - - hci_req_complete(hdev, HCI_OP_LE_SET_ADV_ENABLE, status); } static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) @@ -995,8 +941,6 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, status); - hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_PARAM, status); - if (status) { hci_dev_lock(hdev); mgmt_start_discovery_failed(hdev, status); @@ -1019,8 +963,6 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, switch (cp->enable) { case LE_SCANNING_ENABLED: - hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status); - if (status) { hci_dev_lock(hdev); mgmt_start_discovery_failed(hdev, status); @@ -1071,8 +1013,6 @@ static void hci_cc_le_read_white_list_size(struct hci_dev *hdev, if (!rp->status) hdev->le_white_list_size = rp->size; - - hci_req_complete(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, rp->status); } static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -1083,8 +1023,6 @@ static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; - - hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status); } static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -1095,8 +1033,6 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; - - hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); } static void hci_cc_le_read_supported_states(struct hci_dev *hdev, @@ -1108,8 +1044,6 @@ static void hci_cc_le_read_supported_states(struct hci_dev *hdev, if (!rp->status) memcpy(hdev->le_states, rp->le_states, 8); - - hci_req_complete(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, rp->status); } static void hci_cc_write_le_host_supported(struct hci_dev *hdev, @@ -1139,8 +1073,6 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, if (test_bit(HCI_MGMT, &hdev->dev_flags) && !test_bit(HCI_INIT, &hdev->flags)) mgmt_le_enable_complete(hdev, sent->le, status); - - hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status); } static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev, @@ -1162,7 +1094,6 @@ static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) BT_DBG("%s status 0x%2.2x", hdev->name, status); if (status) { - hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags)) @@ -1694,7 +1625,6 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, status); hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status); - hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); -- cgit v1.2.3-70-g09d2 From cecbb967b2f5c52e090978ff6afe7deddbfbeda5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:50 +0200 Subject: Bluetooth: Remove unused hdev->init_last_cmd This variable is no longer needed (due to async HCI request support and the conversion of hci_req_sync to use it), so it can be safely removed. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 -- net/bluetooth/hci_core.c | 6 ------ 2 files changed, 8 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3f124f43fcb..3a9cbf2b55c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -248,8 +248,6 @@ struct hci_dev { __u32 req_status; __u32 req_result; - __u16 init_last_cmd; - struct list_head mgmt_pending; struct discovery_state discovery; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6218eced153..3fc699db8fb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1015,10 +1015,7 @@ int hci_dev_open(__u16 dev) if (!test_bit(HCI_RAW, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); - hdev->init_last_cmd = 0; - ret = __hci_init(hdev); - clear_bit(HCI_INIT, &hdev->flags); } @@ -2509,9 +2506,6 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) return -ENOMEM; } - if (test_bit(HCI_INIT, &hdev->flags)) - hdev->init_last_cmd = opcode; - /* Stand-alone HCI commands must be flaged as * single-command requests. */ -- cgit v1.2.3-70-g09d2 From c031e234ee304b507b79f76a7677ea0a7a8890e8 Mon Sep 17 00:00:00 2001 From: Andy King Date: Thu, 7 Mar 2013 05:26:13 +0000 Subject: VSOCK: Split vm_sockets.h into kernel/uapi Split the vSockets header into kernel and UAPI parts. The former gets the bits that used to be in __KERNEL__ guards, while the latter gets everything that is user-visible. Tested by compiling vsock (+transport) and a simple user-mode vSockets application. Reported-by: David Howells Acked-by: Dmitry Torokhov Signed-off-by: Andy King Acked-by: David Howells Signed-off-by: David S. Miller --- include/linux/vm_sockets.h | 23 +++++++++++++++++++++++ include/uapi/linux/vm_sockets.h | 23 ++++++++--------------- 2 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 include/linux/vm_sockets.h (limited to 'include') diff --git a/include/linux/vm_sockets.h b/include/linux/vm_sockets.h new file mode 100644 index 00000000000..0805eecba8f --- /dev/null +++ b/include/linux/vm_sockets.h @@ -0,0 +1,23 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2007-2013 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation version 2 and no later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _VM_SOCKETS_H +#define _VM_SOCKETS_H + +#include + +int vm_sockets_get_local_cid(void); + +#endif /* _VM_SOCKETS_H */ diff --git a/include/uapi/linux/vm_sockets.h b/include/uapi/linux/vm_sockets.h index df91301847e..b4ed5d89569 100644 --- a/include/uapi/linux/vm_sockets.h +++ b/include/uapi/linux/vm_sockets.h @@ -13,12 +13,10 @@ * more details. */ -#ifndef _VM_SOCKETS_H_ -#define _VM_SOCKETS_H_ +#ifndef _UAPI_VM_SOCKETS_H +#define _UAPI_VM_SOCKETS_H -#if !defined(__KERNEL__) -#include -#endif +#include /* Option name for STREAM socket buffer size. Use as the option name in * setsockopt(3) or getsockopt(3) to set or get an unsigned long long that @@ -137,14 +135,13 @@ #define VM_SOCKETS_VERSION_MINOR(_v) (((_v) & 0x0000FFFF)) /* Address structure for vSockets. The address family should be set to - * whatever vmci_sock_get_af_value_fd() returns. The structure members should - * all align on their natural boundaries without resorting to compiler packing - * directives. The total size of this structure should be exactly the same as - * that of struct sockaddr. + * AF_VSOCK. The structure members should all align on their natural + * boundaries without resorting to compiler packing directives. The total size + * of this structure should be exactly the same as that of struct sockaddr. */ struct sockaddr_vm { - sa_family_t svm_family; + __kernel_sa_family_t svm_family; unsigned short svm_reserved1; unsigned int svm_port; unsigned int svm_cid; @@ -156,8 +153,4 @@ struct sockaddr_vm { #define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9) -#if defined(__KERNEL__) -int vm_sockets_get_local_cid(void); -#endif - -#endif +#endif /* _UAPI_VM_SOCKETS_H */ -- cgit v1.2.3-70-g09d2 From b7ef213ef65256168df83ddfbb8131ed9adc10f9 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Fri, 8 Mar 2013 02:07:16 +0000 Subject: ipv6: introdcue __ipv6_addr_needs_scope_id and ipv6_iface_scope_id helper functions __ipv6_addr_needs_scope_id checks if an ipv6 address needs to supply a 'sin6_scope_id != 0'. 'sin6_scope_id != 0' was enforced in case of link-local addresses. To support interface-local multicast these checks had to be enhanced and are now consolidated into these new helper functions. v2: a) migrated to struct ipv6_addr_props v3: a) reverted changes for ipv6_addr_props b) test for address type instead of comparing scope v4: a) unchanged Suggested-by: YOSHIFUJI Hideaki Cc: YOSHIFUJI Hideaki Acked-by: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Acked-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/net/ipv6.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 988c9f28f0f..42ef6abf25c 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -320,6 +320,18 @@ static inline int ipv6_addr_src_scope(const struct in6_addr *addr) return __ipv6_addr_src_scope(__ipv6_addr_type(addr)); } +static inline bool __ipv6_addr_needs_scope_id(int type) +{ + return type & IPV6_ADDR_LINKLOCAL || + (type & IPV6_ADDR_MULTICAST && + (type & (IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL))); +} + +static inline __u32 ipv6_iface_scope_id(const struct in6_addr *addr, int iface) +{ + return __ipv6_addr_needs_scope_id(__ipv6_addr_type(addr)) ? iface : 0; +} + static inline int ipv6_addr_cmp(const struct in6_addr *a1, const struct in6_addr *a2) { return memcmp(a1, a2, sizeof(struct in6_addr)); -- cgit v1.2.3-70-g09d2 From 5d73e0342fd9bf500583868906325d42c4d2bf6f Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 8 Mar 2013 11:20:16 -0300 Subject: Bluetooth: HCI request error handling When we are building a HCI request with more than one HCI command and one of the hci_req_add calls fail, we should have some cleanup routine so the HCI commands already queued on HCI request can be deleted. Otherwise, we will face some memory leaks issues. This patch implements the HCI request error handling which is the following: If a hci_req_add fails, we save the error code in hci_ request. Once hci_req_run is called, we verify the error field. If it is different from zero, we delete all HCI commands already queued and return the error code. Signed-off-by: Andre Guedes Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 5 +++++ net/bluetooth/hci_core.c | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3a9cbf2b55c..332ee5099a5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1042,6 +1042,11 @@ int hci_unregister_cb(struct hci_cb *hcb); struct hci_request { struct hci_dev *hdev; struct sk_buff_head cmd_q; + + /* If something goes wrong when building the HCI request, the error + * value is stored in this field. + */ + int err; }; void hci_req_init(struct hci_request *req, struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 4603464b91e..b432baafdf1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2443,6 +2443,7 @@ void hci_req_init(struct hci_request *req, struct hci_dev *hdev) { skb_queue_head_init(&req->cmd_q); req->hdev = hdev; + req->err = 0; } int hci_req_run(struct hci_request *req, hci_req_complete_t complete) @@ -2453,6 +2454,14 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) BT_DBG("length %u", skb_queue_len(&req->cmd_q)); + /* If an error occured during request building, remove all HCI + * commands queued on the HCI request queue. + */ + if (req->err) { + skb_queue_purge(&req->cmd_q); + return req->err; + } + /* Do not allow empty requests */ if (skb_queue_empty(&req->cmd_q)) return -ENODATA; @@ -2529,7 +2538,9 @@ int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) skb = hci_prepare_cmd(hdev, opcode, plen, param); if (!skb) { - BT_ERR("%s no memory for command", hdev->name); + BT_ERR("%s no memory for command (opcode 0x%4.4x)", + hdev->name, opcode); + req->err = -ENOMEM; return -ENOMEM; } -- cgit v1.2.3-70-g09d2 From e348fe6bbab85c513816d2536ffabac4be016442 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 8 Mar 2013 11:20:17 -0300 Subject: Bluetooth: Make hci_req_add returning void Since no one checks the returning value of hci_req_add and HCI request errors are now handled in hci_req_run, we can make hci_ req_add returning void. Signed-off-by: Andre Guedes Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 332ee5099a5..d6c32561fc0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1051,7 +1051,7 @@ struct hci_request { void hci_req_init(struct hci_request *req, struct hci_dev *hdev); int hci_req_run(struct hci_request *req, hci_req_complete_t complete); -int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param); +void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param); void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status); void hci_req_cmd_status(struct hci_dev *hdev, u16 opcode, u8 status); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b432baafdf1..1c678757c83 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2529,7 +2529,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) } /* Queue a command to an asynchronous HCI request */ -int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) +void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) { struct hci_dev *hdev = req->hdev; struct sk_buff *skb; @@ -2541,15 +2541,13 @@ int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) BT_ERR("%s no memory for command (opcode 0x%4.4x)", hdev->name, opcode); req->err = -ENOMEM; - return -ENOMEM; + return; } if (skb_queue_empty(&req->cmd_q)) bt_cb(skb)->req.start = true; skb_queue_tail(&req->cmd_q, skb); - - return 0; } /* Get data from the previously sent command */ -- cgit v1.2.3-70-g09d2 From ec5f061564238892005257c83565a0b58ec79295 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 7 Mar 2013 09:28:01 +0000 Subject: net: Kill link between CSUM and SG features. Earlier SG was unset if CSUM was not available for given device to force skb copy to avoid sending inconsistent csum. Commit c9af6db4c11c (net: Fix possible wrong checksum generation) added explicit flag to force copy to fix this issue. Therefore there is no need to link SG and CSUM, following patch kills this link between there two features. This patch is also required following patch in series. Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- include/linux/netdevice.h | 13 ++++++++++ net/core/dev.c | 63 +++++++++++++++++++++++++---------------------- net/core/skbuff.c | 13 ++++++++++ net/ipv4/af_inet.c | 3 --- net/ipv6/ip6_offload.c | 3 --- 5 files changed, 59 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 896eb4985f9..e1ebeffa6b3 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2683,6 +2683,19 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features) { return __skb_gso_segment(skb, features, true); } +__be16 skb_network_protocol(struct sk_buff *skb); + +static inline bool can_checksum_protocol(netdev_features_t features, + __be16 protocol) +{ + return ((features & NETIF_F_GEN_CSUM) || + ((features & NETIF_F_V4_CSUM) && + protocol == htons(ETH_P_IP)) || + ((features & NETIF_F_V6_CSUM) && + protocol == htons(ETH_P_IPV6)) || + ((features & NETIF_F_FCOE_CRC) && + protocol == htons(ETH_P_FCOE))); +} #ifdef CONFIG_BUG extern void netdev_rx_csum_fault(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index 96103894ad6..bb999931729 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2208,16 +2208,8 @@ out: } EXPORT_SYMBOL(skb_checksum_help); -/** - * skb_mac_gso_segment - mac layer segmentation handler. - * @skb: buffer to segment - * @features: features for the output path (see dev->features) - */ -struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb, - netdev_features_t features) +__be16 skb_network_protocol(struct sk_buff *skb) { - struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); - struct packet_offload *ptype; __be16 type = skb->protocol; while (type == htons(ETH_P_8021Q)) { @@ -2225,13 +2217,31 @@ struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb, struct vlan_hdr *vh; if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN))) - return ERR_PTR(-EINVAL); + return 0; vh = (struct vlan_hdr *)(skb->data + vlan_depth); type = vh->h_vlan_encapsulated_proto; vlan_depth += VLAN_HLEN; } + return type; +} + +/** + * skb_mac_gso_segment - mac layer segmentation handler. + * @skb: buffer to segment + * @features: features for the output path (see dev->features) + */ +struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); + struct packet_offload *ptype; + __be16 type = skb_network_protocol(skb); + + if (unlikely(!type)) + return ERR_PTR(-EINVAL); + __skb_pull(skb, skb->mac_len); rcu_read_lock(); @@ -2398,24 +2408,12 @@ static int dev_gso_segment(struct sk_buff *skb, netdev_features_t features) return 0; } -static bool can_checksum_protocol(netdev_features_t features, __be16 protocol) -{ - return ((features & NETIF_F_GEN_CSUM) || - ((features & NETIF_F_V4_CSUM) && - protocol == htons(ETH_P_IP)) || - ((features & NETIF_F_V6_CSUM) && - protocol == htons(ETH_P_IPV6)) || - ((features & NETIF_F_FCOE_CRC) && - protocol == htons(ETH_P_FCOE))); -} - static netdev_features_t harmonize_features(struct sk_buff *skb, __be16 protocol, netdev_features_t features) { if (skb->ip_summed != CHECKSUM_NONE && !can_checksum_protocol(features, protocol)) { features &= ~NETIF_F_ALL_CSUM; - features &= ~NETIF_F_SG; } else if (illegal_highdma(skb->dev, skb)) { features &= ~NETIF_F_SG; } @@ -4921,20 +4919,25 @@ static netdev_features_t netdev_fix_features(struct net_device *dev, features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM); } - /* Fix illegal SG+CSUM combinations. */ - if ((features & NETIF_F_SG) && - !(features & NETIF_F_ALL_CSUM)) { - netdev_dbg(dev, - "Dropping NETIF_F_SG since no checksum feature.\n"); - features &= ~NETIF_F_SG; - } - /* TSO requires that SG is present as well. */ if ((features & NETIF_F_ALL_TSO) && !(features & NETIF_F_SG)) { netdev_dbg(dev, "Dropping TSO features since no SG feature.\n"); features &= ~NETIF_F_ALL_TSO; } + if ((features & NETIF_F_TSO) && !(features & NETIF_F_HW_CSUM) && + !(features & NETIF_F_IP_CSUM)) { + netdev_dbg(dev, "Dropping TSO features since no CSUM feature.\n"); + features &= ~NETIF_F_TSO; + features &= ~NETIF_F_TSO_ECN; + } + + if ((features & NETIF_F_TSO6) && !(features & NETIF_F_HW_CSUM) && + !(features & NETIF_F_IPV6_CSUM)) { + netdev_dbg(dev, "Dropping TSO6 features since no CSUM feature.\n"); + features &= ~NETIF_F_TSO6; + } + /* TSO ECN requires that TSO is present as well. */ if ((features & NETIF_F_ALL_TSO) == NETIF_F_TSO_ECN) features &= ~NETIF_F_TSO_ECN; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 33245ef54c3..0a48ae20c90 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2741,12 +2741,19 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) unsigned int tnl_hlen = skb_tnl_header_len(skb); unsigned int headroom; unsigned int len; + __be16 proto; + bool csum; int sg = !!(features & NETIF_F_SG); int nfrags = skb_shinfo(skb)->nr_frags; int err = -ENOMEM; int i = 0; int pos; + proto = skb_network_protocol(skb); + if (unlikely(!proto)) + return ERR_PTR(-EINVAL); + + csum = !!can_checksum_protocol(features, proto); __skb_push(skb, doffset); headroom = skb_headroom(skb); pos = skb_headlen(skb); @@ -2884,6 +2891,12 @@ skip_fraglist: nskb->data_len = len - hsize; nskb->len += nskb->data_len; nskb->truesize += nskb->data_len; + + if (!csum) { + nskb->csum = skb_checksum(nskb, doffset, + nskb->len - doffset, 0); + nskb->ip_summed = CHECKSUM_NONE; + } } while ((offset += len) < skb->len); return segs; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 68f6a94f766..dc3f677360a 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1284,9 +1284,6 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int id; unsigned int offset = 0; - if (!(features & NETIF_F_V4_CSUM)) - features &= ~NETIF_F_SG; - if (unlikely(skb_shinfo(skb)->gso_type & ~(SKB_GSO_TCPV4 | SKB_GSO_UDP | diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 8234c1dcdf7..7a0d25a5479 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -92,9 +92,6 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, u8 *prevhdr; int offset = 0; - if (!(features & NETIF_F_V6_CSUM)) - features &= ~NETIF_F_SG; - if (unlikely(skb_shinfo(skb)->gso_type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | -- cgit v1.2.3-70-g09d2 From aefbd2b3c2a9c657605e4337f9919d6c6273e8e6 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 7 Mar 2013 13:21:46 +0000 Subject: tunneling: Capture inner mac header during encapsulation. This patch adds inner mac header. This will be used in next patch to find tunner header length. Header len is required to copy tunnel header to each gso segment. This patch does not change any functionality. Signed-off-by: Pravin B Shelar Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/skbuff.h | 34 ++++++++++++++++++++++++++++++++++ net/core/skbuff.c | 2 ++ 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 821c7f45d2a..d7f96ff68f7 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -387,6 +387,7 @@ typedef unsigned char *sk_buff_data_t; * @vlan_tci: vlan tag control information * @inner_transport_header: Inner transport layer header (encapsulation) * @inner_network_header: Network layer header (encapsulation) + * @inner_mac_header: Link layer header (encapsulation) * @transport_header: Transport layer header * @network_header: Network layer header * @mac_header: Link layer header @@ -505,6 +506,7 @@ struct sk_buff { sk_buff_data_t inner_transport_header; sk_buff_data_t inner_network_header; + sk_buff_data_t inner_mac_header; sk_buff_data_t transport_header; sk_buff_data_t network_header; sk_buff_data_t mac_header; @@ -1466,6 +1468,7 @@ static inline void skb_reserve(struct sk_buff *skb, int len) static inline void skb_reset_inner_headers(struct sk_buff *skb) { + skb->inner_mac_header = skb->mac_header; skb->inner_network_header = skb->network_header; skb->inner_transport_header = skb->transport_header; } @@ -1511,6 +1514,22 @@ static inline void skb_set_inner_network_header(struct sk_buff *skb, skb->inner_network_header += offset; } +static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb) +{ + return skb->head + skb->inner_mac_header; +} + +static inline void skb_reset_inner_mac_header(struct sk_buff *skb) +{ + skb->inner_mac_header = skb->data - skb->head; +} + +static inline void skb_set_inner_mac_header(struct sk_buff *skb, + const int offset) +{ + skb_reset_inner_mac_header(skb); + skb->inner_mac_header += offset; +} static inline bool skb_transport_header_was_set(const struct sk_buff *skb) { return skb->transport_header != ~0U; @@ -1604,6 +1623,21 @@ static inline void skb_set_inner_network_header(struct sk_buff *skb, skb->inner_network_header = skb->data + offset; } +static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb) +{ + return skb->inner_mac_header; +} + +static inline void skb_reset_inner_mac_header(struct sk_buff *skb) +{ + skb->inner_mac_header = skb->data; +} + +static inline void skb_set_inner_mac_header(struct sk_buff *skb, + const int offset) +{ + skb->inner_mac_header = skb->data + offset; +} static inline bool skb_transport_header_was_set(const struct sk_buff *skb) { return skb->transport_header != NULL; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 0278c7f787b..31c6737d318 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -673,6 +673,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->mac_header = old->mac_header; new->inner_transport_header = old->inner_transport_header; new->inner_network_header = old->inner_network_header; + new->inner_mac_header = old->inner_mac_header; skb_dst_copy(new, old); new->rxhash = old->rxhash; new->ooo_okay = old->ooo_okay; @@ -876,6 +877,7 @@ static void skb_headers_offset_update(struct sk_buff *skb, int off) skb->mac_header += off; skb->inner_transport_header += off; skb->inner_network_header += off; + skb->inner_mac_header += off; } static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) -- cgit v1.2.3-70-g09d2 From 731362674580cb0c696cd1b1a03d8461a10cf90a Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 7 Mar 2013 13:21:51 +0000 Subject: tunneling: Add generic Tunnel segmentation. Adds generic tunneling offloading support for IPv4-UDP based tunnels. GSO type is added to request this offload for a skb. netdev feature NETIF_F_UDP_TUNNEL is added for hardware offloaded udp-tunnel support. Currently no device supports this feature, software offload is used. This can be used by tunneling protocols like VXLAN. CC: Jesse Gross Signed-off-by: Pravin B Shelar Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/netdev_features.h | 7 +-- include/linux/skbuff.h | 2 + net/core/ethtool.c | 1 + net/ipv4/af_inet.c | 6 ++- net/ipv4/tcp.c | 1 + net/ipv4/udp.c | 115 +++++++++++++++++++++++++++++++--------- net/ipv6/ip6_offload.c | 1 + net/ipv6/udp_offload.c | 8 ++- 8 files changed, 111 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 3dd39340430..f5e797c0c2a 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -42,9 +42,9 @@ enum { NETIF_F_TSO6_BIT, /* ... TCPv6 segmentation */ NETIF_F_FSO_BIT, /* ... FCoE segmentation */ NETIF_F_GSO_GRE_BIT, /* ... GRE with TSO */ - /**/NETIF_F_GSO_LAST, /* [can't be last bit, see GSO_MASK] */ - NETIF_F_GSO_RESERVED2 /* ... free (fill GSO_MASK to 8 bits) */ - = NETIF_F_GSO_LAST, + NETIF_F_GSO_UDP_TUNNEL_BIT, /* ... UDP TUNNEL with TSO */ + /**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */ + NETIF_F_GSO_UDP_TUNNEL_BIT, NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */ NETIF_F_SCTP_CSUM_BIT, /* SCTP checksum offload */ @@ -103,6 +103,7 @@ enum { #define NETIF_F_RXFCS __NETIF_F(RXFCS) #define NETIF_F_RXALL __NETIF_F(RXALL) #define NETIF_F_GRE_GSO __NETIF_F(GSO_GRE) +#define NETIF_F_UDP_TUNNEL __NETIF_F(UDP_TUNNEL) /* Features valid for ethtool to change */ /* = all defined minus driver/device-class-related */ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d7f96ff68f7..eb2106fe3bb 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -316,6 +316,8 @@ enum { SKB_GSO_FCOE = 1 << 5, SKB_GSO_GRE = 1 << 6, + + SKB_GSO_UDP_TUNNEL = 1 << 7, }; #if BITS_PER_LONG > 32 diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 3e9b2c3e30f..adc1351e687 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -78,6 +78,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation", [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation", [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation", + [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation", [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp", diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index dc3f677360a..9e5882caf8a 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1283,6 +1283,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int ihl; int id; unsigned int offset = 0; + bool tunnel; if (unlikely(skb_shinfo(skb)->gso_type & ~(SKB_GSO_TCPV4 | @@ -1290,6 +1291,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_GRE | + SKB_GSO_UDP_TUNNEL | 0))) goto out; @@ -1304,6 +1306,8 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, ihl))) goto out; + tunnel = !!skb->encapsulation; + __skb_pull(skb, ihl); skb_reset_transport_header(skb); iph = ip_hdr(skb); @@ -1323,7 +1327,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, skb = segs; do { iph = ip_hdr(skb); - if (proto == IPPROTO_UDP) { + if (!tunnel && proto == IPPROTO_UDP) { iph->id = htons(id); iph->frag_off = htons(offset >> 3); if (skb->next != NULL) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 47e854fcae2..8d14573ade7 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3044,6 +3044,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, SKB_GSO_TCP_ECN | SKB_GSO_TCPV6 | SKB_GSO_GRE | + SKB_GSO_UDP_TUNNEL | 0) || !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) goto out; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 265c42cf963..41760e043bf 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2272,31 +2272,88 @@ void __init udp_init(void) int udp4_ufo_send_check(struct sk_buff *skb) { - const struct iphdr *iph; - struct udphdr *uh; - - if (!pskb_may_pull(skb, sizeof(*uh))) + if (!pskb_may_pull(skb, sizeof(struct udphdr))) return -EINVAL; - iph = ip_hdr(skb); - uh = udp_hdr(skb); + if (likely(!skb->encapsulation)) { + const struct iphdr *iph; + struct udphdr *uh; - uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, - IPPROTO_UDP, 0); - skb->csum_start = skb_transport_header(skb) - skb->head; - skb->csum_offset = offsetof(struct udphdr, check); - skb->ip_summed = CHECKSUM_PARTIAL; + iph = ip_hdr(skb); + uh = udp_hdr(skb); + + uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, + IPPROTO_UDP, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + skb->ip_summed = CHECKSUM_PARTIAL; + } return 0; } +static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + int mac_len = skb->mac_len; + int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb); + int outer_hlen; + netdev_features_t enc_features; + + if (unlikely(!pskb_may_pull(skb, tnl_hlen))) + goto out; + + skb->encapsulation = 0; + __skb_pull(skb, tnl_hlen); + skb_reset_mac_header(skb); + skb_set_network_header(skb, skb_inner_network_offset(skb)); + skb->mac_len = skb_inner_network_offset(skb); + + /* segment inner packet. */ + enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); + segs = skb_mac_gso_segment(skb, enc_features); + if (!segs || IS_ERR(segs)) + goto out; + + outer_hlen = skb_tnl_header_len(skb); + skb = segs; + do { + struct udphdr *uh; + int udp_offset = outer_hlen - tnl_hlen; + + skb->mac_len = mac_len; + + skb_push(skb, outer_hlen); + skb_reset_mac_header(skb); + skb_set_network_header(skb, mac_len); + skb_set_transport_header(skb, udp_offset); + uh = udp_hdr(skb); + uh->len = htons(skb->len - udp_offset); + + /* csum segment if tunnel sets skb with csum. */ + if (unlikely(uh->check)) { + struct iphdr *iph = ip_hdr(skb); + + uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, + skb->len - udp_offset, + IPPROTO_UDP, 0); + uh->check = csum_fold(skb_checksum(skb, udp_offset, + skb->len - udp_offset, 0)); + if (uh->check == 0) + uh->check = CSUM_MANGLED_0; + + } + skb->ip_summed = CHECKSUM_NONE; + } while ((skb = skb->next)); +out: + return segs; +} + struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EINVAL); unsigned int mss; - int offset; - __wsum csum; - mss = skb_shinfo(skb)->gso_size; if (unlikely(skb->len <= mss)) goto out; @@ -2306,6 +2363,7 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int type = skb_shinfo(skb)->gso_type; if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | + SKB_GSO_UDP_TUNNEL | SKB_GSO_GRE) || !(type & (SKB_GSO_UDP)))) goto out; @@ -2316,20 +2374,27 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, goto out; } - /* Do software UFO. Complete and fill in the UDP checksum as HW cannot - * do checksum of UDP packets sent as multiple IP fragments. - */ - offset = skb_checksum_start_offset(skb); - csum = skb_checksum(skb, offset, skb->len - offset, 0); - offset += skb->csum_offset; - *(__sum16 *)(skb->data + offset) = csum_fold(csum); - skb->ip_summed = CHECKSUM_NONE; - /* Fragment the skb. IP headers of the fragments are updated in * inet_gso_segment() */ - segs = skb_segment(skb, features); + if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) + segs = skb_udp_tunnel_segment(skb, features); + else { + int offset; + __wsum csum; + + /* Do software UFO. Complete and fill in the UDP checksum as + * HW cannot do checksum of UDP packets sent as multiple + * IP fragments. + */ + offset = skb_checksum_start_offset(skb); + csum = skb_checksum(skb, offset, skb->len - offset, 0); + offset += skb->csum_offset; + *(__sum16 *)(skb->data + offset) = csum_fold(csum); + skb->ip_summed = CHECKSUM_NONE; + + segs = skb_segment(skb, features); + } out: return segs; } - diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 7a0d25a5479..71b766ee821 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -97,6 +97,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_GRE | + SKB_GSO_UDP_TUNNEL | SKB_GSO_TCPV6 | 0))) goto out; diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index cf05cf073c5..3bb3a891a42 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -21,6 +21,10 @@ static int udp6_ufo_send_check(struct sk_buff *skb) const struct ipv6hdr *ipv6h; struct udphdr *uh; + /* UDP Tunnel offload on ipv6 is not yet supported. */ + if (skb->encapsulation) + return -EINVAL; + if (!pskb_may_pull(skb, sizeof(*uh))) return -EINVAL; @@ -56,7 +60,9 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, /* Packet is from an untrusted source, reset gso_segs. */ int type = skb_shinfo(skb)->gso_type; - if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | + if (unlikely(type & ~(SKB_GSO_UDP | + SKB_GSO_DODGY | + SKB_GSO_UDP_TUNNEL | SKB_GSO_GRE) || !(type & (SKB_GSO_UDP)))) goto out; -- cgit v1.2.3-70-g09d2 From 6aed0c8bf7d2f389b472834053eb6e3bd6926999 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sat, 9 Mar 2013 16:38:39 +0000 Subject: tunnel: use iptunnel_xmit() again With recent patches from Pravin, most tunnels can't use iptunnel_xmit() any more, due to ip_select_ident() and skb->ip_summed. But we can just move these operations out of iptunnel_xmit(), so that tunnels can use it again. This by the way fixes a bug in vxlan (missing nf_reset()) for net-next. Cc: Pravin B Shelar Cc: Stephen Hemminger Cc: "David S. Miller" Signed-off-by: Cong Wang Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 14 +------------- include/net/ipip.h | 3 --- net/ipv4/ip_gre.c | 16 +--------------- net/ipv4/ipip.c | 18 +----------------- net/ipv6/sit.c | 2 ++ 5 files changed, 5 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index f057ec00bba..f3a135cb50a 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -855,7 +855,6 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) __u16 src_port; __be16 df = 0; __u8 tos, ttl; - int err; bool did_rsc = false; const struct vxlan_fdb *f; @@ -980,18 +979,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) if (handle_offloads(skb)) goto drop; - err = ip_local_out(skb); - if (likely(net_xmit_eval(err) == 0)) { - struct vxlan_stats *stats = this_cpu_ptr(vxlan->stats); - - u64_stats_update_begin(&stats->syncp); - stats->tx_packets++; - stats->tx_bytes += pkt_len; - u64_stats_update_end(&stats->syncp); - } else { - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - } + iptunnel_xmit(skb, dev); return NETDEV_TX_OK; drop: diff --git a/include/net/ipip.h b/include/net/ipip.h index fd19625ff99..0c046e3bca0 100644 --- a/include/net/ipip.h +++ b/include/net/ipip.h @@ -51,13 +51,10 @@ struct ip_tunnel_prl_entry { static inline void iptunnel_xmit(struct sk_buff *skb, struct net_device *dev) { int err; - struct iphdr *iph = ip_hdr(skb); int pkt_len = skb->len - skb_transport_offset(skb); struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats); nf_reset(skb); - skb->ip_summed = CHECKSUM_NONE; - ip_select_ident(iph, skb_dst(skb), NULL); err = ip_local_out(skb); if (likely(net_xmit_eval(err) == 0)) { diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index d0ef0e674ec..a13a0972a57 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -762,7 +762,6 @@ error: static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { - struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats); struct ip_tunnel *tunnel = netdev_priv(dev); const struct iphdr *old_iph; const struct iphdr *tiph; @@ -778,7 +777,6 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev int mtu; u8 ttl; int err; - int pkt_len; skb = handle_offloads(tunnel, skb); if (IS_ERR(skb)) { @@ -1022,19 +1020,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev } } - nf_reset(skb); - - pkt_len = skb->len - skb_transport_offset(skb); - err = ip_local_out(skb); - if (likely(net_xmit_eval(err) == 0)) { - u64_stats_update_begin(&tstats->syncp); - tstats->tx_bytes += pkt_len; - tstats->tx_packets++; - u64_stats_update_end(&tstats->syncp); - } else { - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - } + iptunnel_xmit(skb, dev); return NETDEV_TX_OK; #if IS_ENABLED(CONFIG_IPV6) diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index b50435ba0ce..34e006fe2d8 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -478,8 +478,6 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) __be32 dst = tiph->daddr; struct flowi4 fl4; int mtu; - int err; - int pkt_len; if (skb->protocol != htons(ETH_P_IP)) goto tx_error; @@ -600,21 +598,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) if ((iph->ttl = tiph->ttl) == 0) iph->ttl = old_iph->ttl; - nf_reset(skb); - - pkt_len = skb->len - skb_transport_offset(skb); - err = ip_local_out(skb); - if (likely(net_xmit_eval(err) == 0)) { - struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats); - - u64_stats_update_begin(&tstats->syncp); - tstats->tx_bytes += pkt_len; - tstats->tx_packets++; - u64_stats_update_end(&tstats->syncp); - } else { - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - } + iptunnel_xmit(skb, dev); return NETDEV_TX_OK; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 02f96dcbcf0..898e671a526 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -899,6 +899,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, if ((iph->ttl = tiph->ttl) == 0) iph->ttl = iph6->hop_limit; + skb->ip_summed = CHECKSUM_NONE; + ip_select_ident(iph, skb_dst(skb), NULL); iptunnel_xmit(skb, dev); return NETDEV_TX_OK; -- cgit v1.2.3-70-g09d2 From 6150f3bc0b4f94f0eea3e32b4e7462025e4bd972 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Wed, 20 Feb 2013 17:10:23 +0100 Subject: ARM: AT91SAM9G45: same platform data structure for all crypto peripherals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only AES use DMA in AT91SAM9G45 (TDES and SHA use PDC). However latest Atmel TDES and SHA IP releases use DMA instead of PDC. --> Atmel TDES and SHA drivers need DMA platform data for those IP releases. Goal of this patch is to use the same platform data structure for all Atmel crypto peripherals. This structure contains information about DMA interface. Signed-off-by: Nicolas Royer Acked-by: Nicolas Ferre Acked-by: Eric Bénard Tested-by: Eric Bénard Signed-off-by: Herbert Xu --- arch/arm/mach-at91/at91sam9g45_devices.c | 14 ++++++-------- include/linux/platform_data/atmel-aes.h | 22 ---------------------- include/linux/platform_data/crypto-atmel.h | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 30 deletions(-) delete mode 100644 include/linux/platform_data/atmel-aes.h create mode 100644 include/linux/platform_data/crypto-atmel.h (limited to 'include') diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index 827c9f2a70f..f0bf68268ca 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include @@ -1900,7 +1900,8 @@ static void __init at91_add_device_tdes(void) {} * -------------------------------------------------------------------- */ #if defined(CONFIG_CRYPTO_DEV_ATMEL_AES) || defined(CONFIG_CRYPTO_DEV_ATMEL_AES_MODULE) -static struct aes_platform_data aes_data; +static struct crypto_platform_data aes_data; +static struct crypto_dma_data alt_atslave; static u64 aes_dmamask = DMA_BIT_MASK(32); static struct resource aes_resources[] = { @@ -1931,23 +1932,20 @@ static struct platform_device at91sam9g45_aes_device = { static void __init at91_add_device_aes(void) { struct at_dma_slave *atslave; - struct aes_dma_data *alt_atslave; - - alt_atslave = kzalloc(sizeof(struct aes_dma_data), GFP_KERNEL); /* DMA TX slave channel configuration */ - atslave = &alt_atslave->txdata; + atslave = &alt_atslave.txdata; atslave->dma_dev = &at_hdmac_device.dev; atslave->cfg = ATC_FIFOCFG_ENOUGHSPACE | ATC_SRC_H2SEL_HW | ATC_SRC_PER(AT_DMA_ID_AES_RX); /* DMA RX slave channel configuration */ - atslave = &alt_atslave->rxdata; + atslave = &alt_atslave.rxdata; atslave->dma_dev = &at_hdmac_device.dev; atslave->cfg = ATC_FIFOCFG_ENOUGHSPACE | ATC_DST_H2SEL_HW | ATC_DST_PER(AT_DMA_ID_AES_TX); - aes_data.dma_slave = alt_atslave; + aes_data.dma_slave = &alt_atslave; platform_device_register(&at91sam9g45_aes_device); } #else diff --git a/include/linux/platform_data/atmel-aes.h b/include/linux/platform_data/atmel-aes.h deleted file mode 100644 index ab68082fbcb..00000000000 --- a/include/linux/platform_data/atmel-aes.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef __LINUX_ATMEL_AES_H -#define __LINUX_ATMEL_AES_H - -#include - -/** - * struct aes_dma_data - DMA data for AES - */ -struct aes_dma_data { - struct at_dma_slave txdata; - struct at_dma_slave rxdata; -}; - -/** - * struct aes_platform_data - board-specific AES configuration - * @dma_slave: DMA slave interface to use in data transfers. - */ -struct aes_platform_data { - struct aes_dma_data *dma_slave; -}; - -#endif /* __LINUX_ATMEL_AES_H */ diff --git a/include/linux/platform_data/crypto-atmel.h b/include/linux/platform_data/crypto-atmel.h new file mode 100644 index 00000000000..b46e0d9062a --- /dev/null +++ b/include/linux/platform_data/crypto-atmel.h @@ -0,0 +1,22 @@ +#ifndef __LINUX_CRYPTO_ATMEL_H +#define __LINUX_CRYPTO_ATMEL_H + +#include + +/** + * struct crypto_dma_data - DMA data for AES/TDES/SHA + */ +struct crypto_dma_data { + struct at_dma_slave txdata; + struct at_dma_slave rxdata; +}; + +/** + * struct crypto_platform_data - board-specific AES/TDES/SHA configuration + * @dma_slave: DMA slave interface to use in data transfers. + */ +struct crypto_platform_data { + struct crypto_dma_data *dma_slave; +}; + +#endif /* __LINUX_CRYPTO_ATMEL_H */ -- cgit v1.2.3-70-g09d2 From e8f72ea4a1380eeca10a551bc8d678e7d4388d42 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sat, 9 Mar 2013 23:00:39 +0000 Subject: ipv6: introduce ip6tunnel_xmit() helper Similar to iptunnel_xmit(), group these operations into a helper function. This by the way fixes the missing u64_stats_update_begin() and u64_stats_update_end() for 32 bit arch. Cc: Eric Dumazet Cc: Pravin B Shelar Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/ip6_tunnel.h | 20 ++++++++++++++++++++ net/ipv6/ip6_gre.c | 17 +---------------- net/ipv6/ip6_tunnel.c | 15 +-------------- 3 files changed, 22 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index e03047f7090..ebdef7f6086 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -68,4 +68,24 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw); __u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr, const struct in6_addr *raddr); +static inline void ip6tunnel_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + int pkt_len, err; + + nf_reset(skb); + pkt_len = skb->len; + err = ip6_local_out(skb); + + if (net_xmit_eval(err) == 0) { + struct pcpu_tstats *tstats = this_cpu_ptr(dev->tstats); + u64_stats_update_begin(&tstats->syncp); + tstats->tx_bytes += pkt_len; + tstats->tx_packets++; + u64_stats_update_end(&tstats->syncp); + } else { + stats->tx_errors++; + stats->tx_aborted_errors++; + } +} #endif diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index e4efffe2522..6a6ba73ff26 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -667,7 +667,6 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb, struct net_device_stats *stats = &tunnel->dev->stats; int err = -1; u8 proto; - int pkt_len; struct sk_buff *new_skb; if (dev->type == ARPHRD_ETHER) @@ -801,23 +800,9 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb, } } - nf_reset(skb); - pkt_len = skb->len; - err = ip6_local_out(skb); - - if (net_xmit_eval(err) == 0) { - struct pcpu_tstats *tstats = this_cpu_ptr(tunnel->dev->tstats); - - tstats->tx_bytes += pkt_len; - tstats->tx_packets++; - } else { - stats->tx_errors++; - stats->tx_aborted_errors++; - } - + ip6tunnel_xmit(skb, dev); if (ndst) ip6_tnl_dst_store(tunnel, ndst); - return 0; tx_err_link_failure: stats->tx_carrier_errors++; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index fff83cbc197..bef3fedfdc5 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -955,7 +955,6 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, unsigned int max_headroom = sizeof(struct ipv6hdr); u8 proto; int err = -1; - int pkt_len; if (!fl6->flowi6_mark) dst = ip6_tnl_dst_check(t); @@ -1035,19 +1034,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, ipv6h->nexthdr = proto; ipv6h->saddr = fl6->saddr; ipv6h->daddr = fl6->daddr; - nf_reset(skb); - pkt_len = skb->len; - err = ip6_local_out(skb); - - if (net_xmit_eval(err) == 0) { - struct pcpu_tstats *tstats = this_cpu_ptr(t->dev->tstats); - - tstats->tx_bytes += pkt_len; - tstats->tx_packets++; - } else { - stats->tx_errors++; - stats->tx_aborted_errors++; - } + ip6tunnel_xmit(skb, dev); if (ndst) ip6_tnl_dst_store(t, ndst); return 0; -- cgit v1.2.3-70-g09d2 From e61667af2f77d481411f2ccd307fed2247d785a8 Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Sun, 10 Mar 2013 05:18:39 +0000 Subject: tcp: Remove unused tw_cookie_values from tcp_timewait_sock tw_cookie_values is never used in the TCP-stack. It was added by 435cf559f (TCPCT part 1d: define TCP cookie option, extend existing struct's), but already at that time it was not used at all, nor mentioned in the commit-message. Signed-off-by: Christoph Paasch Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/tcp.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index f28408c07dc..515c3746b67 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -361,10 +361,6 @@ struct tcp_timewait_sock { #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *tw_md5_key; #endif - /* Few sockets in timewait have cookies; in that case, then this - * object holds a reference to them (tw_cookie_values->kref). - */ - struct tcp_cookie_values *tw_cookie_values; }; static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk) -- cgit v1.2.3-70-g09d2 From 26fd76cab2e61cedc5c25f7151fb31b57ddc53c7 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 22 Feb 2013 10:53:25 +0100 Subject: NFC: llcp: Implement socket options Some LLCP services (e.g. the validation ones) require some control over the LLCP link parameters like the receive window (RW) or the MIU extension (MIUX). This can only be done through socket options. Signed-off-by: Samuel Ortiz --- include/linux/socket.h | 1 + include/uapi/linux/nfc.h | 4 ++ net/nfc/llcp/llcp.h | 3 ++ net/nfc/llcp/sock.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 125 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/socket.h b/include/linux/socket.h index 2b9f74b0ffe..428c37a1f95 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -298,6 +298,7 @@ struct ucred { #define SOL_IUCV 277 #define SOL_CAIF 278 #define SOL_ALG 279 +#define SOL_NFC 280 /* IPX options */ #define IPX_TYPE 1 diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 7969f46f1bb..855630fe731 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -220,4 +220,8 @@ struct sockaddr_nfc_llcp { #define NFC_LLCP_DIRECTION_RX 0x00 #define NFC_LLCP_DIRECTION_TX 0x01 +/* socket option names */ +#define NFC_LLCP_RW 0 +#define NFC_LLCP_MIUX 1 + #endif /*__LINUX_NFC_H */ diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h index 32cec81939e..5f117adac2e 100644 --- a/net/nfc/llcp/llcp.h +++ b/net/nfc/llcp/llcp.h @@ -104,6 +104,9 @@ struct nfc_llcp_sock { u8 dsap; char *service_name; size_t service_name_len; + u8 rw; + u16 miux; + /* Remote link parameters */ u8 remote_rw; diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index cc564992ba9..9357a756f7a 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -223,6 +223,121 @@ error: return ret; } +static int nfc_llcp_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk); + u32 opt; + int err = 0; + + pr_debug("%p optname %d\n", sk, optname); + + if (level != SOL_NFC) + return -ENOPROTOOPT; + + lock_sock(sk); + + switch (optname) { + case NFC_LLCP_RW: + if (sk->sk_state == LLCP_CONNECTED || + sk->sk_state == LLCP_BOUND || + sk->sk_state == LLCP_LISTEN) { + err = -EINVAL; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + if (opt > LLCP_MAX_RW) { + err = -EINVAL; + break; + } + + llcp_sock->rw = (u8) opt; + + break; + + case NFC_LLCP_MIUX: + if (sk->sk_state == LLCP_CONNECTED || + sk->sk_state == LLCP_BOUND || + sk->sk_state == LLCP_LISTEN) { + err = -EINVAL; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + if (opt > LLCP_MAX_MIUX) { + err = -EINVAL; + break; + } + + llcp_sock->miux = (u16) opt; + + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + + return err; +} + +static int nfc_llcp_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk); + int len, err = 0; + + pr_debug("%p optname %d\n", sk, optname); + + if (level != SOL_NFC) + return -ENOPROTOOPT; + + if (get_user(len, optlen)) + return -EFAULT; + + len = min_t(u32, len, sizeof(u32)); + + lock_sock(sk); + + switch (optname) { + case NFC_LLCP_RW: + if (put_user(llcp_sock->rw, (u32 __user *) optval)) + err = -EFAULT; + + break; + + case NFC_LLCP_MIUX: + if (put_user(llcp_sock->miux, (u32 __user *) optval)) + err = -EFAULT; + + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + + if (put_user(len, optlen)) + return -EFAULT; + + return err; +} + void nfc_llcp_accept_unlink(struct sock *sk) { struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk); @@ -735,8 +850,8 @@ static const struct proto_ops llcp_sock_ops = { .ioctl = sock_no_ioctl, .listen = llcp_sock_listen, .shutdown = sock_no_shutdown, - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, + .setsockopt = nfc_llcp_setsockopt, + .getsockopt = nfc_llcp_getsockopt, .sendmsg = llcp_sock_sendmsg, .recvmsg = llcp_sock_recvmsg, .mmap = sock_no_mmap, -- cgit v1.2.3-70-g09d2 From d9b8d8e19b073096d3609bbd60f82148d128b555 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Fri, 15 Feb 2013 10:43:06 +0100 Subject: NFC: llcp: Service Name Lookup netlink interface This adds a netlink interface for service name lookup support. Multiple URIs can be passed nested into the NFC_ATTR_LLC_SDP attribute using the NFC_CMD_LLC_SDREQ netlink command. When the SNL reply is received, a NFC_EVENT_LLC_SDRES event is sent to the user space. URI and SAP tuples are passed back, nested into NFC_ATTR_LLC_SDP attribute. Signed-off-by: Thierry Escande Signed-off-by: Samuel Ortiz --- include/uapi/linux/nfc.h | 12 ++++ net/nfc/llcp/commands.c | 78 ++++++++++++++++++++++ net/nfc/llcp/llcp.c | 32 +++++++++ net/nfc/llcp/llcp.h | 9 +++ net/nfc/netlink.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++ net/nfc/nfc.h | 14 ++++ 6 files changed, 314 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 855630fe731..7440bc81a04 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -90,6 +90,8 @@ enum nfc_commands { NFC_CMD_LLC_SET_PARAMS, NFC_CMD_ENABLE_SE, NFC_CMD_DISABLE_SE, + NFC_CMD_LLC_SDREQ, + NFC_EVENT_LLC_SDRES, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; @@ -140,11 +142,21 @@ enum nfc_attrs { NFC_ATTR_LLC_PARAM_RW, NFC_ATTR_LLC_PARAM_MIUX, NFC_ATTR_SE, + NFC_ATTR_LLC_SDP, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; #define NFC_ATTR_MAX (__NFC_ATTR_AFTER_LAST - 1) +enum nfc_sdp_attr { + NFC_SDP_ATTR_UNSPEC, + NFC_SDP_ATTR_URI, + NFC_SDP_ATTR_SAP, +/* private: internal use only */ + __NFC_SDP_ATTR_AFTER_LAST +}; +#define NFC_SDP_ATTR_MAX (__NFC_SDP_ATTR_AFTER_LAST - 1) + #define NFC_DEVICE_NAME_MAXSIZE 8 #define NFC_NFCID1_MAXSIZE 10 #define NFC_SENSB_RES_MAXSIZE 12 diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c index 59f7ffca783..c943edb07b7 100644 --- a/net/nfc/llcp/commands.c +++ b/net/nfc/llcp/commands.c @@ -144,12 +144,59 @@ struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap) return sdres; } +struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri, + size_t uri_len) +{ + struct nfc_llcp_sdp_tlv *sdreq; + + pr_debug("uri: %s, len: %zu\n", uri, uri_len); + + sdreq = kzalloc(sizeof(struct nfc_llcp_sdp_tlv), GFP_KERNEL); + if (sdreq == NULL) + return NULL; + + sdreq->tlv_len = uri_len + 3; + + if (uri[uri_len - 1] == 0) + sdreq->tlv_len--; + + sdreq->tlv = kzalloc(sdreq->tlv_len + 1, GFP_KERNEL); + if (sdreq->tlv == NULL) { + kfree(sdreq); + return NULL; + } + + sdreq->tlv[0] = LLCP_TLV_SDREQ; + sdreq->tlv[1] = sdreq->tlv_len - 2; + sdreq->tlv[2] = tid; + + sdreq->tid = tid; + sdreq->uri = sdreq->tlv + 3; + memcpy(sdreq->uri, uri, uri_len); + + INIT_HLIST_NODE(&sdreq->node); + + return sdreq; +} + void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp) { kfree(sdp->tlv); kfree(sdp); } +void nfc_llcp_free_sdp_tlv_list(struct hlist_head *head) +{ + struct nfc_llcp_sdp_tlv *sdp; + struct hlist_node *n; + + hlist_for_each_entry_safe(sdp, n, head, node) { + hlist_del(&sdp->node); + + nfc_llcp_free_sdp_tlv(sdp); + } +} + int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local, u8 *tlv_array, u16 tlv_array_len) { @@ -511,6 +558,37 @@ int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local, return 0; } +int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local, + struct hlist_head *tlv_list, size_t tlvs_len) +{ + struct nfc_llcp_sdp_tlv *sdreq; + struct hlist_node *n; + struct sk_buff *skb; + + skb = nfc_llcp_allocate_snl(local, tlvs_len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mutex_lock(&local->sdreq_lock); + + hlist_for_each_entry_safe(sdreq, n, tlv_list, node) { + pr_debug("tid %d for %s\n", sdreq->tid, sdreq->uri); + + memcpy(skb_put(skb, sdreq->tlv_len), sdreq->tlv, + sdreq->tlv_len); + + hlist_del(&sdreq->node); + + hlist_add_head(&sdreq->node, &local->pending_sdreqs); + } + + mutex_unlock(&local->sdreq_lock); + + skb_queue_tail(&local->tx_queue, skb); + + return 0; +} + int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason) { struct sk_buff *skb; diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 30b61c1aa98..99e91106042 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -156,6 +156,7 @@ static void local_release(struct kref *ref) cancel_work_sync(&local->rx_work); cancel_work_sync(&local->timeout_work); kfree_skb(local->rx_pending); + nfc_llcp_free_sdp_tlv_list(&local->pending_sdreqs); kfree(local); } @@ -1147,6 +1148,7 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local, struct nfc_llcp_sdp_tlv *sdp; HLIST_HEAD(llc_sdres_list); size_t sdres_tlvs_len; + HLIST_HEAD(nl_sdres_list); dsap = nfc_llcp_dsap(skb); ssap = nfc_llcp_ssap(skb); @@ -1229,6 +1231,30 @@ add_snl: hlist_add_head(&sdp->node, &llc_sdres_list); break; + case LLCP_TLV_SDRES: + mutex_lock(&local->sdreq_lock); + + pr_debug("LLCP_TLV_SDRES: searching tid %d\n", tlv[2]); + + hlist_for_each_entry(sdp, &local->pending_sdreqs, node) { + if (sdp->tid != tlv[2]) + continue; + + sdp->sap = tlv[3]; + + pr_debug("Found: uri=%s, sap=%d\n", + sdp->uri, sdp->sap); + + hlist_del(&sdp->node); + + hlist_add_head(&sdp->node, &nl_sdres_list); + + break; + } + + mutex_unlock(&local->sdreq_lock); + break; + default: pr_err("Invalid SNL tlv value 0x%x\n", type); break; @@ -1239,6 +1265,9 @@ add_snl: } exit: + if (!hlist_empty(&nl_sdres_list)) + nfc_genl_llc_send_sdres(local->dev, &nl_sdres_list); + if (!hlist_empty(&llc_sdres_list)) nfc_llcp_send_snl_sdres(local, &llc_sdres_list, sdres_tlvs_len); } @@ -1426,6 +1455,9 @@ int nfc_llcp_register_device(struct nfc_dev *ndev) local->remote_miu = LLCP_DEFAULT_MIU; local->remote_lto = LLCP_DEFAULT_LTO; + mutex_init(&local->sdreq_lock); + INIT_HLIST_HEAD(&local->pending_sdreqs); + list_add(&local->list, &llcp_devices); return 0; diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h index 465f5953e2d..ca8c6d94ab8 100644 --- a/net/nfc/llcp/llcp.h +++ b/net/nfc/llcp/llcp.h @@ -97,6 +97,10 @@ struct nfc_llcp_local { u8 remote_opt; u16 remote_wks; + struct mutex sdreq_lock; + struct hlist_head pending_sdreqs; + u8 sdreq_next_tid; + /* sockets array */ struct llcp_sock_list sockets; struct llcp_sock_list connecting_sockets; @@ -230,7 +234,10 @@ int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock, void nfc_llcp_recv(void *data, struct sk_buff *skb, int err); u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length); struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdres_tlv(u8 tid, u8 sap); +struct nfc_llcp_sdp_tlv *nfc_llcp_build_sdreq_tlv(u8 tid, char *uri, + size_t uri_len); void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp); +void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head); void nfc_llcp_recv(void *data, struct sk_buff *skb, int err); int nfc_llcp_disconnect(struct nfc_llcp_sock *sock); int nfc_llcp_send_symm(struct nfc_dev *dev); @@ -238,6 +245,8 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock); int nfc_llcp_send_cc(struct nfc_llcp_sock *sock); int nfc_llcp_send_snl_sdres(struct nfc_llcp_local *local, struct hlist_head *tlv_list, size_t tlvs_len); +int nfc_llcp_send_snl_sdreq(struct nfc_llcp_local *local, + struct hlist_head *tlv_list, size_t tlvs_len); int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason); int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock); int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 63975c039b8..73fd51098f4 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -56,6 +56,12 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { [NFC_ATTR_LLC_PARAM_LTO] = { .type = NLA_U8 }, [NFC_ATTR_LLC_PARAM_RW] = { .type = NLA_U8 }, [NFC_ATTR_LLC_PARAM_MIUX] = { .type = NLA_U16 }, + [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED }, +}; + +static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = { + [NFC_SDP_ATTR_URI] = { .type = NLA_STRING }, + [NFC_SDP_ATTR_SAP] = { .type = NLA_U8 }, }; static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target, @@ -351,6 +357,74 @@ free_msg: return -EMSGSIZE; } +int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list) +{ + struct sk_buff *msg; + struct nlattr *sdp_attr, *uri_attr; + struct nfc_llcp_sdp_tlv *sdres; + struct hlist_node *n; + void *hdr; + int rc = -EMSGSIZE; + int i; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, + NFC_EVENT_LLC_SDRES); + if (!hdr) + goto free_msg; + + if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) + goto nla_put_failure; + + sdp_attr = nla_nest_start(msg, NFC_ATTR_LLC_SDP); + if (sdp_attr == NULL) { + rc = -ENOMEM; + goto nla_put_failure; + } + + i = 1; + hlist_for_each_entry_safe(sdres, n, sdres_list, node) { + pr_debug("uri: %s, sap: %d\n", sdres->uri, sdres->sap); + + uri_attr = nla_nest_start(msg, i++); + if (uri_attr == NULL) { + rc = -ENOMEM; + goto nla_put_failure; + } + + if (nla_put_u8(msg, NFC_SDP_ATTR_SAP, sdres->sap)) + goto nla_put_failure; + + if (nla_put_string(msg, NFC_SDP_ATTR_URI, sdres->uri)) + goto nla_put_failure; + + nla_nest_end(msg, uri_attr); + + hlist_del(&sdres->node); + + nfc_llcp_free_sdp_tlv(sdres); + } + + nla_nest_end(msg, sdp_attr); + + genlmsg_end(msg, hdr); + + return genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_ATOMIC); + +nla_put_failure: + genlmsg_cancel(msg, hdr); + +free_msg: + nlmsg_free(msg); + + nfc_llcp_free_sdp_tlv_list(sdres_list); + + return rc; +} + static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, u32 portid, u32 seq, struct netlink_callback *cb, @@ -862,6 +936,96 @@ exit: return rc; } +static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + struct nfc_llcp_local *local; + struct nlattr *attr, *sdp_attrs[NFC_SDP_ATTR_MAX+1]; + u32 idx; + u8 tid; + char *uri; + int rc = 0, rem; + size_t uri_len, tlvs_len; + struct hlist_head sdreq_list; + struct nfc_llcp_sdp_tlv *sdreq; + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || + !info->attrs[NFC_ATTR_LLC_SDP]) + return -EINVAL; + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) { + rc = -ENODEV; + goto exit; + } + + device_lock(&dev->dev); + + if (dev->dep_link_up == false) { + rc = -ENOLINK; + goto exit; + } + + local = nfc_llcp_find_local(dev); + if (!local) { + nfc_put_device(dev); + rc = -ENODEV; + goto exit; + } + + INIT_HLIST_HEAD(&sdreq_list); + + tlvs_len = 0; + + nla_for_each_nested(attr, info->attrs[NFC_ATTR_LLC_SDP], rem) { + rc = nla_parse_nested(sdp_attrs, NFC_SDP_ATTR_MAX, attr, + nfc_sdp_genl_policy); + + if (rc != 0) { + rc = -EINVAL; + goto exit; + } + + if (!sdp_attrs[NFC_SDP_ATTR_URI]) + continue; + + uri_len = nla_len(sdp_attrs[NFC_SDP_ATTR_URI]); + if (uri_len == 0) + continue; + + uri = nla_data(sdp_attrs[NFC_SDP_ATTR_URI]); + if (uri == NULL || *uri == 0) + continue; + + tid = local->sdreq_next_tid++; + + sdreq = nfc_llcp_build_sdreq_tlv(tid, uri, uri_len); + if (sdreq == NULL) { + rc = -ENOMEM; + goto exit; + } + + tlvs_len += sdreq->tlv_len; + + hlist_add_head(&sdreq->node, &sdreq_list); + } + + if (hlist_empty(&sdreq_list)) { + rc = -EINVAL; + goto exit; + } + + rc = nfc_llcp_send_snl_sdreq(local, &sdreq_list, tlvs_len); +exit: + device_unlock(&dev->dev); + + nfc_put_device(dev); + + return rc; +} + static struct genl_ops nfc_genl_ops[] = { { .cmd = NFC_CMD_GET_DEVICE, @@ -916,6 +1080,11 @@ static struct genl_ops nfc_genl_ops[] = { .doit = nfc_genl_llc_set_params, .policy = nfc_genl_policy, }, + { + .cmd = NFC_CMD_LLC_SDREQ, + .doit = nfc_genl_llc_sdreq, + .policy = nfc_genl_policy, + }, }; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 87d914d2876..94bfe19ba67 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -46,6 +46,8 @@ struct nfc_rawsock { #define to_rawsock_sk(_tx_work) \ ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work)) +struct nfc_llcp_sdp_tlv; + #ifdef CONFIG_NFC_LLCP void nfc_llcp_mac_is_down(struct nfc_dev *dev); @@ -59,6 +61,8 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb); struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev); int __init nfc_llcp_init(void); void nfc_llcp_exit(void); +void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp); +void nfc_llcp_free_sdp_tlv_list(struct hlist_head *head); #else @@ -112,6 +116,14 @@ static inline void nfc_llcp_exit(void) { } +static inline void nfc_llcp_free_sdp_tlv(struct nfc_llcp_sdp_tlv *sdp) +{ +} + +static inline void nfc_llcp_free_sdp_tlv_list(struct hlist_head *sdp_head) +{ +} + #endif int __init rawsock_init(void); @@ -144,6 +156,8 @@ int nfc_genl_dep_link_down_event(struct nfc_dev *dev); int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol); int nfc_genl_tm_deactivated(struct nfc_dev *dev); +int nfc_genl_llc_send_sdres(struct nfc_dev *dev, struct hlist_head *sdres_list); + struct nfc_dev *nfc_get_device(unsigned int idx); static inline void nfc_put_device(struct nfc_dev *dev) -- cgit v1.2.3-70-g09d2 From d5702162f85526319c848c667df49ee1754dccef Mon Sep 17 00:00:00 2001 From: Christine Spang Date: Mon, 4 Mar 2013 17:02:59 -0500 Subject: ALSA: Make snd_BUG_ON() always evaluate and return the conditional expression Having snd_BUG_ON() only evaluate its conditional when CONFIG_SND_DEBUG is set leads to frequent bugs, since other similar macros in the kernel have different behavior. Let's make snd_BUG_ON() act like those macros so it will stop being accidentally misused. Signed-off-by: Christine Spang Signed-off-by: Takashi Iwai --- Documentation/DocBook/writing-an-alsa-driver.tmpl | 12 +++++------- include/sound/core.h | 24 ++++++++--------------- 2 files changed, 13 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/writing-an-alsa-driver.tmpl b/Documentation/DocBook/writing-an-alsa-driver.tmpl index bd6fee22c4d..06741e92598 100644 --- a/Documentation/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/DocBook/writing-an-alsa-driver.tmpl @@ -6164,14 +6164,12 @@ struct _snd_pcm_runtime { The macro takes an conditional expression to evaluate. - When CONFIG_SND_DEBUG, is set, the - expression is actually evaluated. If it's non-zero, it shows - the warning message such as + When CONFIG_SND_DEBUG, is set, if the + expression is non-zero, it shows the warning message such as BUG? (xxx) - normally followed by stack trace. It returns the evaluated - value. - When no CONFIG_SND_DEBUG is set, this - macro always returns zero. + normally followed by stack trace. + + In both cases it returns the evaluated value. diff --git a/include/sound/core.h b/include/sound/core.h index 7cede2d6aa8..a63680b9819 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -379,18 +379,10 @@ void __snd_printk(unsigned int level, const char *file, int line, * snd_BUG_ON - debugging check macro * @cond: condition to evaluate * - * When CONFIG_SND_DEBUG is set, this macro evaluates the given condition, - * and call WARN() and returns the value if it's non-zero. - * - * When CONFIG_SND_DEBUG is not set, this just returns zero, and the given - * condition is ignored. - * - * NOTE: the argument won't be evaluated at all when CONFIG_SND_DEBUG=n. - * Thus, don't put any statement that influences on the code behavior, - * such as pre/post increment, to the argument of this macro. - * If you want to evaluate and give a warning, use standard WARN_ON(). + * Has the same behavior as WARN_ON when CONFIG_SND_DEBUG is set, + * otherwise just evaluates the conditional and returns the value. */ -#define snd_BUG_ON(cond) WARN((cond), "BUG? (%s)\n", __stringify(cond)) +#define snd_BUG_ON(cond) WARN_ON((cond)) #else /* !CONFIG_SND_DEBUG */ @@ -400,11 +392,11 @@ __printf(2, 3) static inline void _snd_printd(int level, const char *format, ...) {} #define snd_BUG() do { } while (0) -static inline int __snd_bug_on(int cond) -{ - return 0; -} -#define snd_BUG_ON(cond) __snd_bug_on(0 && (cond)) /* always false */ + +#define snd_BUG_ON(condition) ({ \ + int __ret_warn_on = !!(condition); \ + unlikely(__ret_warn_on); \ +}) #endif /* CONFIG_SND_DEBUG */ -- cgit v1.2.3-70-g09d2 From 3a08a8f9f0936e182d387afd85fdc5d303381521 Mon Sep 17 00:00:00 2001 From: Raghavendra K T Date: Mon, 4 Mar 2013 23:32:07 +0530 Subject: kvm: Record the preemption status of vcpus using preempt notifiers Note that we mark as preempted only when vcpu's task state was Running during preemption. Thanks Jiannan, Avi for preemption notifier ideas. Thanks Gleb, PeterZ for their precious suggestions. Thanks Srikar for an idea on avoiding rcu lock while checking task state that improved overcommit numbers. Reviewed-by: Chegu Vinod Reviewed-by: Marcelo Tosatti Signed-off-by: Raghavendra K T Signed-off-by: Gleb Natapov --- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 9fa13ebc338..0f4941a9c9c 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -253,6 +253,7 @@ struct kvm_vcpu { bool dy_eligible; } spin_loop; #endif + bool preempted; struct kvm_vcpu_arch arch; }; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index faf05bddd13..470f2bc8205 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -244,6 +244,7 @@ int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) kvm_vcpu_set_in_spin_loop(vcpu, false); kvm_vcpu_set_dy_eligible(vcpu, false); + vcpu->preempted = false; r = kvm_arch_vcpu_init(vcpu); if (r < 0) @@ -2880,6 +2881,8 @@ struct kvm_vcpu *preempt_notifier_to_vcpu(struct preempt_notifier *pn) static void kvm_sched_in(struct preempt_notifier *pn, int cpu) { struct kvm_vcpu *vcpu = preempt_notifier_to_vcpu(pn); + if (vcpu->preempted) + vcpu->preempted = false; kvm_arch_vcpu_load(vcpu, cpu); } @@ -2889,6 +2892,8 @@ static void kvm_sched_out(struct preempt_notifier *pn, { struct kvm_vcpu *vcpu = preempt_notifier_to_vcpu(pn); + if (current->state == TASK_RUNNING) + vcpu->preempted = true; kvm_arch_vcpu_put(vcpu); } -- cgit v1.2.3-70-g09d2 From e0c25362384f4be9c755c98560cd4b1cdb2ec79c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 10 Mar 2013 21:52:53 -0500 Subject: clocksource: add empty version of clocksource_of_init Add an empty clocksource_of_init when !CLKSRC_OF. This is needed for builds where no timer has selected CLKSRC_OF. Signed-off-by: Rob Herring Cc: John Stultz Cc: Thomas Gleixner --- include/linux/clocksource.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 27cfda427dd..08ed5e19d8c 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -340,6 +340,7 @@ extern void clocksource_of_init(void); __used __section(__clksrc_of_table) \ = { .compatible = compat, .data = fn }; #else +static inline void clocksource_of_init(void) {} #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) #endif -- cgit v1.2.3-70-g09d2 From 488b366a452934141959384c7a1b52b22d6154ef Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Mon, 11 Feb 2013 14:56:29 +0200 Subject: mac80211: add driver callback for per-interface multicast filter Some devices have multicast filter capability for each individual virtual interface rather than just a global one. Add an interface specific driver callback allowing such drivers to configure this. Signed-off-by: Alexander Bondar Signed-off-by: Johannes Berg --- include/net/mac80211.h | 7 +++++++ net/mac80211/driver-ops.h | 16 ++++++++++++++++ net/mac80211/iface.c | 11 +++++++++++ net/mac80211/trace.h | 24 ++++++++++++++++++++++++ 4 files changed, 58 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8c0ca11a39c..f5db5e97042 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2259,6 +2259,9 @@ enum ieee80211_roc_type { * See the section "Frame filtering" for more information. * This callback must be implemented and can sleep. * + * @set_multicast_list: Configure the device's interface specific RX multicast + * filter. This callback is optional. This callback must be atomic. + * * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit * must be set or cleared for a given STA. Must be atomic. * @@ -2605,6 +2608,10 @@ struct ieee80211_ops { unsigned int changed_flags, unsigned int *total_flags, u64 multicast); + void (*set_multicast_list)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, bool allmulti, + struct netdev_hw_addr_list *mc_list); + int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 832acea4a5c..025b7592b79 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -241,6 +241,22 @@ static inline u64 drv_prepare_multicast(struct ieee80211_local *local, return ret; } +static inline void drv_set_multicast_list(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct netdev_hw_addr_list *mc_list) +{ + bool allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI; + + trace_drv_set_multicast_list(local, sdata, mc_list->count); + + check_sdata_in_driver(sdata); + + if (local->ops->set_multicast_list) + local->ops->set_multicast_list(&local->hw, &sdata->vif, + allmulti, mc_list); + trace_drv_return_void(local); +} + static inline void drv_configure_filter(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d85282f6440..9875e321c9e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -919,6 +919,17 @@ static void ieee80211_set_multicast_list(struct net_device *dev) atomic_dec(&local->iff_promiscs); sdata->flags ^= IEEE80211_SDATA_PROMISC; } + + /* + * TODO: If somebody needs this on AP interfaces, + * it can be enabled easily but multicast + * addresses from VLANs need to be synced. + */ + if (sdata->vif.type != NL80211_IFTYPE_MONITOR && + sdata->vif.type != NL80211_IFTYPE_AP_VLAN && + sdata->vif.type != NL80211_IFTYPE_AP) + drv_set_multicast_list(local, sdata, &dev->mc); + spin_lock_bh(&local->filter_lock); __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len); spin_unlock_bh(&local->filter_lock); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index e7db2b804e0..d97e4305cf1 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -431,6 +431,30 @@ TRACE_EVENT(drv_prepare_multicast, ) ); +TRACE_EVENT(drv_set_multicast_list, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, int mc_count), + + TP_ARGS(local, sdata, mc_count), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(bool, allmulti) + __field(int, mc_count) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI; + __entry->mc_count = mc_count; + ), + + TP_printk( + LOCAL_PR_FMT " configure mc filter, count=%d, allmulti=%d", + LOCAL_PR_ARG, __entry->mc_count, __entry->allmulti + ) +); + TRACE_EVENT(drv_configure_filter, TP_PROTO(struct ieee80211_local *local, unsigned int changed_flags, -- cgit v1.2.3-70-g09d2 From 44f507163d9e51238458ee6904b4d71fb0723723 Mon Sep 17 00:00:00 2001 From: Vijay Mohan Pandarathil Date: Mon, 11 Mar 2013 09:28:44 -0600 Subject: VFIO: Wrapper for getting reference to vfio_device - Added vfio_device_get_from_dev() as wrapper to get reference to vfio_device from struct device. - Added vfio_device_data() as a wrapper to get device_data from vfio_device. Signed-off-by: Vijay Mohan Pandarathil Signed-off-by: Alex Williamson --- drivers/vfio/vfio.c | 30 +++++++++++++++++++++++++++++- include/linux/vfio.h | 3 +++ 2 files changed, 32 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index fcc12f3e60a..21eddd9e0f2 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -392,12 +392,13 @@ static void vfio_device_release(struct kref *kref) } /* Device reference always implies a group reference */ -static void vfio_device_put(struct vfio_device *device) +void vfio_device_put(struct vfio_device *device) { struct vfio_group *group = device->group; kref_put_mutex(&device->kref, vfio_device_release, &group->device_lock); vfio_group_put(group); } +EXPORT_SYMBOL_GPL(vfio_device_put); static void vfio_device_get(struct vfio_device *device) { @@ -627,6 +628,33 @@ int vfio_add_group_dev(struct device *dev, } EXPORT_SYMBOL_GPL(vfio_add_group_dev); +/** + * Get a reference to the vfio_device for a device that is known to + * be bound to a vfio driver. The driver implicitly holds a + * vfio_device reference between vfio_add_group_dev and + * vfio_del_group_dev. We can therefore use drvdata to increment + * that reference from the struct device. This additional + * reference must be released by calling vfio_device_put. + */ +struct vfio_device *vfio_device_get_from_dev(struct device *dev) +{ + struct vfio_device *device = dev_get_drvdata(dev); + + vfio_device_get(device); + + return device; +} +EXPORT_SYMBOL_GPL(vfio_device_get_from_dev); + +/* + * Caller must hold a reference to the vfio_device + */ +void *vfio_device_data(struct vfio_device *device) +{ + return device->device_data; +} +EXPORT_SYMBOL_GPL(vfio_device_data); + /* Given a referenced group, check if it contains the device */ static bool vfio_dev_present(struct vfio_group *group, struct device *dev) { diff --git a/include/linux/vfio.h b/include/linux/vfio.h index ab9e86224c5..ac8d488e437 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -45,6 +45,9 @@ extern int vfio_add_group_dev(struct device *dev, void *device_data); extern void *vfio_del_group_dev(struct device *dev); +extern struct vfio_device *vfio_device_get_from_dev(struct device *dev); +extern void vfio_device_put(struct vfio_device *device); +extern void *vfio_device_data(struct vfio_device *device); /** * struct vfio_iommu_driver_ops - VFIO IOMMU driver callbacks -- cgit v1.2.3-70-g09d2 From dad9f8972e04cd081a028d8fb1249d746d97fc03 Mon Sep 17 00:00:00 2001 From: Vijay Mohan Pandarathil Date: Mon, 11 Mar 2013 09:31:22 -0600 Subject: VFIO-AER: Vfio-pci driver changes for supporting AER - New VFIO_SET_IRQ ioctl option to pass the eventfd that is signaled when an error occurs in the vfio_pci_device - Register pci_error_handler for the vfio_pci driver - When the device encounters an error, the error handler registered by the vfio_pci driver gets invoked by the AER infrastructure - In the error handler, signal the eventfd registered for the device. - This results in the qemu eventfd handler getting invoked and appropriate action taken for the guest. Signed-off-by: Vijay Mohan Pandarathil Signed-off-by: Alex Williamson --- drivers/vfio/pci/vfio_pci.c | 44 ++++++++++++++++++++++++- drivers/vfio/pci/vfio_pci_intrs.c | 64 +++++++++++++++++++++++++++++++++++++ drivers/vfio/pci/vfio_pci_private.h | 1 + include/uapi/linux/vfio.h | 1 + 4 files changed, 109 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 8189cb6a86a..acfcb1ae77b 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -201,7 +201,9 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) return (flags & PCI_MSIX_FLAGS_QSIZE) + 1; } - } + } else if (irq_type == VFIO_PCI_ERR_IRQ_INDEX) + if (pci_is_pcie(vdev->pdev)) + return 1; return 0; } @@ -317,6 +319,17 @@ static long vfio_pci_ioctl(void *device_data, if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS) return -EINVAL; + switch (info.index) { + case VFIO_PCI_INTX_IRQ_INDEX ... VFIO_PCI_MSIX_IRQ_INDEX: + break; + case VFIO_PCI_ERR_IRQ_INDEX: + if (pci_is_pcie(vdev->pdev)) + break; + /* pass thru to return error */ + default: + return -EINVAL; + } + info.flags = VFIO_IRQ_INFO_EVENTFD; info.count = vfio_pci_get_irq_count(vdev, info.index); @@ -551,11 +564,40 @@ static void vfio_pci_remove(struct pci_dev *pdev) kfree(vdev); } +static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct vfio_pci_device *vdev; + struct vfio_device *device; + + device = vfio_device_get_from_dev(&pdev->dev); + if (device == NULL) + return PCI_ERS_RESULT_DISCONNECT; + + vdev = vfio_device_data(device); + if (vdev == NULL) { + vfio_device_put(device); + return PCI_ERS_RESULT_DISCONNECT; + } + + if (vdev->err_trigger) + eventfd_signal(vdev->err_trigger, 1); + + vfio_device_put(device); + + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static struct pci_error_handlers vfio_err_handlers = { + .error_detected = vfio_pci_aer_err_detected, +}; + static struct pci_driver vfio_pci_driver = { .name = "vfio-pci", .id_table = NULL, /* only dynamic ids */ .probe = vfio_pci_probe, .remove = vfio_pci_remove, + .err_handler = &vfio_err_handlers, }; static void __exit vfio_pci_cleanup(void) diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index 3639371fa69..b84bf2210a9 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -745,6 +745,63 @@ static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev, return 0; } +static int vfio_pci_set_err_trigger(struct vfio_pci_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, void *data) +{ + int32_t fd = *(int32_t *)data; + struct pci_dev *pdev = vdev->pdev; + + if ((index != VFIO_PCI_ERR_IRQ_INDEX) || + !(flags & VFIO_IRQ_SET_DATA_TYPE_MASK)) + return -EINVAL; + + /* + * device_lock synchronizes setting and checking of + * err_trigger. The vfio_pci_aer_err_detected() is also + * called with device_lock held. + */ + + /* DATA_NONE/DATA_BOOL enables loopback testing */ + + if (flags & VFIO_IRQ_SET_DATA_NONE) { + device_lock(&pdev->dev); + if (vdev->err_trigger) + eventfd_signal(vdev->err_trigger, 1); + device_unlock(&pdev->dev); + return 0; + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t trigger = *(uint8_t *)data; + device_lock(&pdev->dev); + if (trigger && vdev->err_trigger) + eventfd_signal(vdev->err_trigger, 1); + device_unlock(&pdev->dev); + return 0; + } + + /* Handle SET_DATA_EVENTFD */ + + if (fd == -1) { + device_lock(&pdev->dev); + if (vdev->err_trigger) + eventfd_ctx_put(vdev->err_trigger); + vdev->err_trigger = NULL; + device_unlock(&pdev->dev); + return 0; + } else if (fd >= 0) { + struct eventfd_ctx *efdctx; + efdctx = eventfd_ctx_fdget(fd); + if (IS_ERR(efdctx)) + return PTR_ERR(efdctx); + device_lock(&pdev->dev); + if (vdev->err_trigger) + eventfd_ctx_put(vdev->err_trigger); + vdev->err_trigger = efdctx; + device_unlock(&pdev->dev); + return 0; + } else + return -EINVAL; +} int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, unsigned index, unsigned start, unsigned count, void *data) @@ -779,6 +836,13 @@ int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, break; } break; + case VFIO_PCI_ERR_IRQ_INDEX: + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_TRIGGER: + if (pci_is_pcie(vdev->pdev)) + func = vfio_pci_set_err_trigger; + break; + } } if (!func) diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index d7e55d03f49..9c6d5d0f3b0 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -56,6 +56,7 @@ struct vfio_pci_device { bool has_vga; struct pci_saved_state *pci_saved_state; atomic_t refcnt; + struct eventfd_ctx *err_trigger; }; #define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX) diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 4f41f309911..284ff243682 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -319,6 +319,7 @@ enum { VFIO_PCI_INTX_IRQ_INDEX, VFIO_PCI_MSI_IRQ_INDEX, VFIO_PCI_MSIX_IRQ_INDEX, + VFIO_PCI_ERR_IRQ_INDEX, VFIO_PCI_NUM_IRQS }; -- cgit v1.2.3-70-g09d2 From 8c2809144a372284f757bd4d70b08206e20681b7 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 8 Mar 2013 09:20:21 +0000 Subject: ACPICA: Update RASF table definition Update to reflect final ACPI 5.0 changes. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl3.h | 51 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index 332b17e3bec..9f27890d33a 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -505,26 +505,59 @@ struct acpi_rasf_shared_memory { u32 signature; u16 command; u16 status; - u64 requested_address; - u64 requested_length; - u64 actual_address; - u64 actual_length; + u16 version; + u8 capabilities[16]; + u8 set_capabilities[16]; + u16 num_parameter_blocks; + u32 set_capabilities_status; +}; + +/* RASF Parameter Block Structure Header */ + +struct acpi_rasf_parameter_block { + u16 type; + u16 version; + u16 length; +}; + +/* RASF Parameter Block Structure for PATROL_SCRUB */ + +struct acpi_rasf_patrol_scrub_parameter { + struct acpi_rasf_parameter_block header; + u16 patrol_scrub_command; + u64 requested_address_range[2]; + u64 actual_address_range[2]; u16 flags; - u8 speed; + u8 requested_speed; }; /* Masks for Flags and Speed fields above */ #define ACPI_RASF_SCRUBBER_RUNNING 1 #define ACPI_RASF_SPEED (7<<1) +#define ACPI_RASF_SPEED_SLOW (0<<1) +#define ACPI_RASF_SPEED_MEDIUM (4<<1) +#define ACPI_RASF_SPEED_FAST (7<<1) /* Channel Commands */ enum acpi_rasf_commands { - ACPI_RASF_GET_RAS_CAPABILITIES = 1, - ACPI_RASF_GET_PATROL_PARAMETERS = 2, - ACPI_RASF_START_PATROL_SCRUBBER = 3, - ACPI_RASF_STOP_PATROL_SCRUBBER = 4 + ACPI_RASF_EXECUTE_RASF_COMMAND = 1 +}; + +/* Platform RAS Capabilities */ + +enum acpi_rasf_capabiliities { + ACPI_HW_PATROL_SCRUB_SUPPORTED = 0, + ACPI_SW_PATROL_SCRUB_EXPOSED = 1 +}; + +/* Patrol Scrub Commands */ + +enum acpi_rasf_patrol_scrub_commands { + ACPI_RASF_GET_PATROL_PARAMETERS = 1, + ACPI_RASF_START_PATROL_SCRUBBER = 2, + ACPI_RASF_STOP_PATROL_SCRUBBER = 3 }; /* Channel Command flags */ -- cgit v1.2.3-70-g09d2 From 25c0330aa6bba60e36ac460d12ef954332852426 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 8 Mar 2013 09:20:32 +0000 Subject: ACPICA: iASL/Disassembler: Add support for VRTC table VRTC is used in Intel MID platforms as a replacement of the traditional x86 RTC. VRTC table can be found in the recent ACPI BIOS enabled Intel MID platforms. The format of this table has been defined in the "Simple Firmware Interface Specification" except it uses GAS instead of 64-bit values for address fields. This patch introduces VRTC table support into ACPICA. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl2.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'include') diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 77dc7a4099a..3f1b0a474ae 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -77,6 +77,7 @@ #define ACPI_SIG_SPMI "SPMI" /* Server Platform Management Interface table */ #define ACPI_SIG_TCPA "TCPA" /* Trusted Computing Platform Alliance table */ #define ACPI_SIG_UEFI "UEFI" /* Uefi Boot Optimization Table */ +#define ACPI_SIG_VRTC "VRTC" /* Virtual Real Time Clock Table */ #define ACPI_SIG_WAET "WAET" /* Windows ACPI Emulated devices Table */ #define ACPI_SIG_WDAT "WDAT" /* Watchdog Action Table */ #define ACPI_SIG_WDDT "WDDT" /* Watchdog Timer Description Table */ @@ -1023,6 +1024,28 @@ struct acpi_table_uefi { u16 data_offset; /* Offset of remaining data in table */ }; +/******************************************************************************* + * + * VRTC - Virtual Real Time Clock Table + * Version 1 + * + * Conforms to "Simple Firmware Interface Specification", + * Draft 0.8.2, Oct 19, 2010 + * NOTE: The ACPI VRTC is equivalent to The SFI MRTC table. + * + ******************************************************************************/ + +struct acpi_table_vrtc { + struct acpi_table_header header; /* Common ACPI table header */ +}; + +/* VRTC entry */ + +struct acpi_vrtc_entry { + struct acpi_generic_address physical_address; + u32 irq; +}; + /******************************************************************************* * * WAET - Windows ACPI Emulated devices Table -- cgit v1.2.3-70-g09d2 From 98b5c9934ccdf6c04413e0d03c1ddeb32592d8c6 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 8 Mar 2013 09:20:45 +0000 Subject: ACPICA: iASL/Disassembler: Add support for MTMR table MTMR table is used in the recent ACPI BIOS enabled Intel MID platforms. The format of this table has been defined in the "Simple Firmware Interface Specification" except it uses GAS instead of 64-bit values for address fields. This patch introduces MTMR table support into ACPICA. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl2.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'include') diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 3f1b0a474ae..ffaac0e7e0c 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -72,6 +72,7 @@ #define ACPI_SIG_IVRS "IVRS" /* I/O Virtualization Reporting Structure */ #define ACPI_SIG_MCFG "MCFG" /* PCI Memory Mapped Configuration table */ #define ACPI_SIG_MCHI "MCHI" /* Management Controller Host Interface table */ +#define ACPI_SIG_MTMR "MTMR" /* MID Timer table */ #define ACPI_SIG_SLIC "SLIC" /* Software Licensing Description Table */ #define ACPI_SIG_SPCR "SPCR" /* Serial Port Console Redirection table */ #define ACPI_SIG_SPMI "SPMI" /* Server Platform Management Interface table */ @@ -851,6 +852,29 @@ struct acpi_table_mchi { u8 pci_function; }; +/******************************************************************************* + * + * MTMR - MID Timer Table + * Version 1 + * + * Conforms to "Simple Firmware Interface Specification", + * Draft 0.8.2, Oct 19, 2010 + * NOTE: The ACPI MTMR is equivalent to the SFI MTMR table. + * + ******************************************************************************/ + +struct acpi_table_mtmr { + struct acpi_table_header header; /* Common ACPI table header */ +}; + +/* MTMR entry */ + +struct acpi_mtmr_entry { + struct acpi_generic_address physical_address; + u32 frequency; + u32 irq; +}; + /******************************************************************************* * * SLIC - Software Licensing Description Table -- cgit v1.2.3-70-g09d2 From 3cf24497f45d61ed3c3290b5b03f3baeb8401f04 Mon Sep 17 00:00:00 2001 From: Jung-uk Kim Date: Fri, 8 Mar 2013 09:21:02 +0000 Subject: ACPICA: Fix a long-standing bug in local cache Since 20060317, the pointer to next object is the first element in its common header. Remove bogus LinkOffset from ACPI_MEMORY_LIST and directly use NextObject. Signed-off-by: Jung-uk Kim Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utcache.c | 20 +++++++------------- include/acpi/actypes.h | 1 - 2 files changed, 7 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/utcache.c b/drivers/acpi/acpica/utcache.c index e0e8579deaa..2de22fbacf4 100644 --- a/drivers/acpi/acpica/utcache.c +++ b/drivers/acpi/acpica/utcache.c @@ -85,7 +85,6 @@ acpi_os_create_cache(char *cache_name, /* Populate the cache object and return it */ ACPI_MEMSET(cache, 0, sizeof(struct acpi_memory_list)); - cache->link_offset = 8; cache->list_name = cache_name; cache->object_size = object_size; cache->max_depth = max_depth; @@ -108,7 +107,7 @@ acpi_os_create_cache(char *cache_name, acpi_status acpi_os_purge_cache(struct acpi_memory_list * cache) { - char *next; + void *next; acpi_status status; ACPI_FUNCTION_ENTRY(); @@ -128,10 +127,9 @@ acpi_status acpi_os_purge_cache(struct acpi_memory_list * cache) /* Delete and unlink one cached state object */ - next = *(ACPI_CAST_INDIRECT_PTR(char, - &(((char *)cache-> - list_head)[cache-> - link_offset]))); + next = + ((struct acpi_object_common *)cache->list_head)-> + next_object; ACPI_FREE(cache->list_head); cache->list_head = next; @@ -221,9 +219,7 @@ acpi_os_release_object(struct acpi_memory_list * cache, void *object) /* Put the object at the head of the cache list */ - *(ACPI_CAST_INDIRECT_PTR(char, - &(((char *)object)[cache-> - link_offset]))) = + ((struct acpi_object_common *)object)->next_object = cache->list_head; cache->list_head = object; cache->current_depth++; @@ -272,10 +268,8 @@ void *acpi_os_acquire_object(struct acpi_memory_list *cache) /* There is an object available, use it */ object = cache->list_head; - cache->list_head = *(ACPI_CAST_INDIRECT_PTR(char, - &(((char *) - object)[cache-> - link_offset]))); + cache->list_head = + ((struct acpi_object_common *)object)->next_object; cache->current_depth--; diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 845e75f1ffd..3fac1be2d8b 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -1128,7 +1128,6 @@ struct acpi_memory_list { u16 object_size; u16 max_depth; u16 current_depth; - u16 link_offset; #ifdef ACPI_DBG_TRACK_ALLOCATIONS -- cgit v1.2.3-70-g09d2 From fd1af7126fb62688cfcf4b563c73b2909ac30f74 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 8 Mar 2013 09:22:23 +0000 Subject: ACPICA: Regression fix: reinstate safe exit macros Removal caused a regression on at least FreeBSD. This fix reinstates the macros. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/dsutils.c | 10 ++++---- drivers/acpi/acpica/evgpe.c | 6 ++--- drivers/acpi/acpica/evsci.c | 4 ++-- drivers/acpi/acpica/exprep.c | 4 ++-- drivers/acpi/acpica/exutils.c | 4 ++-- drivers/acpi/acpica/hwacpi.c | 10 ++++---- drivers/acpi/acpica/nsutils.c | 8 +++---- drivers/acpi/acpica/psargs.c | 2 +- drivers/acpi/acpica/utaddress.c | 4 ++-- include/acpi/acoutput.h | 53 +++++++++++++++++++++++++++++++---------- 10 files changed, 67 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c index 4d8c992a51d..99778997c35 100644 --- a/drivers/acpi/acpica/dsutils.c +++ b/drivers/acpi/acpica/dsutils.c @@ -178,7 +178,7 @@ acpi_ds_is_result_used(union acpi_parse_object * op, if (!op) { ACPI_ERROR((AE_INFO, "Null Op")); - return_VALUE(TRUE); + return_UINT8(TRUE); } /* @@ -210,7 +210,7 @@ acpi_ds_is_result_used(union acpi_parse_object * op, "At Method level, result of [%s] not used\n", acpi_ps_get_opcode_name(op->common. aml_opcode))); - return_VALUE(FALSE); + return_UINT8(FALSE); } /* Get info on the parent. The root_op is AML_SCOPE */ @@ -219,7 +219,7 @@ acpi_ds_is_result_used(union acpi_parse_object * op, acpi_ps_get_opcode_info(op->common.parent->common.aml_opcode); if (parent_info->class == AML_CLASS_UNKNOWN) { ACPI_ERROR((AE_INFO, "Unknown parent opcode Op=%p", op)); - return_VALUE(FALSE); + return_UINT8(FALSE); } /* @@ -307,7 +307,7 @@ acpi_ds_is_result_used(union acpi_parse_object * op, acpi_ps_get_opcode_name(op->common.parent->common. aml_opcode), op)); - return_VALUE(TRUE); + return_UINT8(TRUE); result_not_used: ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, @@ -316,7 +316,7 @@ acpi_ds_is_result_used(union acpi_parse_object * op, acpi_ps_get_opcode_name(op->common.parent->common. aml_opcode), op)); - return_VALUE(FALSE); + return_UINT8(FALSE); } /******************************************************************************* diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index b9adb9a7ed8..a493b528f8f 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -707,7 +707,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Unable to clear GPE%02X", gpe_number)); - return_VALUE(ACPI_INTERRUPT_NOT_HANDLED); + return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); } } @@ -724,7 +724,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Unable to disable GPE%02X", gpe_number)); - return_VALUE(ACPI_INTERRUPT_NOT_HANDLED); + return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); } /* @@ -784,7 +784,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, break; } - return_VALUE(ACPI_INTERRUPT_HANDLED); + return_UINT32(ACPI_INTERRUPT_HANDLED); } #endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/drivers/acpi/acpica/evsci.c b/drivers/acpi/acpica/evsci.c index f4b43bede01..b905acf7aac 100644 --- a/drivers/acpi/acpica/evsci.c +++ b/drivers/acpi/acpica/evsci.c @@ -89,7 +89,7 @@ static u32 ACPI_SYSTEM_XFACE acpi_ev_sci_xrupt_handler(void *context) */ interrupt_handled |= acpi_ev_gpe_detect(gpe_xrupt_list); - return_VALUE(interrupt_handled); + return_UINT32(interrupt_handled); } /******************************************************************************* @@ -120,7 +120,7 @@ u32 ACPI_SYSTEM_XFACE acpi_ev_gpe_xrupt_handler(void *context) interrupt_handled |= acpi_ev_gpe_detect(gpe_xrupt_list); - return_VALUE(interrupt_handled); + return_UINT32(interrupt_handled); } /****************************************************************************** diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c index d6eab81f54f..6b728aef2dc 100644 --- a/drivers/acpi/acpica/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -276,7 +276,7 @@ acpi_ex_decode_field_access(union acpi_operand_object *obj_desc, /* Invalid field access type */ ACPI_ERROR((AE_INFO, "Unknown field access type 0x%X", access)); - return_VALUE(0); + return_UINT32(0); } if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) { @@ -289,7 +289,7 @@ acpi_ex_decode_field_access(union acpi_operand_object *obj_desc, } *return_byte_alignment = byte_alignment; - return_VALUE(bit_length); + return_UINT32(bit_length); } /******************************************************************************* diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c index b205cbb4b50..99dc7b287d5 100644 --- a/drivers/acpi/acpica/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -340,7 +340,7 @@ static u32 acpi_ex_digits_needed(u64 value, u32 base) /* u64 is unsigned, so we don't worry about a '-' prefix */ if (value == 0) { - return_VALUE(1); + return_UINT32(1); } current_value = value; @@ -354,7 +354,7 @@ static u32 acpi_ex_digits_needed(u64 value, u32 base) num_digits++; } - return_VALUE(num_digits); + return_UINT32(num_digits); } /******************************************************************************* diff --git a/drivers/acpi/acpica/hwacpi.c b/drivers/acpi/acpica/hwacpi.c index 9b02a9f5b04..579c3a53ac8 100644 --- a/drivers/acpi/acpica/hwacpi.c +++ b/drivers/acpi/acpica/hwacpi.c @@ -155,7 +155,7 @@ u32 acpi_hw_get_mode(void) /* If the Hardware Reduced flag is set, machine is always in acpi mode */ if (acpi_gbl_reduced_hardware) { - return_VALUE(ACPI_SYS_MODE_ACPI); + return_UINT32(ACPI_SYS_MODE_ACPI); } /* @@ -163,18 +163,18 @@ u32 acpi_hw_get_mode(void) * system does not support mode transition. */ if (!acpi_gbl_FADT.smi_command) { - return_VALUE(ACPI_SYS_MODE_ACPI); + return_UINT32(ACPI_SYS_MODE_ACPI); } status = acpi_read_bit_register(ACPI_BITREG_SCI_ENABLE, &value); if (ACPI_FAILURE(status)) { - return_VALUE(ACPI_SYS_MODE_LEGACY); + return_UINT32(ACPI_SYS_MODE_LEGACY); } if (value) { - return_VALUE(ACPI_SYS_MODE_ACPI); + return_UINT32(ACPI_SYS_MODE_ACPI); } else { - return_VALUE(ACPI_SYS_MODE_LEGACY); + return_UINT32(ACPI_SYS_MODE_LEGACY); } } diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index 686420df684..2808586fad3 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -112,10 +112,10 @@ acpi_object_type acpi_ns_get_type(struct acpi_namespace_node * node) if (!node) { ACPI_WARNING((AE_INFO, "Null Node parameter")); - return_VALUE(ACPI_TYPE_ANY); + return_UINT8(ACPI_TYPE_ANY); } - return_VALUE(node->type); + return_UINT8(node->type); } /******************************************************************************* @@ -140,10 +140,10 @@ u32 acpi_ns_local(acpi_object_type type) /* Type code out of range */ ACPI_WARNING((AE_INFO, "Invalid Object Type 0x%X", type)); - return_VALUE(ACPI_NS_NORMAL); + return_UINT32(ACPI_NS_NORMAL); } - return_VALUE(acpi_gbl_ns_properties[type] & ACPI_NS_LOCAL); + return_UINT32(acpi_gbl_ns_properties[type] & ACPI_NS_LOCAL); } /******************************************************************************* diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c index f51308cdbc6..9f25a3d4e99 100644 --- a/drivers/acpi/acpica/psargs.c +++ b/drivers/acpi/acpica/psargs.c @@ -108,7 +108,7 @@ acpi_ps_get_next_package_length(struct acpi_parse_state *parser_state) /* Byte 0 is a special case, either bits [0:3] or [0:5] are used */ package_length |= (aml[0] & byte_zero_mask); - return_VALUE(package_length); + return_UINT32(package_length); } /******************************************************************************* diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c index 698b9d38551..e0a2e2779c2 100644 --- a/drivers/acpi/acpica/utaddress.c +++ b/drivers/acpi/acpica/utaddress.c @@ -214,7 +214,7 @@ acpi_ut_check_address_range(acpi_adr_space_type space_id, if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) && (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { - return_VALUE(0); + return_UINT32(0); } range_info = acpi_gbl_address_range_list[space_id]; @@ -256,7 +256,7 @@ acpi_ut_check_address_range(acpi_adr_space_type space_id, range_info = range_info->next; } - return_VALUE(overlap_count); + return_UINT32(overlap_count); } /******************************************************************************* diff --git a/include/acpi/acoutput.h b/include/acpi/acoutput.h index 9885276178e..4f52ea795c7 100644 --- a/include/acpi/acoutput.h +++ b/include/acpi/acoutput.h @@ -324,9 +324,9 @@ /* Helper macro */ -#define ACPI_TRACE_ENTRY(name, function, cast, param) \ +#define ACPI_TRACE_ENTRY(name, function, type, param) \ ACPI_FUNCTION_NAME (name) \ - function (ACPI_DEBUG_PARAMETERS, cast (param)) + function (ACPI_DEBUG_PARAMETERS, (type) (param)) /* The actual entry trace macros */ @@ -335,13 +335,13 @@ acpi_ut_trace (ACPI_DEBUG_PARAMETERS) #define ACPI_FUNCTION_TRACE_PTR(name, pointer) \ - ACPI_TRACE_ENTRY (name, acpi_ut_trace_ptr, (void *), pointer) + ACPI_TRACE_ENTRY (name, acpi_ut_trace_ptr, void *, pointer) #define ACPI_FUNCTION_TRACE_U32(name, value) \ - ACPI_TRACE_ENTRY (name, acpi_ut_trace_u32, (u32), value) + ACPI_TRACE_ENTRY (name, acpi_ut_trace_u32, u32, value) #define ACPI_FUNCTION_TRACE_STR(name, string) \ - ACPI_TRACE_ENTRY (name, acpi_ut_trace_str, (char *), string) + ACPI_TRACE_ENTRY (name, acpi_ut_trace_str, char *, string) #define ACPI_FUNCTION_ENTRY() \ acpi_ut_track_stack_ptr() @@ -355,16 +355,37 @@ * * One of the FUNCTION_TRACE macros above must be used in conjunction * with these macros so that "_AcpiFunctionName" is defined. + * + * There are two versions of most of the return macros. The default version is + * safer, since it avoids side-effects by guaranteeing that the argument will + * not be evaluated twice. + * + * A less-safe version of the macros is provided for optional use if the + * compiler uses excessive CPU stack (for example, this may happen in the + * debug case if code optimzation is disabled.) */ /* Exit trace helper macro */ -#define ACPI_TRACE_EXIT(function, cast, param) \ +#ifndef ACPI_SIMPLE_RETURN_MACROS + +#define ACPI_TRACE_EXIT(function, type, param) \ + ACPI_DO_WHILE0 ({ \ + register type _param = (type) (param); \ + function (ACPI_DEBUG_PARAMETERS, _param); \ + return (_param); \ + }) + +#else /* Use original less-safe macros */ + +#define ACPI_TRACE_EXIT(function, type, param) \ ACPI_DO_WHILE0 ({ \ - function (ACPI_DEBUG_PARAMETERS, cast (param)); \ - return ((param)); \ + function (ACPI_DEBUG_PARAMETERS, (type) (param)); \ + return (param); \ }) +#endif /* ACPI_SIMPLE_RETURN_MACROS */ + /* The actual exit macros */ #define return_VOID \ @@ -374,13 +395,19 @@ }) #define return_ACPI_STATUS(status) \ - ACPI_TRACE_EXIT (acpi_ut_status_exit, (acpi_status), status) + ACPI_TRACE_EXIT (acpi_ut_status_exit, acpi_status, status) #define return_PTR(pointer) \ - ACPI_TRACE_EXIT (acpi_ut_ptr_exit, (u8 *), pointer) + ACPI_TRACE_EXIT (acpi_ut_ptr_exit, void *, pointer) #define return_VALUE(value) \ - ACPI_TRACE_EXIT (acpi_ut_value_exit, (u64), value) + ACPI_TRACE_EXIT (acpi_ut_value_exit, u64, value) + +#define return_UINT32(value) \ + ACPI_TRACE_EXIT (acpi_ut_value_exit, u32, value) + +#define return_UINT8(value) \ + ACPI_TRACE_EXIT (acpi_ut_value_exit, u8, value) /* Conditional execution */ @@ -428,8 +455,10 @@ #define return_VOID return #define return_ACPI_STATUS(s) return(s) -#define return_VALUE(s) return(s) #define return_PTR(s) return(s) +#define return_VALUE(s) return(s) +#define return_UINT8(s) return(s) +#define return_UINT32(s) return(s) #endif /* ACPI_DEBUG_OUTPUT */ -- cgit v1.2.3-70-g09d2 From 995b9a9d44acaf9e551be6f1fe606af179b9753f Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 8 Mar 2013 09:22:31 +0000 Subject: ACPICA: Add macros to exception code definitions Simplifies the definitions of new and existing codes. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/acexcep.h | 192 ++++++++++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 89 deletions(-) (limited to 'include') diff --git a/include/acpi/acexcep.h b/include/acpi/acexcep.h index 9bf59d0e8aa..3e6b163ee15 100644 --- a/include/acpi/acexcep.h +++ b/include/acpi/acexcep.h @@ -44,8 +44,10 @@ #ifndef __ACEXCEP_H__ #define __ACEXCEP_H__ +/* This module contains all possible exception codes for acpi_status */ + /* - * Exceptions returned by external ACPI interfaces + * Exception code classes */ #define AE_CODE_ENVIRONMENTAL 0x0000 #define AE_CODE_PROGRAMMER 0x1000 @@ -55,6 +57,18 @@ #define AE_CODE_MAX 0x4000 #define AE_CODE_MASK 0xF000 +/* + * Macros to insert the exception code classes + */ +#define EXCEP_ENV(code) ((acpi_status) (code | AE_CODE_ENVIRONMENTAL)) +#define EXCEP_PGM(code) ((acpi_status) (code | AE_CODE_PROGRAMMER)) +#define EXCEP_TBL(code) ((acpi_status) (code | AE_CODE_ACPI_TABLES)) +#define EXCEP_AML(code) ((acpi_status) (code | AE_CODE_AML)) +#define EXCEP_CTL(code) ((acpi_status) (code | AE_CODE_CONTROL)) + +/* + * Success is always zero, failure is non-zero + */ #define ACPI_SUCCESS(a) (!(a)) #define ACPI_FAILURE(a) (a) @@ -64,60 +78,60 @@ /* * Environmental exceptions */ -#define AE_ERROR (acpi_status) (0x0001 | AE_CODE_ENVIRONMENTAL) -#define AE_NO_ACPI_TABLES (acpi_status) (0x0002 | AE_CODE_ENVIRONMENTAL) -#define AE_NO_NAMESPACE (acpi_status) (0x0003 | AE_CODE_ENVIRONMENTAL) -#define AE_NO_MEMORY (acpi_status) (0x0004 | AE_CODE_ENVIRONMENTAL) -#define AE_NOT_FOUND (acpi_status) (0x0005 | AE_CODE_ENVIRONMENTAL) -#define AE_NOT_EXIST (acpi_status) (0x0006 | AE_CODE_ENVIRONMENTAL) -#define AE_ALREADY_EXISTS (acpi_status) (0x0007 | AE_CODE_ENVIRONMENTAL) -#define AE_TYPE (acpi_status) (0x0008 | AE_CODE_ENVIRONMENTAL) -#define AE_NULL_OBJECT (acpi_status) (0x0009 | AE_CODE_ENVIRONMENTAL) -#define AE_NULL_ENTRY (acpi_status) (0x000A | AE_CODE_ENVIRONMENTAL) -#define AE_BUFFER_OVERFLOW (acpi_status) (0x000B | AE_CODE_ENVIRONMENTAL) -#define AE_STACK_OVERFLOW (acpi_status) (0x000C | AE_CODE_ENVIRONMENTAL) -#define AE_STACK_UNDERFLOW (acpi_status) (0x000D | AE_CODE_ENVIRONMENTAL) -#define AE_NOT_IMPLEMENTED (acpi_status) (0x000E | AE_CODE_ENVIRONMENTAL) -#define AE_SUPPORT (acpi_status) (0x000F | AE_CODE_ENVIRONMENTAL) -#define AE_LIMIT (acpi_status) (0x0010 | AE_CODE_ENVIRONMENTAL) -#define AE_TIME (acpi_status) (0x0011 | AE_CODE_ENVIRONMENTAL) -#define AE_ACQUIRE_DEADLOCK (acpi_status) (0x0012 | AE_CODE_ENVIRONMENTAL) -#define AE_RELEASE_DEADLOCK (acpi_status) (0x0013 | AE_CODE_ENVIRONMENTAL) -#define AE_NOT_ACQUIRED (acpi_status) (0x0014 | AE_CODE_ENVIRONMENTAL) -#define AE_ALREADY_ACQUIRED (acpi_status) (0x0015 | AE_CODE_ENVIRONMENTAL) -#define AE_NO_HARDWARE_RESPONSE (acpi_status) (0x0016 | AE_CODE_ENVIRONMENTAL) -#define AE_NO_GLOBAL_LOCK (acpi_status) (0x0017 | AE_CODE_ENVIRONMENTAL) -#define AE_ABORT_METHOD (acpi_status) (0x0018 | AE_CODE_ENVIRONMENTAL) -#define AE_SAME_HANDLER (acpi_status) (0x0019 | AE_CODE_ENVIRONMENTAL) -#define AE_NO_HANDLER (acpi_status) (0x001A | AE_CODE_ENVIRONMENTAL) -#define AE_OWNER_ID_LIMIT (acpi_status) (0x001B | AE_CODE_ENVIRONMENTAL) -#define AE_NOT_CONFIGURED (acpi_status) (0x001C | AE_CODE_ENVIRONMENTAL) +#define AE_ERROR EXCEP_ENV (0x0001) +#define AE_NO_ACPI_TABLES EXCEP_ENV (0x0002) +#define AE_NO_NAMESPACE EXCEP_ENV (0x0003) +#define AE_NO_MEMORY EXCEP_ENV (0x0004) +#define AE_NOT_FOUND EXCEP_ENV (0x0005) +#define AE_NOT_EXIST EXCEP_ENV (0x0006) +#define AE_ALREADY_EXISTS EXCEP_ENV (0x0007) +#define AE_TYPE EXCEP_ENV (0x0008) +#define AE_NULL_OBJECT EXCEP_ENV (0x0009) +#define AE_NULL_ENTRY EXCEP_ENV (0x000A) +#define AE_BUFFER_OVERFLOW EXCEP_ENV (0x000B) +#define AE_STACK_OVERFLOW EXCEP_ENV (0x000C) +#define AE_STACK_UNDERFLOW EXCEP_ENV (0x000D) +#define AE_NOT_IMPLEMENTED EXCEP_ENV (0x000E) +#define AE_SUPPORT EXCEP_ENV (0x000F) +#define AE_LIMIT EXCEP_ENV (0x0010) +#define AE_TIME EXCEP_ENV (0x0011) +#define AE_ACQUIRE_DEADLOCK EXCEP_ENV (0x0012) +#define AE_RELEASE_DEADLOCK EXCEP_ENV (0x0013) +#define AE_NOT_ACQUIRED EXCEP_ENV (0x0014) +#define AE_ALREADY_ACQUIRED EXCEP_ENV (0x0015) +#define AE_NO_HARDWARE_RESPONSE EXCEP_ENV (0x0016) +#define AE_NO_GLOBAL_LOCK EXCEP_ENV (0x0017) +#define AE_ABORT_METHOD EXCEP_ENV (0x0018) +#define AE_SAME_HANDLER EXCEP_ENV (0x0019) +#define AE_NO_HANDLER EXCEP_ENV (0x001A) +#define AE_OWNER_ID_LIMIT EXCEP_ENV (0x001B) +#define AE_NOT_CONFIGURED EXCEP_ENV (0x001C) #define AE_CODE_ENV_MAX 0x001C /* * Programmer exceptions */ -#define AE_BAD_PARAMETER (acpi_status) (0x0001 | AE_CODE_PROGRAMMER) -#define AE_BAD_CHARACTER (acpi_status) (0x0002 | AE_CODE_PROGRAMMER) -#define AE_BAD_PATHNAME (acpi_status) (0x0003 | AE_CODE_PROGRAMMER) -#define AE_BAD_DATA (acpi_status) (0x0004 | AE_CODE_PROGRAMMER) -#define AE_BAD_HEX_CONSTANT (acpi_status) (0x0005 | AE_CODE_PROGRAMMER) -#define AE_BAD_OCTAL_CONSTANT (acpi_status) (0x0006 | AE_CODE_PROGRAMMER) -#define AE_BAD_DECIMAL_CONSTANT (acpi_status) (0x0007 | AE_CODE_PROGRAMMER) -#define AE_MISSING_ARGUMENTS (acpi_status) (0x0008 | AE_CODE_PROGRAMMER) -#define AE_BAD_ADDRESS (acpi_status) (0x0009 | AE_CODE_PROGRAMMER) +#define AE_BAD_PARAMETER EXCEP_PGM (0x0001) +#define AE_BAD_CHARACTER EXCEP_PGM (0x0002) +#define AE_BAD_PATHNAME EXCEP_PGM (0x0003) +#define AE_BAD_DATA EXCEP_PGM (0x0004) +#define AE_BAD_HEX_CONSTANT EXCEP_PGM (0x0005) +#define AE_BAD_OCTAL_CONSTANT EXCEP_PGM (0x0006) +#define AE_BAD_DECIMAL_CONSTANT EXCEP_PGM (0x0007) +#define AE_MISSING_ARGUMENTS EXCEP_PGM (0x0008) +#define AE_BAD_ADDRESS EXCEP_PGM (0x0009) #define AE_CODE_PGM_MAX 0x0009 /* * Acpi table exceptions */ -#define AE_BAD_SIGNATURE (acpi_status) (0x0001 | AE_CODE_ACPI_TABLES) -#define AE_BAD_HEADER (acpi_status) (0x0002 | AE_CODE_ACPI_TABLES) -#define AE_BAD_CHECKSUM (acpi_status) (0x0003 | AE_CODE_ACPI_TABLES) -#define AE_BAD_VALUE (acpi_status) (0x0004 | AE_CODE_ACPI_TABLES) -#define AE_INVALID_TABLE_LENGTH (acpi_status) (0x0005 | AE_CODE_ACPI_TABLES) +#define AE_BAD_SIGNATURE EXCEP_TBL (0x0001) +#define AE_BAD_HEADER EXCEP_TBL (0x0002) +#define AE_BAD_CHECKSUM EXCEP_TBL (0x0003) +#define AE_BAD_VALUE EXCEP_TBL (0x0004) +#define AE_INVALID_TABLE_LENGTH EXCEP_TBL (0x0005) #define AE_CODE_TBL_MAX 0x0005 @@ -125,58 +139,58 @@ * AML exceptions. These are caused by problems with * the actual AML byte stream */ -#define AE_AML_BAD_OPCODE (acpi_status) (0x0001 | AE_CODE_AML) -#define AE_AML_NO_OPERAND (acpi_status) (0x0002 | AE_CODE_AML) -#define AE_AML_OPERAND_TYPE (acpi_status) (0x0003 | AE_CODE_AML) -#define AE_AML_OPERAND_VALUE (acpi_status) (0x0004 | AE_CODE_AML) -#define AE_AML_UNINITIALIZED_LOCAL (acpi_status) (0x0005 | AE_CODE_AML) -#define AE_AML_UNINITIALIZED_ARG (acpi_status) (0x0006 | AE_CODE_AML) -#define AE_AML_UNINITIALIZED_ELEMENT (acpi_status) (0x0007 | AE_CODE_AML) -#define AE_AML_NUMERIC_OVERFLOW (acpi_status) (0x0008 | AE_CODE_AML) -#define AE_AML_REGION_LIMIT (acpi_status) (0x0009 | AE_CODE_AML) -#define AE_AML_BUFFER_LIMIT (acpi_status) (0x000A | AE_CODE_AML) -#define AE_AML_PACKAGE_LIMIT (acpi_status) (0x000B | AE_CODE_AML) -#define AE_AML_DIVIDE_BY_ZERO (acpi_status) (0x000C | AE_CODE_AML) -#define AE_AML_BAD_NAME (acpi_status) (0x000D | AE_CODE_AML) -#define AE_AML_NAME_NOT_FOUND (acpi_status) (0x000E | AE_CODE_AML) -#define AE_AML_INTERNAL (acpi_status) (0x000F | AE_CODE_AML) -#define AE_AML_INVALID_SPACE_ID (acpi_status) (0x0010 | AE_CODE_AML) -#define AE_AML_STRING_LIMIT (acpi_status) (0x0011 | AE_CODE_AML) -#define AE_AML_NO_RETURN_VALUE (acpi_status) (0x0012 | AE_CODE_AML) -#define AE_AML_METHOD_LIMIT (acpi_status) (0x0013 | AE_CODE_AML) -#define AE_AML_NOT_OWNER (acpi_status) (0x0014 | AE_CODE_AML) -#define AE_AML_MUTEX_ORDER (acpi_status) (0x0015 | AE_CODE_AML) -#define AE_AML_MUTEX_NOT_ACQUIRED (acpi_status) (0x0016 | AE_CODE_AML) -#define AE_AML_INVALID_RESOURCE_TYPE (acpi_status) (0x0017 | AE_CODE_AML) -#define AE_AML_INVALID_INDEX (acpi_status) (0x0018 | AE_CODE_AML) -#define AE_AML_REGISTER_LIMIT (acpi_status) (0x0019 | AE_CODE_AML) -#define AE_AML_NO_WHILE (acpi_status) (0x001A | AE_CODE_AML) -#define AE_AML_ALIGNMENT (acpi_status) (0x001B | AE_CODE_AML) -#define AE_AML_NO_RESOURCE_END_TAG (acpi_status) (0x001C | AE_CODE_AML) -#define AE_AML_BAD_RESOURCE_VALUE (acpi_status) (0x001D | AE_CODE_AML) -#define AE_AML_CIRCULAR_REFERENCE (acpi_status) (0x001E | AE_CODE_AML) -#define AE_AML_BAD_RESOURCE_LENGTH (acpi_status) (0x001F | AE_CODE_AML) -#define AE_AML_ILLEGAL_ADDRESS (acpi_status) (0x0020 | AE_CODE_AML) -#define AE_AML_INFINITE_LOOP (acpi_status) (0x0021 | AE_CODE_AML) +#define AE_AML_BAD_OPCODE EXCEP_AML (0x0001) +#define AE_AML_NO_OPERAND EXCEP_AML (0x0002) +#define AE_AML_OPERAND_TYPE EXCEP_AML (0x0003) +#define AE_AML_OPERAND_VALUE EXCEP_AML (0x0004) +#define AE_AML_UNINITIALIZED_LOCAL EXCEP_AML (0x0005) +#define AE_AML_UNINITIALIZED_ARG EXCEP_AML (0x0006) +#define AE_AML_UNINITIALIZED_ELEMENT EXCEP_AML (0x0007) +#define AE_AML_NUMERIC_OVERFLOW EXCEP_AML (0x0008) +#define AE_AML_REGION_LIMIT EXCEP_AML (0x0009) +#define AE_AML_BUFFER_LIMIT EXCEP_AML (0x000A) +#define AE_AML_PACKAGE_LIMIT EXCEP_AML (0x000B) +#define AE_AML_DIVIDE_BY_ZERO EXCEP_AML (0x000C) +#define AE_AML_BAD_NAME EXCEP_AML (0x000D) +#define AE_AML_NAME_NOT_FOUND EXCEP_AML (0x000E) +#define AE_AML_INTERNAL EXCEP_AML (0x000F) +#define AE_AML_INVALID_SPACE_ID EXCEP_AML (0x0010) +#define AE_AML_STRING_LIMIT EXCEP_AML (0x0011) +#define AE_AML_NO_RETURN_VALUE EXCEP_AML (0x0012) +#define AE_AML_METHOD_LIMIT EXCEP_AML (0x0013) +#define AE_AML_NOT_OWNER EXCEP_AML (0x0014) +#define AE_AML_MUTEX_ORDER EXCEP_AML (0x0015) +#define AE_AML_MUTEX_NOT_ACQUIRED EXCEP_AML (0x0016) +#define AE_AML_INVALID_RESOURCE_TYPE EXCEP_AML (0x0017) +#define AE_AML_INVALID_INDEX EXCEP_AML (0x0018) +#define AE_AML_REGISTER_LIMIT EXCEP_AML (0x0019) +#define AE_AML_NO_WHILE EXCEP_AML (0x001A) +#define AE_AML_ALIGNMENT EXCEP_AML (0x001B) +#define AE_AML_NO_RESOURCE_END_TAG EXCEP_AML (0x001C) +#define AE_AML_BAD_RESOURCE_VALUE EXCEP_AML (0x001D) +#define AE_AML_CIRCULAR_REFERENCE EXCEP_AML (0x001E) +#define AE_AML_BAD_RESOURCE_LENGTH EXCEP_AML (0x001F) +#define AE_AML_ILLEGAL_ADDRESS EXCEP_AML (0x0020) +#define AE_AML_INFINITE_LOOP EXCEP_AML (0x0021) #define AE_CODE_AML_MAX 0x0021 /* * Internal exceptions used for control */ -#define AE_CTRL_RETURN_VALUE (acpi_status) (0x0001 | AE_CODE_CONTROL) -#define AE_CTRL_PENDING (acpi_status) (0x0002 | AE_CODE_CONTROL) -#define AE_CTRL_TERMINATE (acpi_status) (0x0003 | AE_CODE_CONTROL) -#define AE_CTRL_TRUE (acpi_status) (0x0004 | AE_CODE_CONTROL) -#define AE_CTRL_FALSE (acpi_status) (0x0005 | AE_CODE_CONTROL) -#define AE_CTRL_DEPTH (acpi_status) (0x0006 | AE_CODE_CONTROL) -#define AE_CTRL_END (acpi_status) (0x0007 | AE_CODE_CONTROL) -#define AE_CTRL_TRANSFER (acpi_status) (0x0008 | AE_CODE_CONTROL) -#define AE_CTRL_BREAK (acpi_status) (0x0009 | AE_CODE_CONTROL) -#define AE_CTRL_CONTINUE (acpi_status) (0x000A | AE_CODE_CONTROL) -#define AE_CTRL_SKIP (acpi_status) (0x000B | AE_CODE_CONTROL) -#define AE_CTRL_PARSE_CONTINUE (acpi_status) (0x000C | AE_CODE_CONTROL) -#define AE_CTRL_PARSE_PENDING (acpi_status) (0x000D | AE_CODE_CONTROL) +#define AE_CTRL_RETURN_VALUE EXCEP_CTL (0x0001) +#define AE_CTRL_PENDING EXCEP_CTL (0x0002) +#define AE_CTRL_TERMINATE EXCEP_CTL (0x0003) +#define AE_CTRL_TRUE EXCEP_CTL (0x0004) +#define AE_CTRL_FALSE EXCEP_CTL (0x0005) +#define AE_CTRL_DEPTH EXCEP_CTL (0x0006) +#define AE_CTRL_END EXCEP_CTL (0x0007) +#define AE_CTRL_TRANSFER EXCEP_CTL (0x0008) +#define AE_CTRL_BREAK EXCEP_CTL (0x0009) +#define AE_CTRL_CONTINUE EXCEP_CTL (0x000A) +#define AE_CTRL_SKIP EXCEP_CTL (0x000B) +#define AE_CTRL_PARSE_CONTINUE EXCEP_CTL (0x000C) +#define AE_CTRL_PARSE_PENDING EXCEP_CTL (0x000D) #define AE_CODE_CTRL_MAX 0x000D -- cgit v1.2.3-70-g09d2 From ae1b4769989f8707a1f092db191fa2f9a0fc8604 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 8 Mar 2013 09:22:39 +0000 Subject: ACPICA: Add exception descriptions to exception info table Descriptions to be compiled/used by the acpihelp utility only. Not compiled for the kernel ACPICA code. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acutils.h | 3 +- drivers/acpi/acpica/utexcep.c | 26 ++-- include/acpi/acexcep.h | 269 ++++++++++++++++++++++++++---------------- 3 files changed, 183 insertions(+), 115 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 0082fa0a613..c01f1a10a9d 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -483,7 +483,8 @@ acpi_ut_short_divide(u64 in_dividend, /* * utmisc */ -const char *acpi_ut_validate_exception(acpi_status status); +const struct acpi_exception_info *acpi_ut_validate_exception(acpi_status + status); u8 acpi_ut_is_pci_root_bridge(char *id); diff --git a/drivers/acpi/acpica/utexcep.c b/drivers/acpi/acpica/utexcep.c index a0ab7c02e87..b543a144941 100644 --- a/drivers/acpi/acpica/utexcep.c +++ b/drivers/acpi/acpica/utexcep.c @@ -64,7 +64,7 @@ ACPI_MODULE_NAME("utexcep") ******************************************************************************/ const char *acpi_format_exception(acpi_status status) { - const char *exception = NULL; + const struct acpi_exception_info *exception; ACPI_FUNCTION_ENTRY(); @@ -76,10 +76,10 @@ const char *acpi_format_exception(acpi_status status) ACPI_ERROR((AE_INFO, "Unknown exception code: 0x%8.8X", status)); - exception = "UNKNOWN_STATUS_CODE"; + return ("UNKNOWN_STATUS_CODE"); } - return (ACPI_CAST_PTR(const char, exception)); + return (exception->name); } ACPI_EXPORT_SYMBOL(acpi_format_exception) @@ -97,10 +97,10 @@ ACPI_EXPORT_SYMBOL(acpi_format_exception) * an ASCII string. * ******************************************************************************/ -const char *acpi_ut_validate_exception(acpi_status status) +const struct acpi_exception_info *acpi_ut_validate_exception(acpi_status status) { u32 sub_status; - const char *exception = NULL; + const struct acpi_exception_info *exception = NULL; ACPI_FUNCTION_ENTRY(); @@ -113,35 +113,35 @@ const char *acpi_ut_validate_exception(acpi_status status) case AE_CODE_ENVIRONMENTAL: if (sub_status <= AE_CODE_ENV_MAX) { - exception = acpi_gbl_exception_names_env[sub_status]; + exception = &acpi_gbl_exception_names_env[sub_status]; } break; case AE_CODE_PROGRAMMER: if (sub_status <= AE_CODE_PGM_MAX) { - exception = acpi_gbl_exception_names_pgm[sub_status]; + exception = &acpi_gbl_exception_names_pgm[sub_status]; } break; case AE_CODE_ACPI_TABLES: if (sub_status <= AE_CODE_TBL_MAX) { - exception = acpi_gbl_exception_names_tbl[sub_status]; + exception = &acpi_gbl_exception_names_tbl[sub_status]; } break; case AE_CODE_AML: if (sub_status <= AE_CODE_AML_MAX) { - exception = acpi_gbl_exception_names_aml[sub_status]; + exception = &acpi_gbl_exception_names_aml[sub_status]; } break; case AE_CODE_CONTROL: if (sub_status <= AE_CODE_CTRL_MAX) { - exception = acpi_gbl_exception_names_ctrl[sub_status]; + exception = &acpi_gbl_exception_names_ctrl[sub_status]; } break; @@ -149,5 +149,9 @@ const char *acpi_ut_validate_exception(acpi_status status) break; } - return (ACPI_CAST_PTR(const char, exception)); + if (!exception || !exception->name) { + return (NULL); + } + + return (exception); } diff --git a/include/acpi/acexcep.h b/include/acpi/acexcep.h index 3e6b163ee15..cf051e05a8f 100644 --- a/include/acpi/acexcep.h +++ b/include/acpi/acexcep.h @@ -49,11 +49,12 @@ /* * Exception code classes */ -#define AE_CODE_ENVIRONMENTAL 0x0000 -#define AE_CODE_PROGRAMMER 0x1000 -#define AE_CODE_ACPI_TABLES 0x2000 -#define AE_CODE_AML 0x3000 -#define AE_CODE_CONTROL 0x4000 +#define AE_CODE_ENVIRONMENTAL 0x0000 /* General ACPICA environment */ +#define AE_CODE_PROGRAMMER 0x1000 /* External ACPICA interface caller */ +#define AE_CODE_ACPI_TABLES 0x2000 /* ACPI tables */ +#define AE_CODE_AML 0x3000 /* From executing AML code */ +#define AE_CODE_CONTROL 0x4000 /* Internal control codes */ + #define AE_CODE_MAX 0x4000 #define AE_CODE_MASK 0xF000 @@ -66,6 +67,24 @@ #define EXCEP_AML(code) ((acpi_status) (code | AE_CODE_AML)) #define EXCEP_CTL(code) ((acpi_status) (code | AE_CODE_CONTROL)) +/* + * Exception info table. The "Description" field is used only by the + * ACPICA help application (acpihelp). + */ +struct acpi_exception_info { + char *name; + +#ifdef ACPI_HELP_APP + char *description; +#endif +}; + +#ifdef ACPI_HELP_APP +#define EXCEP_TXT(name,description) {name, description} +#else +#define EXCEP_TXT(name,description) {name} +#endif + /* * Success is always zero, failure is non-zero */ @@ -202,112 +221,156 @@ * String versions of the exception codes above * These strings must match the corresponding defines exactly */ -char const *acpi_gbl_exception_names_env[] = { - "AE_OK", - "AE_ERROR", - "AE_NO_ACPI_TABLES", - "AE_NO_NAMESPACE", - "AE_NO_MEMORY", - "AE_NOT_FOUND", - "AE_NOT_EXIST", - "AE_ALREADY_EXISTS", - "AE_TYPE", - "AE_NULL_OBJECT", - "AE_NULL_ENTRY", - "AE_BUFFER_OVERFLOW", - "AE_STACK_OVERFLOW", - "AE_STACK_UNDERFLOW", - "AE_NOT_IMPLEMENTED", - "AE_SUPPORT", - "AE_LIMIT", - "AE_TIME", - "AE_ACQUIRE_DEADLOCK", - "AE_RELEASE_DEADLOCK", - "AE_NOT_ACQUIRED", - "AE_ALREADY_ACQUIRED", - "AE_NO_HARDWARE_RESPONSE", - "AE_NO_GLOBAL_LOCK", - "AE_ABORT_METHOD", - "AE_SAME_HANDLER", - "AE_NO_HANDLER", - "AE_OWNER_ID_LIMIT", - "AE_NOT_CONFIGURED" +static const struct acpi_exception_info acpi_gbl_exception_names_env[] = { + EXCEP_TXT("AE_OK", "No error"), + EXCEP_TXT("AE_ERROR", "Unspecified error"), + EXCEP_TXT("AE_NO_ACPI_TABLES", "ACPI tables could not be found"), + EXCEP_TXT("AE_NO_NAMESPACE", "A namespace has not been loaded"), + EXCEP_TXT("AE_NO_MEMORY", "Insufficient dynamic memory"), + EXCEP_TXT("AE_NOT_FOUND", "The name was not found in the namespace"), + EXCEP_TXT("AE_NOT_EXIST", "A required entity does not exist"), + EXCEP_TXT("AE_ALREADY_EXISTS", "An entity already exists"), + EXCEP_TXT("AE_TYPE", "The object type is incorrect"), + EXCEP_TXT("AE_NULL_OBJECT", "A required object was missing"), + EXCEP_TXT("AE_NULL_ENTRY", "The requested object does not exist"), + EXCEP_TXT("AE_BUFFER_OVERFLOW", "The buffer provided is too small"), + EXCEP_TXT("AE_STACK_OVERFLOW", "An internal stack overflowed"), + EXCEP_TXT("AE_STACK_UNDERFLOW", "An internal stack underflowed"), + EXCEP_TXT("AE_NOT_IMPLEMENTED", "The feature is not implemented"), + EXCEP_TXT("AE_SUPPORT", "The feature is not supported"), + EXCEP_TXT("AE_LIMIT", "A predefined limit was exceeded"), + EXCEP_TXT("AE_TIME", "A time limit or timeout expired"), + EXCEP_TXT("AE_ACQUIRE_DEADLOCK", + "Internal error, attempt was made to acquire a mutex in improper order"), + EXCEP_TXT("AE_RELEASE_DEADLOCK", + "Internal error, attempt was made to release a mutex in improper order"), + EXCEP_TXT("AE_NOT_ACQUIRED", + "An attempt to release a mutex or Global Lock without a previous acquire"), + EXCEP_TXT("AE_ALREADY_ACQUIRED", + "Internal error, attempt was made to acquire a mutex twice"), + EXCEP_TXT("AE_NO_HARDWARE_RESPONSE", + "Hardware did not respond after an I/O operation"), + EXCEP_TXT("AE_NO_GLOBAL_LOCK", "There is no FACS Global Lock"), + EXCEP_TXT("AE_ABORT_METHOD", "A control method was aborted"), + EXCEP_TXT("AE_SAME_HANDLER", + "Attempt was made to install the same handler that is already installed"), + EXCEP_TXT("AE_NO_HANDLER", + "A handler for the operation is not installed"), + EXCEP_TXT("AE_OWNER_ID_LIMIT", + "There are no more Owner IDs available for ACPI tables or control methods"), + EXCEP_TXT("AE_NOT_CONFIGURED", + "The interface is not part of the current subsystem configuration") }; -char const *acpi_gbl_exception_names_pgm[] = { - NULL, - "AE_BAD_PARAMETER", - "AE_BAD_CHARACTER", - "AE_BAD_PATHNAME", - "AE_BAD_DATA", - "AE_BAD_HEX_CONSTANT", - "AE_BAD_OCTAL_CONSTANT", - "AE_BAD_DECIMAL_CONSTANT", - "AE_MISSING_ARGUMENTS", - "AE_BAD_ADDRESS" +static const struct acpi_exception_info acpi_gbl_exception_names_pgm[] = { + EXCEP_TXT(NULL, NULL), + EXCEP_TXT("AE_BAD_PARAMETER", "A parameter is out of range or invalid"), + EXCEP_TXT("AE_BAD_CHARACTER", + "An invalid character was found in a name"), + EXCEP_TXT("AE_BAD_PATHNAME", + "An invalid character was found in a pathname"), + EXCEP_TXT("AE_BAD_DATA", + "A package or buffer contained incorrect data"), + EXCEP_TXT("AE_BAD_HEX_CONSTANT", "Invalid character in a Hex constant"), + EXCEP_TXT("AE_BAD_OCTAL_CONSTANT", + "Invalid character in an Octal constant"), + EXCEP_TXT("AE_BAD_DECIMAL_CONSTANT", + "Invalid character in a Decimal constant"), + EXCEP_TXT("AE_MISSING_ARGUMENTS", + "Too few arguments were passed to a control method"), + EXCEP_TXT("AE_BAD_ADDRESS", "An illegal null I/O address") }; -char const *acpi_gbl_exception_names_tbl[] = { - NULL, - "AE_BAD_SIGNATURE", - "AE_BAD_HEADER", - "AE_BAD_CHECKSUM", - "AE_BAD_VALUE", - "AE_INVALID_TABLE_LENGTH" +static const struct acpi_exception_info acpi_gbl_exception_names_tbl[] = { + EXCEP_TXT(NULL, NULL), + EXCEP_TXT("AE_BAD_SIGNATURE", "An ACPI table has an invalid signature"), + EXCEP_TXT("AE_BAD_HEADER", "Invalid field in an ACPI table header"), + EXCEP_TXT("AE_BAD_CHECKSUM", "An ACPI table checksum is not correct"), + EXCEP_TXT("AE_BAD_VALUE", "An invalid value was found in a table"), + EXCEP_TXT("AE_INVALID_TABLE_LENGTH", + "The FADT or FACS has improper length") }; -char const *acpi_gbl_exception_names_aml[] = { - NULL, - "AE_AML_BAD_OPCODE", - "AE_AML_NO_OPERAND", - "AE_AML_OPERAND_TYPE", - "AE_AML_OPERAND_VALUE", - "AE_AML_UNINITIALIZED_LOCAL", - "AE_AML_UNINITIALIZED_ARG", - "AE_AML_UNINITIALIZED_ELEMENT", - "AE_AML_NUMERIC_OVERFLOW", - "AE_AML_REGION_LIMIT", - "AE_AML_BUFFER_LIMIT", - "AE_AML_PACKAGE_LIMIT", - "AE_AML_DIVIDE_BY_ZERO", - "AE_AML_BAD_NAME", - "AE_AML_NAME_NOT_FOUND", - "AE_AML_INTERNAL", - "AE_AML_INVALID_SPACE_ID", - "AE_AML_STRING_LIMIT", - "AE_AML_NO_RETURN_VALUE", - "AE_AML_METHOD_LIMIT", - "AE_AML_NOT_OWNER", - "AE_AML_MUTEX_ORDER", - "AE_AML_MUTEX_NOT_ACQUIRED", - "AE_AML_INVALID_RESOURCE_TYPE", - "AE_AML_INVALID_INDEX", - "AE_AML_REGISTER_LIMIT", - "AE_AML_NO_WHILE", - "AE_AML_ALIGNMENT", - "AE_AML_NO_RESOURCE_END_TAG", - "AE_AML_BAD_RESOURCE_VALUE", - "AE_AML_CIRCULAR_REFERENCE", - "AE_AML_BAD_RESOURCE_LENGTH", - "AE_AML_ILLEGAL_ADDRESS", - "AE_AML_INFINITE_LOOP" +static const struct acpi_exception_info acpi_gbl_exception_names_aml[] = { + EXCEP_TXT(NULL, NULL), + EXCEP_TXT("AE_AML_BAD_OPCODE", "Invalid AML opcode encountered"), + EXCEP_TXT("AE_AML_NO_OPERAND", "A required operand is missing"), + EXCEP_TXT("AE_AML_OPERAND_TYPE", + "An operand of an incorrect type was encountered"), + EXCEP_TXT("AE_AML_OPERAND_VALUE", + "The operand had an inappropriate or invalid value"), + EXCEP_TXT("AE_AML_UNINITIALIZED_LOCAL", + "Method tried to use an uninitialized local variable"), + EXCEP_TXT("AE_AML_UNINITIALIZED_ARG", + "Method tried to use an uninitialized argument"), + EXCEP_TXT("AE_AML_UNINITIALIZED_ELEMENT", + "Method tried to use an empty package element"), + EXCEP_TXT("AE_AML_NUMERIC_OVERFLOW", + "Overflow during BCD conversion or other"), + EXCEP_TXT("AE_AML_REGION_LIMIT", + "Tried to access beyond the end of an Operation Region"), + EXCEP_TXT("AE_AML_BUFFER_LIMIT", + "Tried to access beyond the end of a buffer"), + EXCEP_TXT("AE_AML_PACKAGE_LIMIT", + "Tried to access beyond the end of a package"), + EXCEP_TXT("AE_AML_DIVIDE_BY_ZERO", + "During execution of AML Divide operator"), + EXCEP_TXT("AE_AML_BAD_NAME", + "An ACPI name contains invalid character(s)"), + EXCEP_TXT("AE_AML_NAME_NOT_FOUND", + "Could not resolve a named reference"), + EXCEP_TXT("AE_AML_INTERNAL", "An internal error within the interprete"), + EXCEP_TXT("AE_AML_INVALID_SPACE_ID", + "An Operation Region SpaceID is invalid"), + EXCEP_TXT("AE_AML_STRING_LIMIT", + "String is longer than 200 characters"), + EXCEP_TXT("AE_AML_NO_RETURN_VALUE", + "A method did not return a required value"), + EXCEP_TXT("AE_AML_METHOD_LIMIT", + "A control method reached the maximum reentrancy limit of 255"), + EXCEP_TXT("AE_AML_NOT_OWNER", + "A thread tried to release a mutex that it does not own"), + EXCEP_TXT("AE_AML_MUTEX_ORDER", "Mutex SyncLevel release mismatch"), + EXCEP_TXT("AE_AML_MUTEX_NOT_ACQUIRED", + "Attempt to release a mutex that was not previously acquired"), + EXCEP_TXT("AE_AML_INVALID_RESOURCE_TYPE", + "Invalid resource type in resource list"), + EXCEP_TXT("AE_AML_INVALID_INDEX", + "Invalid Argx or Localx (x too large)"), + EXCEP_TXT("AE_AML_REGISTER_LIMIT", + "Bank value or Index value beyond range of register"), + EXCEP_TXT("AE_AML_NO_WHILE", "Break or Continue without a While"), + EXCEP_TXT("AE_AML_ALIGNMENT", + "Non-aligned memory transfer on platform that does not support this"), + EXCEP_TXT("AE_AML_NO_RESOURCE_END_TAG", + "No End Tag in a resource list"), + EXCEP_TXT("AE_AML_BAD_RESOURCE_VALUE", + "Invalid value of a resource element"), + EXCEP_TXT("AE_AML_CIRCULAR_REFERENCE", + "Two references refer to each other"), + EXCEP_TXT("AE_AML_BAD_RESOURCE_LENGTH", + "The length of a Resource Descriptor in the AML is incorrect"), + EXCEP_TXT("AE_AML_ILLEGAL_ADDRESS", + "A memory, I/O, or PCI configuration address is invalid"), + EXCEP_TXT("AE_AML_INFINITE_LOOP", + "An apparent infinite AML While loop, method was aborted") }; -char const *acpi_gbl_exception_names_ctrl[] = { - NULL, - "AE_CTRL_RETURN_VALUE", - "AE_CTRL_PENDING", - "AE_CTRL_TERMINATE", - "AE_CTRL_TRUE", - "AE_CTRL_FALSE", - "AE_CTRL_DEPTH", - "AE_CTRL_END", - "AE_CTRL_TRANSFER", - "AE_CTRL_BREAK", - "AE_CTRL_CONTINUE", - "AE_CTRL_SKIP", - "AE_CTRL_PARSE_CONTINUE", - "AE_CTRL_PARSE_PENDING" +static const struct acpi_exception_info acpi_gbl_exception_names_ctrl[] = { + EXCEP_TXT(NULL, NULL), + EXCEP_TXT("AE_CTRL_RETURN_VALUE", "A Method returned a value"), + EXCEP_TXT("AE_CTRL_PENDING", "Method is calling another method"), + EXCEP_TXT("AE_CTRL_TERMINATE", "Terminate the executing method"), + EXCEP_TXT("AE_CTRL_TRUE", "An If or While predicate result"), + EXCEP_TXT("AE_CTRL_FALSE", "An If or While predicate result"), + EXCEP_TXT("AE_CTRL_DEPTH", "Maximum search depth has been reached"), + EXCEP_TXT("AE_CTRL_END", "An If or While predicate is false"), + EXCEP_TXT("AE_CTRL_TRANSFER", "Transfer control to called method"), + EXCEP_TXT("AE_CTRL_BREAK", "A Break has been executed"), + EXCEP_TXT("AE_CTRL_CONTINUE", "A Continue has been executed"), + EXCEP_TXT("AE_CTRL_SKIP", "Not currently used"), + EXCEP_TXT("AE_CTRL_PARSE_CONTINUE", "Used to skip over bad opcodes"), + EXCEP_TXT("AE_CTRL_PARSE_PENDING", "Used to implement AML While loops") }; #endif /* EXCEPTION_TABLE */ -- cgit v1.2.3-70-g09d2 From 6be58e2f21edd7362d985e0a44060352458c0f49 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 8 Mar 2013 09:22:48 +0000 Subject: ACPICA: Remove trailing comma in enum declarations SunStudio compiler complains about trailing commas in enum declarations. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl3.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index 9f27890d33a..e2c0931a3d6 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -174,7 +174,7 @@ struct acpi_fpdt_header { enum acpi_fpdt_type { ACPI_FPDT_TYPE_BOOT = 0, - ACPI_FPDT_TYPE_S3PERF = 1, + ACPI_FPDT_TYPE_S3PERF = 1 }; /* @@ -223,7 +223,7 @@ struct acpi_s3pt_header { enum acpi_s3pt_type { ACPI_S3PT_TYPE_RESUME = 0, - ACPI_S3PT_TYPE_SUSPEND = 1, + ACPI_S3PT_TYPE_SUSPEND = 1 }; struct acpi_s3pt_resume { -- cgit v1.2.3-70-g09d2 From eccc534378e74030b31cfe10f1188df06f7f680a Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 8 Mar 2013 09:23:58 +0000 Subject: ACPICA: Update version to 20130214 Version 20130214. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 03322dddd88..7aa231bc1fc 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20130117 +#define ACPI_CA_VERSION 0x20130214 #include #include -- cgit v1.2.3-70-g09d2 From eb7c06e8e9c93b495e355421cffd3c43c266d7d2 Mon Sep 17 00:00:00 2001 From: Yacine Belkadi Date: Mon, 11 Mar 2013 22:05:14 +0100 Subject: ALSA: add/change some comments describing function return values script/kernel-doc reports the following type of warnings (when run in verbose mode): Warning(sound/core/init.c:152): No description found for return value of 'snd_card_create' To fix that: - add missing descriptions of function return values - use "Return:" sections to describe those return values Along the way: - complete some descriptions - fix some typos Signed-off-by: Yacine Belkadi Signed-off-by: Takashi Iwai --- include/sound/control.h | 5 +++- include/sound/core.h | 2 +- include/sound/pcm.h | 23 +++++++++------- sound/core/control.c | 41 ++++++++++++++++------------- sound/core/device.c | 8 +++--- sound/core/hwdep.c | 2 +- sound/core/info.c | 16 +++++------ sound/core/init.c | 16 ++++++----- sound/core/isadma.c | 2 +- sound/core/jack.c | 6 +++-- sound/core/memalloc.c | 20 +++++++------- sound/core/memory.c | 4 +-- sound/core/pcm.c | 6 ++--- sound/core/pcm_lib.c | 54 ++++++++++++++++++++++++++++++-------- sound/core/pcm_memory.c | 19 +++++++++----- sound/core/pcm_misc.c | 28 ++++++++++---------- sound/core/pcm_native.c | 13 +++++++++ sound/core/rawmidi.c | 14 +++++----- sound/core/sound.c | 7 +++-- sound/core/vmaster.c | 7 +++-- sound/drivers/mpu401/mpu401_uart.c | 6 ++++- sound/pci/ac97/ac97_codec.c | 16 ++++++----- sound/pci/ac97/ac97_pcm.c | 10 ++++++- sound/sound_core.c | 22 +++++++++++----- 24 files changed, 221 insertions(+), 126 deletions(-) (limited to 'include') diff --git a/include/sound/control.h b/include/sound/control.h index 8332e865c75..34bc93d80d5 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -189,7 +189,6 @@ int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave, * * Add a virtual slave control to the given master element created via * snd_ctl_create_virtual_master() beforehand. - * Returns zero if successful or a negative error code. * * All slaves must be the same type (returning the same information * via info callback). The function doesn't check it, so it's your @@ -199,6 +198,8 @@ int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave, * at most two channels, * logarithmic volume control (dB level) thus no linear volume, * master can only attenuate the volume without gain + * + * Return: Zero if successful or a negative error code. */ static inline int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) @@ -219,6 +220,8 @@ snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) * When the control peeks the hardware values directly and the value * can be changed by other means than the put callback of the element, * this function should be used to keep the value always up-to-date. + * + * Return: Zero if successful or a negative error code. */ static inline int snd_ctl_add_slave_uncached(struct snd_kcontrol *master, diff --git a/include/sound/core.h b/include/sound/core.h index a63680b9819..5bfe5136441 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -229,7 +229,7 @@ int snd_register_device_for_dev(int type, struct snd_card *card, * This function uses the card's device pointer to link to the * correct &struct device. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ static inline int snd_register_device(int type, struct snd_card *card, int dev, const struct file_operations *f_ops, diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 45c1981c9ca..aa7b0a8385c 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -659,7 +659,7 @@ static inline snd_pcm_sframes_t snd_pcm_capture_hw_avail(struct snd_pcm_runtime * * Checks whether enough free space is available on the playback buffer. * - * Returns non-zero if available, or zero if not. + * Return: Non-zero if available, or zero if not. */ static inline int snd_pcm_playback_ready(struct snd_pcm_substream *substream) { @@ -673,7 +673,7 @@ static inline int snd_pcm_playback_ready(struct snd_pcm_substream *substream) * * Checks whether enough capture data is available on the capture buffer. * - * Returns non-zero if available, or zero if not. + * Return: Non-zero if available, or zero if not. */ static inline int snd_pcm_capture_ready(struct snd_pcm_substream *substream) { @@ -685,10 +685,10 @@ static inline int snd_pcm_capture_ready(struct snd_pcm_substream *substream) * snd_pcm_playback_data - check whether any data exists on the playback buffer * @substream: the pcm substream instance * - * Checks whether any data exists on the playback buffer. If stop_threshold - * is bigger or equal to boundary, then this function returns always non-zero. + * Checks whether any data exists on the playback buffer. * - * Returns non-zero if exists, or zero if not. + * Return: Non-zero if any data exists, or zero if not. If stop_threshold + * is bigger or equal to boundary, then this function returns always non-zero. */ static inline int snd_pcm_playback_data(struct snd_pcm_substream *substream) { @@ -705,7 +705,7 @@ static inline int snd_pcm_playback_data(struct snd_pcm_substream *substream) * * Checks whether the playback buffer is empty. * - * Returns non-zero if empty, or zero if not. + * Return: Non-zero if empty, or zero if not. */ static inline int snd_pcm_playback_empty(struct snd_pcm_substream *substream) { @@ -719,7 +719,7 @@ static inline int snd_pcm_playback_empty(struct snd_pcm_substream *substream) * * Checks whether the capture buffer is empty. * - * Returns non-zero if empty, or zero if not. + * Return: Non-zero if empty, or zero if not. */ static inline int snd_pcm_capture_empty(struct snd_pcm_substream *substream) { @@ -852,7 +852,7 @@ int snd_pcm_format_big_endian(snd_pcm_format_t format); * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian * @format: the format to check * - * Returns 1 if the given PCM format is CPU-endian, 0 if + * Return: 1 if the given PCM format is CPU-endian, 0 if * opposite, or a negative error code if endian not specified. */ int snd_pcm_format_cpu_endian(snd_pcm_format_t format); @@ -963,7 +963,7 @@ struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream, * contiguous in kernel virtual space, but not in physical memory. Use this * if the buffer is accessed by kernel code but not by device DMA. * - * Returns 1 if the buffer was changed, 0 if not changed, or a negative error + * Return: 1 if the buffer was changed, 0 if not changed, or a negative error * code. */ static int snd_pcm_lib_alloc_vmalloc_buffer @@ -975,6 +975,9 @@ static int snd_pcm_lib_alloc_vmalloc_buffer * * This function works like snd_pcm_lib_alloc_vmalloc_buffer(), but uses * vmalloc_32(), i.e., the pages are allocated from 32-bit-addressable memory. + * + * Return: 1 if the buffer was changed, 0 if not changed, or a negative error + * code. */ static int snd_pcm_lib_alloc_vmalloc_32_buffer (struct snd_pcm_substream *substream, size_t size); @@ -1070,6 +1073,8 @@ const char *snd_pcm_format_name(snd_pcm_format_t format); /** * snd_pcm_stream_str - Get a string naming the direction of a stream * @substream: the pcm substream instance + * + * Return: A string naming the direction of the stream. */ static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream) { diff --git a/sound/core/control.c b/sound/core/control.c index 8c7c2c9bba6..d8aa206e8bd 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -190,7 +190,7 @@ EXPORT_SYMBOL(snd_ctl_notify); * Allocates a new struct snd_kcontrol instance and copies the given template * to the new instance. It does not copy volatile data (access). * - * Returns the pointer of the new instance, or NULL on failure. + * Return: The pointer of the new instance, or %NULL on failure. */ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, unsigned int access) @@ -224,7 +224,7 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, * template. When the access field of ncontrol is 0, it's assumed as * READWRITE access. When the count field is 0, it's assumes as one. * - * Returns the pointer of the newly generated instance, or NULL on failure. + * Return: The pointer of the newly generated instance, or %NULL on failure. */ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, void *private_data) @@ -322,9 +322,10 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count) * snd_ctl_new1() to the given card. Assigns also an unique * numid used for fast search. * - * Returns zero if successful, or a negative error code on failure. - * * It frees automatically the control which cannot be added. + * + * Return: Zero if successful, or a negative error code on failure. + * */ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) { @@ -380,9 +381,9 @@ EXPORT_SYMBOL(snd_ctl_add); * and the add_on_replace flag is set, the control is added. If the * control exists, it is destroyed first. * - * Returns zero if successful, or a negative error code on failure. - * * It frees automatically the control which cannot be added or replaced. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace) @@ -442,8 +443,8 @@ EXPORT_SYMBOL(snd_ctl_replace); * Removes the control from the card and then releases the instance. * You don't need to call snd_ctl_free_one(). You must be in * the write lock - down_write(&card->controls_rwsem). - * - * Returns 0 if successful, or a negative error code on failure. + * + * Return: 0 if successful, or a negative error code on failure. */ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol) { @@ -470,8 +471,8 @@ EXPORT_SYMBOL(snd_ctl_remove); * * Finds the control instance with the given id, removes it from the * card list and releases it. - * - * Returns 0 if successful, or a negative error code on failure. + * + * Return: 0 if successful, or a negative error code on failure. */ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id) { @@ -498,8 +499,8 @@ EXPORT_SYMBOL(snd_ctl_remove_id); * * Finds the control instance with the given id, removes it from the * card list and releases it. - * - * Returns 0 if successful, or a negative error code on failure. + * + * Return: 0 if successful, or a negative error code on failure. */ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file, struct snd_ctl_elem_id *id) @@ -541,7 +542,7 @@ error: * Finds the control instance with the given id, and activate or * inactivate the control together with notification, if changed. * - * Returns 0 if unchanged, 1 if changed, or a negative error code on failure. + * Return: 0 if unchanged, 1 if changed, or a negative error code on failure. */ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, int active) @@ -587,7 +588,7 @@ EXPORT_SYMBOL_GPL(snd_ctl_activate_id); * Finds the control with the old id from the card, and replaces the * id with the new one. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id) @@ -616,10 +617,11 @@ EXPORT_SYMBOL(snd_ctl_rename_id); * * Finds the control instance with the given number-id from the card. * - * Returns the pointer of the instance if found, or NULL if not. - * * The caller must down card->controls_rwsem before calling this function * (if the race condition can happen). + * + * Return: The pointer of the instance if found, or %NULL if not. + * */ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid) { @@ -643,10 +645,11 @@ EXPORT_SYMBOL(snd_ctl_find_numid); * * Finds the control instance with the given id from the card. * - * Returns the pointer of the instance if found, or NULL if not. - * * The caller must down card->controls_rwsem before calling this function * (if the race condition can happen). + * + * Return: The pointer of the instance if found, or %NULL if not. + * */ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, struct snd_ctl_elem_id *id) @@ -1710,6 +1713,8 @@ EXPORT_SYMBOL(snd_ctl_boolean_stereo_info); * Sets all required fields in @info to their appropriate values. * If the control's accessibility is not the default (readable and writable), * the caller has to fill @info->access. + * + * Return: Zero. */ int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels, unsigned int items, const char *const names[]) diff --git a/sound/core/device.c b/sound/core/device.c index f03cb5444a5..df88defed17 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -39,7 +39,7 @@ * The data pointer plays a role as the identifier, too, so the * pointer address must be unique and unchanged. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_device_new(struct snd_card *card, snd_device_type_t type, void *device_data, struct snd_device_ops *ops) @@ -73,7 +73,7 @@ EXPORT_SYMBOL(snd_device_new); * callbacks, dev_disconnect and dev_free, corresponding to the state. * Then release the device. * - * Returns zero if successful, or a negative error code on failure or if the + * Return: Zero if successful, or a negative error code on failure or if the * device not found. */ int snd_device_free(struct snd_card *card, void *device_data) @@ -116,7 +116,7 @@ EXPORT_SYMBOL(snd_device_free); * * Usually called from snd_card_disconnect(). * - * Returns zero if successful, or a negative error code on failure or if the + * Return: Zero if successful, or a negative error code on failure or if the * device not found. */ int snd_device_disconnect(struct snd_card *card, void *device_data) @@ -151,7 +151,7 @@ int snd_device_disconnect(struct snd_card *card, void *device_data) * but it can be called later if any new devices are created after * invocation of snd_card_register(). * - * Returns zero if successful, or a negative error code on failure or if the + * Return: Zero if successful, or a negative error code on failure or if the * device not found. */ int snd_device_register(struct snd_card *card, void *device_data) diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 3f7f6628cf7..d105073298c 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -356,7 +356,7 @@ static const struct file_operations snd_hwdep_f_ops = * The callbacks (hwdep->ops) must be set on the returned instance * after this call manually by the caller. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_hwdep_new(struct snd_card *card, char *id, int device, struct snd_hwdep **rhwdep) diff --git a/sound/core/info.c b/sound/core/info.c index 5bb97e7d325..db308dbc891 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -105,7 +105,7 @@ static int resize_info_buffer(struct snd_info_buffer *buffer, * * Outputs the string on the procfs buffer just like printf(). * - * Returns the size of output string. + * Return: The size of output string, or a negative error code. */ int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) { @@ -694,7 +694,7 @@ int snd_info_card_free(struct snd_card *card) * * Reads one line from the buffer and stores the string. * - * Returns zero if successful, or 1 if error or EOF. + * Return: Zero if successful, or 1 if error or EOF. */ int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) { @@ -735,7 +735,7 @@ EXPORT_SYMBOL(snd_info_get_line); * Parses the original string and copy a token to the given * string buffer. * - * Returns the updated pointer of the original string so that + * Return: The updated pointer of the original string so that * it can be used for the next call. */ const char *snd_info_get_str(char *dest, const char *src, int len) @@ -774,7 +774,7 @@ EXPORT_SYMBOL(snd_info_get_str); * Usually called from other functions such as * snd_info_create_card_entry(). * - * Returns the pointer of the new instance, or NULL on failure. + * Return: The pointer of the new instance, or %NULL on failure. */ static struct snd_info_entry *snd_info_create_entry(const char *name) { @@ -803,7 +803,7 @@ static struct snd_info_entry *snd_info_create_entry(const char *name) * * Creates a new info entry and assigns it to the given module. * - * Returns the pointer of the new instance, or NULL on failure. + * Return: The pointer of the new instance, or %NULL on failure. */ struct snd_info_entry *snd_info_create_module_entry(struct module * module, const char *name, @@ -827,7 +827,7 @@ EXPORT_SYMBOL(snd_info_create_module_entry); * * Creates a new info entry and assigns it to the given card. * - * Returns the pointer of the new instance, or NULL on failure. + * Return: The pointer of the new instance, or %NULL on failure. */ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, @@ -893,7 +893,7 @@ static int snd_info_dev_register_entry(struct snd_device *device) * For releasing this entry, use snd_device_free() instead of * snd_info_free_entry(). * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp) @@ -949,7 +949,7 @@ EXPORT_SYMBOL(snd_info_free_entry); * * Registers the proc info entry. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_info_register(struct snd_info_entry * entry) { diff --git a/sound/core/init.c b/sound/core/init.c index 7b012d15c2c..6ef06400dfc 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -144,7 +144,7 @@ static inline int init_info_for_card(struct snd_card *card) * space for the driver to use freely. The allocated struct is stored * in the given card_ret pointer. * - * Returns zero if successful or a negative error code. + * Return: Zero if successful or a negative error code. */ int snd_card_create(int idx, const char *xid, struct module *module, int extra_size, @@ -337,7 +337,7 @@ static const struct file_operations snd_shutdown_f_ops = * * Disconnects all APIs from the file-operations (user space). * - * Returns zero, otherwise a negative error code. + * Return: Zero, otherwise a negative error code. * * Note: The current implementation replaces all active file->f_op with special * dummy file operations (they do nothing except release). @@ -415,7 +415,7 @@ EXPORT_SYMBOL(snd_card_disconnect); * devices automatically. That is, you don't have to release the devices * by yourself. * - * Returns zero. Frees all associated devices and frees the control + * Return: Zero. Frees all associated devices and frees the control * interface associated to given soundcard. */ static int snd_card_do_free(struct snd_card *card) @@ -677,7 +677,7 @@ static struct device_attribute card_number_attrs = * external accesses. Thus, you should call this function at the end * of the initialization of the card. * - * Returns zero otherwise a negative error code if the registration failed. + * Return: Zero otherwise a negative error code if the registration failed. */ int snd_card_register(struct snd_card *card) { @@ -849,7 +849,7 @@ int __exit snd_card_info_done(void) * This function adds the component id string to the supported list. * The component can be referred from the alsa-lib. * - * Returns zero otherwise a negative error code. + * Return: Zero otherwise a negative error code. */ int snd_component_add(struct snd_card *card, const char *component) @@ -883,7 +883,7 @@ EXPORT_SYMBOL(snd_component_add); * This linked-list is used to keep tracking the connection state, * and to avoid the release of busy resources by hotplug. * - * Returns zero or a negative error code. + * Return: zero or a negative error code. */ int snd_card_file_add(struct snd_card *card, struct file *file) { @@ -920,7 +920,7 @@ EXPORT_SYMBOL(snd_card_file_add); * called beforehand, it processes the pending release of * resources. * - * Returns zero or a negative error code. + * Return: Zero or a negative error code. */ int snd_card_file_remove(struct snd_card *card, struct file *file) { @@ -959,6 +959,8 @@ EXPORT_SYMBOL(snd_card_file_remove); * * Waits until the power-state is changed. * + * Return: Zero if successful, or a negative error code. + * * Note: the power lock must be active before call. */ int snd_power_wait(struct snd_card *card, unsigned int power_state) diff --git a/sound/core/isadma.c b/sound/core/isadma.c index c0f1208bb7d..e2b386156a4 100644 --- a/sound/core/isadma.c +++ b/sound/core/isadma.c @@ -81,7 +81,7 @@ EXPORT_SYMBOL(snd_dma_disable); * @dma: the dma number * @size: the dma transfer size * - * Returns the current pointer in DMA tranfer buffer in bytes + * Return: The current pointer in DMA transfer buffer in bytes. */ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) { diff --git a/sound/core/jack.c b/sound/core/jack.c index a06b1651fcb..b35fe7345c2 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -98,8 +98,8 @@ static int snd_jack_dev_register(struct snd_device *device) * * Creates a new jack object. * - * Returns zero if successful, or a negative error code on failure. - * On success jjack will be initialised. + * Return: Zero if successful, or a negative error code on failure. + * On success @jjack will be initialised. */ int snd_jack_new(struct snd_card *card, const char *id, int type, struct snd_jack **jjack) @@ -189,6 +189,8 @@ EXPORT_SYMBOL(snd_jack_set_parent); * using this abstraction. * * This function may only be called prior to registration of the jack. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type, int keytype) diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 69156923843..bdf826f4fe0 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -81,7 +81,7 @@ static inline void dec_snd_pages(int order) * * Allocates the physically contiguous pages with the given size. * - * Returns the pointer of the buffer, or NULL if no enoguh memory. + * Return: The pointer of the buffer, or %NULL if no enough memory. */ void *snd_malloc_pages(size_t size, gfp_t gfp_flags) { @@ -175,9 +175,9 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, * * Calls the memory-allocator function for the corresponding * buffer type. - * - * Returns zero if the buffer with the given size is allocated successfully, - * other a negative value at error. + * + * Return: Zero if the buffer with the given size is allocated successfully, + * otherwise a negative value on error. */ int snd_dma_alloc_pages(int type, struct device *device, size_t size, struct snd_dma_buffer *dmab) @@ -229,9 +229,9 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, * buffer type. When no space is left, this function reduces the size and * tries to allocate again. The size actually allocated is stored in * res_size argument. - * - * Returns zero if the buffer with the given size is allocated successfully, - * other a negative value at error. + * + * Return: Zero if the buffer with the given size is allocated successfully, + * otherwise a negative value on error. */ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size, struct snd_dma_buffer *dmab) @@ -292,7 +292,7 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) * Looks for the reserved-buffer list and re-uses if the same buffer * is found in the list. When the buffer is found, it's removed from the free list. * - * Returns the size of buffer if the buffer is found, or zero if not found. + * Return: The size of buffer if the buffer is found, or zero if not found. */ size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id) { @@ -326,8 +326,8 @@ size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id) * @id: the buffer id * * Reserves the given buffer as a reserved buffer. - * - * Returns zero if successful, or a negative code at error. + * + * Return: Zero if successful, or a negative code on error. */ int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id) { diff --git a/sound/core/memory.c b/sound/core/memory.c index 66a278d0b04..36c0f1a2e18 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -33,7 +33,7 @@ * * Copies the data from mmio-space to user-space. * - * Returns zero if successful, or non-zero on failure. + * Return: Zero if successful, or non-zero on failure. */ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count) { @@ -66,7 +66,7 @@ EXPORT_SYMBOL(copy_to_user_fromio); * * Copies the data from user-space to mmio-space. * - * Returns zero if successful, or non-zero on failure. + * Return: Zero if successful, or non-zero on failure. */ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count) { diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 61798f85d03..578327e2bc2 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -637,7 +637,7 @@ static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substrea * calling this, i.e. zero must be given to the argument of * snd_pcm_new(). * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) { @@ -759,7 +759,7 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device, * The pcm operators have to be set afterwards to the new instance * via snd_pcm_set_ops(). * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, struct snd_pcm **rpcm) @@ -787,7 +787,7 @@ EXPORT_SYMBOL(snd_pcm_new); * The pcm operators have to be set afterwards to the new instance * via snd_pcm_set_ops(). * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index c4840ff75d0..41b3dfe6869 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -666,7 +666,8 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b, * The interval is changed to the range satisfying both intervals. * The interval status (min, max, integer, etc.) are evaluated. * - * Returns non-zero if the value is changed, zero if not changed. + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) { @@ -865,7 +866,8 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, * @nump: pointer to store the resultant numerator * @denp: pointer to store the resultant denominator * - * Returns non-zero if the value is changed, zero if not changed. + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ int snd_interval_ratnum(struct snd_interval *i, unsigned int rats_count, struct snd_ratnum *rats, @@ -983,7 +985,8 @@ EXPORT_SYMBOL(snd_interval_ratnum); * @nump: pointer to store the resultant numerator * @denp: pointer to store the resultant denominator * - * Returns non-zero if the value is changed, zero if not changed. + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ static int snd_interval_ratden(struct snd_interval *i, unsigned int rats_count, struct snd_ratden *rats, @@ -1082,7 +1085,8 @@ static int snd_interval_ratden(struct snd_interval *i, * When mask is non-zero, only the elements corresponding to bit 1 are * evaluated. * - * Returns non-zero if the value is changed, zero if not changed. + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ int snd_interval_list(struct snd_interval *i, unsigned int count, const unsigned int *list, unsigned int mask) @@ -1142,7 +1146,7 @@ static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned * @private: the private data pointer passed to function * @dep: the dependent variables * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, int var, @@ -1200,6 +1204,8 @@ EXPORT_SYMBOL(snd_pcm_hw_rule_add); * @mask: the bitmap mask * * Apply the constraint of the given bitmap mask to a 32-bit mask parameter. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, u_int32_t mask) @@ -1220,6 +1226,8 @@ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param * @mask: the 64bit bitmap mask * * Apply the constraint of the given bitmap mask to a 64-bit mask parameter. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, u_int64_t mask) @@ -1240,6 +1248,9 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par * @var: hw_params variable to apply the integer constraint * * Apply the constraint of integer to an interval parameter. + * + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var) { @@ -1257,6 +1268,9 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); * @max: the maximal value * * Apply the min/max range constraint to an interval parameter. + * + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. */ int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, unsigned int min, unsigned int max) @@ -1288,6 +1302,8 @@ static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, * @l: list * * Apply the list of constraints to an interval parameter. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1322,6 +1338,8 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, * @cond: condition bits * @var: hw_params variable to apply the ratnums constraint * @r: struct snd_ratnums constriants + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1355,6 +1373,8 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, * @cond: condition bits * @var: hw_params variable to apply the ratdens constraint * @r: struct snd_ratdens constriants + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1386,6 +1406,8 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, * @cond: condition bits * @width: sample bits width * @msbits: msbits width + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1414,6 +1436,8 @@ static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, * @cond: condition bits * @var: hw_params variable to apply the step constraint * @step: step size + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1444,6 +1468,8 @@ static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the power-of-2 constraint + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, unsigned int cond, @@ -1470,6 +1496,8 @@ static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params, * snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling * @runtime: PCM runtime instance * @base_rate: the rate at which the hardware does not resample + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime, unsigned int base_rate) @@ -1519,8 +1547,8 @@ EXPORT_SYMBOL(_snd_pcm_hw_params_any); * @var: parameter to retrieve * @dir: pointer to the direction (-1,0,1) or %NULL * - * Return the value for field @var if it's fixed in configuration space - * defined by @params. Return -%EINVAL otherwise. + * Return: The value for field @var if it's fixed in configuration space + * defined by @params. -%EINVAL otherwise. */ int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, int *dir) @@ -1591,7 +1619,8 @@ static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, * * Inside configuration space defined by @params remove from @var all * values > minimum. Reduce configuration space accordingly. - * Return the minimum. + * + * Return: The minimum, or a negative error code on failure. */ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, @@ -1637,7 +1666,8 @@ static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, * * Inside configuration space defined by @params remove from @var all * values < maximum. Reduce configuration space accordingly. - * Return the maximum. + * + * Return: The maximum, or a negative error code on failure. */ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, @@ -1665,6 +1695,8 @@ EXPORT_SYMBOL(snd_pcm_hw_param_last); * The configuration chosen is that obtained fixing in this order: * first access, first format, first subformat, min channels, * min rate, min period time, max buffer size, min tick time + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params) @@ -1771,7 +1803,7 @@ static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream, * Processes the generic ioctl commands for PCM. * Can be passed as the ioctl callback for PCM ops. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) @@ -2510,7 +2542,7 @@ static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol) * @info_ret: store struct snd_pcm_chmap instance if non-NULL * * Create channel-mapping control elements assigned to the given PCM stream(s). - * Returns zero if succeed, or a negative error value. + * Return: Zero if successful, or a negative error value. */ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, const struct snd_pcm_chmap_elem *chmap, diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 69e01c4fc32..0af622c34e1 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -95,7 +95,7 @@ static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream * * Releases the pre-allocated buffer of the given substream. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream) { @@ -115,7 +115,7 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream) * * Releases all the pre-allocated buffers on the given pcm. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm) { @@ -265,7 +265,7 @@ static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream, * destruction time. The dma_buf_id must be unique for all systems * (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id(). * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream, int type, struct device *data, @@ -289,7 +289,7 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); * Do pre-allocation to all substreams of the given pcm for the * specified DMA type. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, int type, void *data, @@ -313,8 +313,9 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); * @substream: the pcm substream instance * @offset: the buffer offset * - * Returns the page struct at the given buffer offset. * Used as the page callback of PCM ops. + * + * Return: The page struct at the given buffer offset. %NULL on failure. */ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset) { @@ -337,7 +338,7 @@ EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); * Allocates the DMA buffer on the BUS type given earlier to * snd_pcm_lib_preallocate_xxx_pages(). * - * Returns 1 if the buffer is changed, 0 if not changed, or a negative + * Return: 1 if the buffer is changed, 0 if not changed, or a negative * code on failure. */ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) @@ -390,7 +391,7 @@ EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); * * Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages(). * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream) { @@ -437,6 +438,8 @@ EXPORT_SYMBOL(_snd_pcm_lib_alloc_vmalloc_buffer); * snd_pcm_lib_free_vmalloc_buffer - free vmalloc buffer * @substream: the substream with a buffer allocated by * snd_pcm_lib_alloc_vmalloc_buffer() + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream) { @@ -458,6 +461,8 @@ EXPORT_SYMBOL(snd_pcm_lib_free_vmalloc_buffer); * @offset: offset in the buffer * * This function is to be used as the page callback in the PCM ops. + * + * Return: The page struct, or %NULL on failure. */ struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream, unsigned long offset) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index d4fc1bfbe45..b875b19f2d5 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -213,7 +213,7 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { * snd_pcm_format_signed - Check the PCM format is signed linear * @format: the format to check * - * Returns 1 if the given PCM format is signed linear, 0 if unsigned + * Return: 1 if the given PCM format is signed linear, 0 if unsigned * linear, and a negative error code for non-linear formats. */ int snd_pcm_format_signed(snd_pcm_format_t format) @@ -232,7 +232,7 @@ EXPORT_SYMBOL(snd_pcm_format_signed); * snd_pcm_format_unsigned - Check the PCM format is unsigned linear * @format: the format to check * - * Returns 1 if the given PCM format is unsigned linear, 0 if signed + * Return: 1 if the given PCM format is unsigned linear, 0 if signed * linear, and a negative error code for non-linear formats. */ int snd_pcm_format_unsigned(snd_pcm_format_t format) @@ -251,7 +251,7 @@ EXPORT_SYMBOL(snd_pcm_format_unsigned); * snd_pcm_format_linear - Check the PCM format is linear * @format: the format to check * - * Returns 1 if the given PCM format is linear, 0 if not. + * Return: 1 if the given PCM format is linear, 0 if not. */ int snd_pcm_format_linear(snd_pcm_format_t format) { @@ -264,7 +264,7 @@ EXPORT_SYMBOL(snd_pcm_format_linear); * snd_pcm_format_little_endian - Check the PCM format is little-endian * @format: the format to check * - * Returns 1 if the given PCM format is little-endian, 0 if + * Return: 1 if the given PCM format is little-endian, 0 if * big-endian, or a negative error code if endian not specified. */ int snd_pcm_format_little_endian(snd_pcm_format_t format) @@ -283,7 +283,7 @@ EXPORT_SYMBOL(snd_pcm_format_little_endian); * snd_pcm_format_big_endian - Check the PCM format is big-endian * @format: the format to check * - * Returns 1 if the given PCM format is big-endian, 0 if + * Return: 1 if the given PCM format is big-endian, 0 if * little-endian, or a negative error code if endian not specified. */ int snd_pcm_format_big_endian(snd_pcm_format_t format) @@ -302,7 +302,7 @@ EXPORT_SYMBOL(snd_pcm_format_big_endian); * snd_pcm_format_width - return the bit-width of the format * @format: the format to check * - * Returns the bit-width of the format, or a negative error code + * Return: The bit-width of the format, or a negative error code * if unknown format. */ int snd_pcm_format_width(snd_pcm_format_t format) @@ -321,7 +321,7 @@ EXPORT_SYMBOL(snd_pcm_format_width); * snd_pcm_format_physical_width - return the physical bit-width of the format * @format: the format to check * - * Returns the physical bit-width of the format, or a negative error code + * Return: The physical bit-width of the format, or a negative error code * if unknown format. */ int snd_pcm_format_physical_width(snd_pcm_format_t format) @@ -341,7 +341,7 @@ EXPORT_SYMBOL(snd_pcm_format_physical_width); * @format: the format to check * @samples: sampling rate * - * Returns the byte size of the given samples for the format, or a + * Return: The byte size of the given samples for the format, or a * negative error code if unknown format. */ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) @@ -358,7 +358,7 @@ EXPORT_SYMBOL(snd_pcm_format_size); * snd_pcm_format_silence_64 - return the silent data in 8 bytes array * @format: the format to check * - * Returns the format pattern to fill or NULL if error. + * Return: The format pattern to fill or %NULL if error. */ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) { @@ -379,7 +379,7 @@ EXPORT_SYMBOL(snd_pcm_format_silence_64); * * Sets the silence data on the buffer for the given samples. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples) { @@ -449,7 +449,7 @@ EXPORT_SYMBOL(snd_pcm_format_set_silence); * Determines the rate_min and rate_max fields from the rates bits of * the given runtime->hw. * - * Returns zero if successful. + * Return: Zero if successful. */ int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime) { @@ -475,7 +475,7 @@ EXPORT_SYMBOL(snd_pcm_limit_hw_rates); * snd_pcm_rate_to_rate_bit - converts sample rate to SNDRV_PCM_RATE_xxx bit * @rate: the sample rate to convert * - * Returns the SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or + * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or * SNDRV_PCM_RATE_KNOT for an unknown rate. */ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate) @@ -493,8 +493,8 @@ EXPORT_SYMBOL(snd_pcm_rate_to_rate_bit); * snd_pcm_rate_bit_to_rate - converts SNDRV_PCM_RATE_xxx bit to sample rate * @rate_bit: the rate bit to convert * - * Returns the sample rate that corresponds to the given SNDRV_PCM_RATE_xxx flag - * or 0 for an unknown rate bit + * Return: The sample rate that corresponds to the given SNDRV_PCM_RATE_xxx flag + * or 0 for an unknown rate bit. */ unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 71ae86ca64a..5bce9152b64 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -898,6 +898,8 @@ static struct action_ops snd_pcm_action_start = { /** * snd_pcm_start - start all linked streams * @substream: the PCM substream instance + * + * Return: Zero if successful, or a negative error code. */ int snd_pcm_start(struct snd_pcm_substream *substream) { @@ -951,6 +953,8 @@ static struct action_ops snd_pcm_action_stop = { * @state: PCM state after stopping the stream * * The state of each stream is then changed to the given state unconditionally. + * + * Return: Zero if succesful, or a negative error code. */ int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state) { @@ -965,6 +969,8 @@ EXPORT_SYMBOL(snd_pcm_stop); * * After stopping, the state is changed to SETUP. * Unlike snd_pcm_stop(), this affects only the given stream. + * + * Return: Zero if succesful, or a negative error code. */ int snd_pcm_drain_done(struct snd_pcm_substream *substream) { @@ -1098,6 +1104,9 @@ static struct action_ops snd_pcm_action_suspend = { * @substream: the PCM substream * * After this call, all streams are changed to SUSPENDED state. + * + * Return: Zero if successful (or @substream is %NULL), or a negative error + * code. */ int snd_pcm_suspend(struct snd_pcm_substream *substream) { @@ -1120,6 +1129,8 @@ EXPORT_SYMBOL(snd_pcm_suspend); * @pcm: the PCM instance * * After this call, all streams are changed to SUSPENDED state. + * + * Return: Zero if successful (or @pcm is %NULL), or a negative error code. */ int snd_pcm_suspend_all(struct snd_pcm *pcm) { @@ -1343,6 +1354,8 @@ static struct action_ops snd_pcm_action_prepare = { * snd_pcm_prepare - prepare the PCM substream to be triggerable * @substream: the PCM substream instance * @file: file to refer f_flags + * + * Return: Zero if successful, or a negative error code. */ static int snd_pcm_prepare(struct snd_pcm_substream *substream, struct file *file) diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 1bb95aeea08..7b596b5751d 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -863,7 +863,7 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card, * * Reads the data from the internal buffer. * - * Returns the size of read data, or a negative error code on failure. + * Return: The size of read data, or a negative error code on failure. */ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, const unsigned char *buffer, int count) @@ -1024,8 +1024,8 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun /** * snd_rawmidi_transmit_empty - check whether the output buffer is empty * @substream: the rawmidi substream - * - * Returns 1 if the internal output buffer is empty, 0 if not. + * + * Return: 1 if the internal output buffer is empty, 0 if not. */ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) { @@ -1055,7 +1055,7 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) * and call snd_rawmidi_transmit_ack() after the transmission is * finished. * - * Returns the size of copied data, or a negative error code on failure. + * Return: The size of copied data, or a negative error code on failure. */ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) @@ -1107,7 +1107,7 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, * the given size and updates the condition. * Call after the transmission is finished. * - * Returns the advanced size if successful, or a negative error code on failure. + * Return: The advanced size if successful, or a negative error code on failure. */ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) { @@ -1140,7 +1140,7 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) * * Copies data from the buffer to the device and advances the pointer. * - * Returns the copied size if successful, or a negative error code on failure. + * Return: The copied size if successful, or a negative error code on failure. */ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) @@ -1438,7 +1438,7 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi, * Creates a new rawmidi instance. * Use snd_rawmidi_set_ops() to set the operators to the new instance. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_rawmidi_new(struct snd_card *card, char *id, int device, int output_count, int input_count, diff --git a/sound/core/sound.c b/sound/core/sound.c index 70ccdab7415..f002bd911da 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -102,6 +102,9 @@ static void snd_request_other(int minor) * This function increments the reference counter of the card instance * if an associated instance with the given minor number and type is found. * The caller must call snd_card_unref() appropriately later. + * + * Return: The user data pointer if the specified device is found. %NULL + * otherwise. */ void *snd_lookup_minor_data(unsigned int minor, int type) { @@ -261,7 +264,7 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev) * Registers an ALSA device file for the given card. * The operators have to be set in reg parameter. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_register_device_for_dev(int type, struct snd_card *card, int dev, const struct file_operations *f_ops, @@ -339,7 +342,7 @@ static int find_snd_minor(int type, struct snd_card *card, int dev) * Unregisters the device file already registered via * snd_register_device(). * - * Returns zero if sucecessful, or a negative error code on failure + * Return: Zero if successful, or a negative error code on failure. */ int snd_unregister_device(int type, struct snd_card *card, int dev) { diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 857586135d1..55c9d8c8d3c 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -362,8 +362,7 @@ static void master_free(struct snd_kcontrol *kcontrol) * @name: name string of the control element to create * @tlv: optional TLV int array for dB information * - * Creates a virtual matster control with the given name string. - * Returns the created control element, or NULL for errors (ENOMEM). + * Creates a virtual master control with the given name string. * * After creating a vmaster element, you can add the slave controls * via snd_ctl_add_slave() or snd_ctl_add_slave_uncached(). @@ -372,6 +371,8 @@ static void master_free(struct snd_kcontrol *kcontrol) * for dB scale of the master control. It should be a single element * with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or * #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB. + * + * Return: The created control element, or %NULL for errors (ENOMEM). */ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, const unsigned int *tlv) @@ -423,6 +424,8 @@ EXPORT_SYMBOL(snd_ctl_make_virtual_master); * * Adds the given hook to the vmaster control element so that it's called * at each time when the value is changed. + * + * Return: Zero. */ int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol, void (*hook)(void *private_data, int), diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index 4608c2ca43f..e3a90d043f0 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -129,6 +129,8 @@ static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu) * @dev_id: mpu401 instance * * Processes the interrupt for MPU401-UART i/o. + * + * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise. */ irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id) { @@ -148,6 +150,8 @@ EXPORT_SYMBOL(snd_mpu401_uart_interrupt); * @dev_id: mpu401 instance * * Processes the interrupt for MPU401-UART output. + * + * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise. */ irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id) { @@ -519,7 +523,7 @@ static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi) * not the mpu401 instance itself. To access to the mpu401 instance, * cast from rawmidi->private_data (with struct snd_mpu401 magic-cast). * - * Returns zero if successful, or a negative error code. + * Return: Zero if successful, or a negative error code. */ int snd_mpu401_uart_new(struct snd_card *card, int device, unsigned short hardware, diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 8b0f9968830..d37c683cfd7 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -299,7 +299,7 @@ EXPORT_SYMBOL(snd_ac97_write); * Reads a value from the given register. This will invoke the read * callback directly after the register check. * - * Returns the read value. + * Return: The read value. */ unsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { @@ -352,7 +352,7 @@ EXPORT_SYMBOL(snd_ac97_write_cache); * Compares the value with the register cache and updates the value * only when the value is changed. * - * Returns 1 if the value is changed, 0 if no change, or a negative + * Return: 1 if the value is changed, 0 if no change, or a negative * code on failure. */ int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value) @@ -384,7 +384,7 @@ EXPORT_SYMBOL(snd_ac97_update); * Updates the masked-bits on the given register only when the value * is changed. * - * Returns 1 if the bits are changed, 0 if no change, or a negative + * Return: 1 if the bits are changed, 0 if no change, or a negative * code on failure. */ int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value) @@ -1836,7 +1836,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m * snd_ac97_get_short_name - retrieve codec name * @ac97: the codec instance * - * Returns the short identifying name of the codec. + * Return: The short identifying name of the codec. */ const char *snd_ac97_get_short_name(struct snd_ac97 *ac97) { @@ -1910,7 +1910,7 @@ static int ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem) * The AC97 bus instance is registered as a low-level device, so you don't * have to release it manually. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops, void *private_data, struct snd_ac97_bus **rbus) @@ -2006,7 +2006,7 @@ static void do_update_power(struct work_struct *work) * The ac97 instance is registered as a low-level device, so you don't * have to release it manually. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, struct snd_ac97 **rac97) { @@ -2373,6 +2373,8 @@ static struct ac97_power_reg power_regs[PWIDX_SIZE] = { * @powerup: non-zero when power up the part * * Update the AC97 powerdown register bits of the given part. + * + * Return: Zero. */ int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup) { @@ -2885,7 +2887,7 @@ static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr) * headphone (true line-out) control as "Master". * The quirk-list must be terminated with a zero-filled entry. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override) diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index f1488fc176d..eab0fc9ff2e 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -253,7 +253,7 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate) * AC97_SPDIF is accepted as a pseudo register to modify the SPDIF * status bits. * - * Returns zero if successful, or a negative error code on failure. + * Return: Zero if successful, or a negative error code on failure. */ int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate) { @@ -440,6 +440,8 @@ static unsigned int get_rates(struct ac97_pcm *pcm, unsigned int cidx, unsigned * It assigns available AC97 slots for given PCMs. If none or only * some slots are available, pcm->xxx.slots and pcm->xxx.rslots[] members * are reduced and might be zero. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_ac97_pcm_assign(struct snd_ac97_bus *bus, unsigned short pcms_count, @@ -562,6 +564,8 @@ EXPORT_SYMBOL(snd_ac97_pcm_assign); * @slots: a subset of allocated slots (snd_ac97_pcm_assign) for this pcm * * It locks the specified slots and sets the given rate to AC97 registers. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate, enum ac97_pcm_cfg cfg, unsigned short slots) @@ -644,6 +648,8 @@ EXPORT_SYMBOL(snd_ac97_pcm_open); * @pcm: the ac97 pcm instance * * It frees the locked AC97 slots. + * + * Return: Zero. */ int snd_ac97_pcm_close(struct ac97_pcm *pcm) { @@ -718,6 +724,8 @@ static int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params, * * Installs the hardware constraint rules to prevent using double rates and * more than two channels at the same time. + * + * Return: Zero if successful, or a negative error code on failure. */ int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime) { diff --git a/sound/sound_core.c b/sound/sound_core.c index bb23009edc8..359753fc24e 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -352,7 +352,9 @@ static struct sound_unit *chains[SOUND_STEP]; * @dev: device pointer * * Allocate a special sound device by minor number from the sound - * subsystem. The allocated number is returned on success. On failure + * subsystem. + * + * Return: The allocated number is returned on success. On failure, * a negative error code is returned. */ @@ -436,8 +438,10 @@ EXPORT_SYMBOL(register_sound_special); * @dev: Unit number to allocate * * Allocate a mixer device. Unit is the number of the mixer requested. - * Pass -1 to request the next free mixer unit. On success the allocated - * number is returned, on failure a negative error code is returned. + * Pass -1 to request the next free mixer unit. + * + * Return: On success, the allocated number is returned. On failure, + * a negative error code is returned. */ int register_sound_mixer(const struct file_operations *fops, int dev) @@ -454,8 +458,10 @@ EXPORT_SYMBOL(register_sound_mixer); * @dev: Unit number to allocate * * Allocate a midi device. Unit is the number of the midi device requested. - * Pass -1 to request the next free midi unit. On success the allocated - * number is returned, on failure a negative error code is returned. + * Pass -1 to request the next free midi unit. + * + * Return: On success, the allocated number is returned. On failure, + * a negative error code is returned. */ int register_sound_midi(const struct file_operations *fops, int dev) @@ -477,11 +483,13 @@ EXPORT_SYMBOL(register_sound_midi); * @dev: Unit number to allocate * * Allocate a DSP device. Unit is the number of the DSP requested. - * Pass -1 to request the next free DSP unit. On success the allocated - * number is returned, on failure a negative error code is returned. + * Pass -1 to request the next free DSP unit. * * This function allocates both the audio and dsp device entries together * and will always allocate them as a matching pair - eg dsp3/audio3 + * + * Return: On success, the allocated number is returned. On failure, + * a negative error code is returned. */ int register_sound_dsp(const struct file_operations *fops, int dev) -- cgit v1.2.3-70-g09d2 From b818d1a7f72575eef17e00dc4085512c9cc8897d Mon Sep 17 00:00:00 2001 From: Hector Palacios Date: Sun, 10 Mar 2013 22:50:02 +0000 Subject: phy/micrel: Add support for KSZ8031 Micrel PHY KSZ8031 is similar to KSZ8021 and also requires the special initialization of "Operation Mode Strap Override" in reg 0x16 introduced in 212ea99 (phy/micrel: Implement support for KSZ8021). Signed-off-by: Hector Palacios Reviewed-by: Marek Vasut Signed-off-by: David S. Miller --- drivers/net/phy/micrel.c | 14 ++++++++++++++ include/linux/micrel_phy.h | 1 + 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index abf7b6153d0..018af1852fe 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -191,6 +191,19 @@ static struct phy_driver ksphy_driver[] = { .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, .driver = { .owner = THIS_MODULE,}, +}, { + .phy_id = PHY_ID_KSZ8031, + .phy_id_mask = 0x00ffffff, + .name = "Micrel KSZ8031", + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | + SUPPORTED_Asym_Pause), + .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, + .config_init = ksz8021_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = kszphy_ack_interrupt, + .config_intr = kszphy_config_intr, + .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8041, .phy_id_mask = 0x00fffff0, @@ -325,6 +338,7 @@ static struct mdio_device_id __maybe_unused micrel_tbl[] = { { PHY_ID_KSZ8001, 0x00ffffff }, { PHY_ID_KS8737, 0x00fffff0 }, { PHY_ID_KSZ8021, 0x00ffffff }, + { PHY_ID_KSZ8031, 0x00ffffff }, { PHY_ID_KSZ8041, 0x00fffff0 }, { PHY_ID_KSZ8051, 0x00fffff0 }, { PHY_ID_KSZ8061, 0x00fffff0 }, diff --git a/include/linux/micrel_phy.h b/include/linux/micrel_phy.h index 9dbb41a4e25..8752dbbc613 100644 --- a/include/linux/micrel_phy.h +++ b/include/linux/micrel_phy.h @@ -19,6 +19,7 @@ #define PHY_ID_KSZ9021 0x00221610 #define PHY_ID_KS8737 0x00221720 #define PHY_ID_KSZ8021 0x00221555 +#define PHY_ID_KSZ8031 0x00221556 #define PHY_ID_KSZ8041 0x00221510 #define PHY_ID_KSZ8051 0x00221550 /* same id: ks8001 Rev. A/B, and ks8721 Rev 3. */ -- cgit v1.2.3-70-g09d2 From 6ba8a3b19e764b6a65e4030ab0999be50c291e6c Mon Sep 17 00:00:00 2001 From: Nandita Dukkipati Date: Mon, 11 Mar 2013 10:00:43 +0000 Subject: tcp: Tail loss probe (TLP) This patch series implement the Tail loss probe (TLP) algorithm described in http://tools.ietf.org/html/draft-dukkipati-tcpm-tcp-loss-probe-01. The first patch implements the basic algorithm. TLP's goal is to reduce tail latency of short transactions. It achieves this by converting retransmission timeouts (RTOs) occuring due to tail losses (losses at end of transactions) into fast recovery. TLP transmits one packet in two round-trips when a connection is in Open state and isn't receiving any ACKs. The transmitted packet, aka loss probe, can be either new or a retransmission. When there is tail loss, the ACK from a loss probe triggers FACK/early-retransmit based fast recovery, thus avoiding a costly RTO. In the absence of loss, there is no change in the connection state. PTO stands for probe timeout. It is a timer event indicating that an ACK is overdue and triggers a loss probe packet. The PTO value is set to max(2*SRTT, 10ms) and is adjusted to account for delayed ACK timer when there is only one oustanding packet. TLP Algorithm On transmission of new data in Open state: -> packets_out > 1: schedule PTO in max(2*SRTT, 10ms). -> packets_out == 1: schedule PTO in max(2*RTT, 1.5*RTT + 200ms) -> PTO = min(PTO, RTO) Conditions for scheduling PTO: -> Connection is in Open state. -> Connection is either cwnd limited or no new data to send. -> Number of probes per tail loss episode is limited to one. -> Connection is SACK enabled. When PTO fires: new_segment_exists: -> transmit new segment. -> packets_out++. cwnd remains same. no_new_packet: -> retransmit the last segment. Its ACK triggers FACK or early retransmit based recovery. ACK path: -> rearm RTO at start of ACK processing. -> reschedule PTO if need be. In addition, the patch includes a small variation to the Early Retransmit (ER) algorithm, such that ER and TLP together can in principle recover any N-degree of tail loss through fast recovery. TLP is controlled by the same sysctl as ER, tcp_early_retrans sysctl. tcp_early_retrans==0; disables TLP and ER. ==1; enables RFC5827 ER. ==2; delayed ER. ==3; TLP and delayed ER. [DEFAULT] ==4; TLP only. The TLP patch series have been extensively tested on Google Web servers. It is most effective for short Web trasactions, where it reduced RTOs by 15% and improved HTTP response time (average by 6%, 99th percentile by 10%). The transmitted probes account for <0.5% of the overall transmissions. Signed-off-by: Nandita Dukkipati Acked-by: Neal Cardwell Acked-by: Yuchung Cheng Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 ++- include/linux/tcp.h | 1 - include/net/inet_connection_sock.h | 5 +- include/net/tcp.h | 6 +- include/uapi/linux/snmp.h | 1 + net/ipv4/inet_diag.c | 4 +- net/ipv4/proc.c | 1 + net/ipv4/sysctl_net_ipv4.c | 4 +- net/ipv4/tcp_input.c | 24 ++++--- net/ipv4/tcp_ipv4.c | 4 +- net/ipv4/tcp_output.c | 128 +++++++++++++++++++++++++++++++-- net/ipv4/tcp_timer.c | 13 ++-- 12 files changed, 171 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index dc2dc87d255..1cae6c383e1 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -190,7 +190,9 @@ tcp_early_retrans - INTEGER Enable Early Retransmit (ER), per RFC 5827. ER lowers the threshold for triggering fast retransmit when the amount of outstanding data is small and when no previously unsent data can be transmitted (such - that limited transmit could be used). + that limited transmit could be used). Also controls the use of + Tail loss probe (TLP) that converts RTOs occuring due to tail + losses into fast recovery (draft-dukkipati-tcpm-tcp-loss-probe-01). Possible values: 0 disables ER 1 enables ER @@ -198,7 +200,9 @@ tcp_early_retrans - INTEGER by a fourth of RTT. This mitigates connection falsely recovers when network has a small degree of reordering (less than 3 packets). - Default: 2 + 3 enables delayed ER and TLP. + 4 enables TLP only. + Default: 3 tcp_ecn - INTEGER Control use of Explicit Congestion Notification (ECN) by TCP. diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 515c3746b67..01860d74555 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -201,7 +201,6 @@ struct tcp_sock { unused : 1; u8 repair_queue; u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */ - early_retrans_delayed:1, /* Delayed ER timer installed */ syn_data:1, /* SYN includes data */ syn_fastopen:1, /* SYN includes Fast Open option */ syn_data_acked:1;/* data in SYN is acked by SYN-ACK */ diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 183292722f6..de2c78529af 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -133,6 +133,8 @@ struct inet_connection_sock { #define ICSK_TIME_RETRANS 1 /* Retransmit timer */ #define ICSK_TIME_DACK 2 /* Delayed ack timer */ #define ICSK_TIME_PROBE0 3 /* Zero window probe timer */ +#define ICSK_TIME_EARLY_RETRANS 4 /* Early retransmit timer */ +#define ICSK_TIME_LOSS_PROBE 5 /* Tail loss probe timer */ static inline struct inet_connection_sock *inet_csk(const struct sock *sk) { @@ -222,7 +224,8 @@ static inline void inet_csk_reset_xmit_timer(struct sock *sk, const int what, when = max_when; } - if (what == ICSK_TIME_RETRANS || what == ICSK_TIME_PROBE0) { + if (what == ICSK_TIME_RETRANS || what == ICSK_TIME_PROBE0 || + what == ICSK_TIME_EARLY_RETRANS || what == ICSK_TIME_LOSS_PROBE) { icsk->icsk_pending = what; icsk->icsk_timeout = jiffies + when; sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout); diff --git a/include/net/tcp.h b/include/net/tcp.h index a2baa5e4ba3..ab9f947b118 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -543,6 +543,8 @@ extern bool tcp_syn_flood_action(struct sock *sk, extern void tcp_push_one(struct sock *, unsigned int mss_now); extern void tcp_send_ack(struct sock *sk); extern void tcp_send_delayed_ack(struct sock *sk); +extern void tcp_send_loss_probe(struct sock *sk); +extern bool tcp_schedule_loss_probe(struct sock *sk); /* tcp_input.c */ extern void tcp_cwnd_application_limited(struct sock *sk); @@ -873,8 +875,8 @@ static inline void tcp_enable_fack(struct tcp_sock *tp) static inline void tcp_enable_early_retrans(struct tcp_sock *tp) { tp->do_early_retrans = sysctl_tcp_early_retrans && - !sysctl_tcp_thin_dupack && sysctl_tcp_reordering == 3; - tp->early_retrans_delayed = 0; + sysctl_tcp_early_retrans < 4 && !sysctl_tcp_thin_dupack && + sysctl_tcp_reordering == 3; } static inline void tcp_disable_early_retrans(struct tcp_sock *tp) diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index b49eab89c9f..290bed6b085 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -202,6 +202,7 @@ enum LINUX_MIB_TCPFORWARDRETRANS, /* TCPForwardRetrans */ LINUX_MIB_TCPSLOWSTARTRETRANS, /* TCPSlowStartRetrans */ LINUX_MIB_TCPTIMEOUTS, /* TCPTimeouts */ + LINUX_MIB_TCPLOSSPROBES, /* TCPLossProbes */ LINUX_MIB_TCPRENORECOVERYFAIL, /* TCPRenoRecoveryFail */ LINUX_MIB_TCPSACKRECOVERYFAIL, /* TCPSackRecoveryFail */ LINUX_MIB_TCPSCHEDULERFAILED, /* TCPSchedulerFailed */ diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 7afa2c3c788..8620408af57 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -158,7 +158,9 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, #define EXPIRES_IN_MS(tmo) DIV_ROUND_UP((tmo - jiffies) * 1000, HZ) - if (icsk->icsk_pending == ICSK_TIME_RETRANS) { + if (icsk->icsk_pending == ICSK_TIME_RETRANS || + icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || + icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) { r->idiag_timer = 1; r->idiag_retrans = icsk->icsk_retransmits; r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout); diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 32030a24e77..4c35911d935 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -224,6 +224,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPForwardRetrans", LINUX_MIB_TCPFORWARDRETRANS), SNMP_MIB_ITEM("TCPSlowStartRetrans", LINUX_MIB_TCPSLOWSTARTRETRANS), SNMP_MIB_ITEM("TCPTimeouts", LINUX_MIB_TCPTIMEOUTS), + SNMP_MIB_ITEM("TCPLossProbes", LINUX_MIB_TCPLOSSPROBES), SNMP_MIB_ITEM("TCPRenoRecoveryFail", LINUX_MIB_TCPRENORECOVERYFAIL), SNMP_MIB_ITEM("TCPSackRecoveryFail", LINUX_MIB_TCPSACKRECOVERYFAIL), SNMP_MIB_ITEM("TCPSchedulerFailed", LINUX_MIB_TCPSCHEDULERFAILED), diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 960fd29d9b8..cca4550f408 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -28,7 +28,7 @@ static int zero; static int one = 1; -static int two = 2; +static int four = 4; static int tcp_retr1_max = 255; static int ip_local_port_range_min[] = { 1, 1 }; static int ip_local_port_range_max[] = { 65535, 65535 }; @@ -760,7 +760,7 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec_minmax, .extra1 = &zero, - .extra2 = &two, + .extra2 = &four, }, { .procname = "udp_mem", diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 0d9bdacce99..b794f89ac1f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -98,7 +98,7 @@ int sysctl_tcp_frto_response __read_mostly; int sysctl_tcp_thin_dupack __read_mostly; int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; -int sysctl_tcp_early_retrans __read_mostly = 2; +int sysctl_tcp_early_retrans __read_mostly = 3; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ #define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ @@ -2150,15 +2150,16 @@ static bool tcp_pause_early_retransmit(struct sock *sk, int flag) * max(RTT/4, 2msec) unless ack has ECE mark, no RTT samples * available, or RTO is scheduled to fire first. */ - if (sysctl_tcp_early_retrans < 2 || (flag & FLAG_ECE) || !tp->srtt) + if (sysctl_tcp_early_retrans < 2 || sysctl_tcp_early_retrans > 3 || + (flag & FLAG_ECE) || !tp->srtt) return false; delay = max_t(unsigned long, (tp->srtt >> 5), msecs_to_jiffies(2)); if (!time_after(inet_csk(sk)->icsk_timeout, (jiffies + delay))) return false; - inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, delay, TCP_RTO_MAX); - tp->early_retrans_delayed = 1; + inet_csk_reset_xmit_timer(sk, ICSK_TIME_EARLY_RETRANS, delay, + TCP_RTO_MAX); return true; } @@ -2321,7 +2322,7 @@ static bool tcp_time_to_recover(struct sock *sk, int flag) * interval if appropriate. */ if (tp->do_early_retrans && !tp->retrans_out && tp->sacked_out && - (tp->packets_out == (tp->sacked_out + 1) && tp->packets_out < 4) && + (tp->packets_out >= (tp->sacked_out + 1) && tp->packets_out < 4) && !tcp_may_send_now(sk)) return !tcp_pause_early_retransmit(sk, flag); @@ -3081,6 +3082,7 @@ static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) */ void tcp_rearm_rto(struct sock *sk) { + const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); /* If the retrans timer is currently being used by Fast Open @@ -3094,12 +3096,13 @@ void tcp_rearm_rto(struct sock *sk) } else { u32 rto = inet_csk(sk)->icsk_rto; /* Offset the time elapsed after installing regular RTO */ - if (tp->early_retrans_delayed) { + if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || + icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) { struct sk_buff *skb = tcp_write_queue_head(sk); const u32 rto_time_stamp = TCP_SKB_CB(skb)->when + rto; s32 delta = (s32)(rto_time_stamp - tcp_time_stamp); /* delta may not be positive if the socket is locked - * when the delayed ER timer fires and is rescheduled. + * when the retrans timer fires and is rescheduled. */ if (delta > 0) rto = delta; @@ -3107,7 +3110,6 @@ void tcp_rearm_rto(struct sock *sk) inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, rto, TCP_RTO_MAX); } - tp->early_retrans_delayed = 0; } /* This function is called when the delayed ER timer fires. TCP enters @@ -3601,7 +3603,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (after(ack, tp->snd_nxt)) goto invalid_ack; - if (tp->early_retrans_delayed) + if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || + icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) tcp_rearm_rto(sk); if (after(ack, prior_snd_una)) @@ -3678,6 +3681,9 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (dst) dst_confirm(dst); } + + if (icsk->icsk_pending == ICSK_TIME_RETRANS) + tcp_schedule_loss_probe(sk); return 1; no_queue: diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 8cdee120a50..b7ab868c828 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2703,7 +2703,9 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len) __u16 srcp = ntohs(inet->inet_sport); int rx_queue; - if (icsk->icsk_pending == ICSK_TIME_RETRANS) { + if (icsk->icsk_pending == ICSK_TIME_RETRANS || + icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || + icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) { timer_active = 1; timer_expires = icsk->icsk_timeout; } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index e2b4461074d..beb63dbc85f 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -74,6 +74,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, /* Account for new data that has been sent to the network. */ static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) { + struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); unsigned int prior_packets = tp->packets_out; @@ -85,7 +86,8 @@ static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) tp->frto_counter = 3; tp->packets_out += tcp_skb_pcount(skb); - if (!prior_packets || tp->early_retrans_delayed) + if (!prior_packets || icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || + icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) tcp_rearm_rto(sk); } @@ -1959,6 +1961,9 @@ static int tcp_mtu_probe(struct sock *sk) * snd_up-64k-mss .. snd_up cannot be large. However, taking into * account rare use of URG, this is not a big flaw. * + * Send at most one packet when push_one > 0. Temporarily ignore + * cwnd limit to force at most one packet out when push_one == 2. + * Returns true, if no segments are in flight and we have queued segments, * but cannot send anything now because of SWS or another problem. */ @@ -1994,8 +1999,13 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, goto repair; /* Skip network transmission */ cwnd_quota = tcp_cwnd_test(tp, skb); - if (!cwnd_quota) - break; + if (!cwnd_quota) { + if (push_one == 2) + /* Force out a loss probe pkt. */ + cwnd_quota = 1; + else + break; + } if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) break; @@ -2049,10 +2059,120 @@ repair: if (likely(sent_pkts)) { if (tcp_in_cwnd_reduction(sk)) tp->prr_out += sent_pkts; + + /* Send one loss probe per tail loss episode. */ + if (push_one != 2) + tcp_schedule_loss_probe(sk); tcp_cwnd_validate(sk); return false; } - return !tp->packets_out && tcp_send_head(sk); + return (push_one == 2) || (!tp->packets_out && tcp_send_head(sk)); +} + +bool tcp_schedule_loss_probe(struct sock *sk) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); + u32 timeout, tlp_time_stamp, rto_time_stamp; + u32 rtt = tp->srtt >> 3; + + if (WARN_ON(icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS)) + return false; + /* No consecutive loss probes. */ + if (WARN_ON(icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)) { + tcp_rearm_rto(sk); + return false; + } + /* Don't do any loss probe on a Fast Open connection before 3WHS + * finishes. + */ + if (sk->sk_state == TCP_SYN_RECV) + return false; + + /* TLP is only scheduled when next timer event is RTO. */ + if (icsk->icsk_pending != ICSK_TIME_RETRANS) + return false; + + /* Schedule a loss probe in 2*RTT for SACK capable connections + * in Open state, that are either limited by cwnd or application. + */ + if (sysctl_tcp_early_retrans < 3 || !rtt || !tp->packets_out || + !tcp_is_sack(tp) || inet_csk(sk)->icsk_ca_state != TCP_CA_Open) + return false; + + if ((tp->snd_cwnd > tcp_packets_in_flight(tp)) && + tcp_send_head(sk)) + return false; + + /* Probe timeout is at least 1.5*rtt + TCP_DELACK_MAX to account + * for delayed ack when there's one outstanding packet. + */ + timeout = rtt << 1; + if (tp->packets_out == 1) + timeout = max_t(u32, timeout, + (rtt + (rtt >> 1) + TCP_DELACK_MAX)); + timeout = max_t(u32, timeout, msecs_to_jiffies(10)); + + /* If RTO is shorter, just schedule TLP in its place. */ + tlp_time_stamp = tcp_time_stamp + timeout; + rto_time_stamp = (u32)inet_csk(sk)->icsk_timeout; + if ((s32)(tlp_time_stamp - rto_time_stamp) > 0) { + s32 delta = rto_time_stamp - tcp_time_stamp; + if (delta > 0) + timeout = delta; + } + + inet_csk_reset_xmit_timer(sk, ICSK_TIME_LOSS_PROBE, timeout, + TCP_RTO_MAX); + return true; +} + +/* When probe timeout (PTO) fires, send a new segment if one exists, else + * retransmit the last segment. + */ +void tcp_send_loss_probe(struct sock *sk) +{ + struct sk_buff *skb; + int pcount; + int mss = tcp_current_mss(sk); + int err = -1; + + if (tcp_send_head(sk) != NULL) { + err = tcp_write_xmit(sk, mss, TCP_NAGLE_OFF, 2, GFP_ATOMIC); + goto rearm_timer; + } + + /* Retransmit last segment. */ + skb = tcp_write_queue_tail(sk); + if (WARN_ON(!skb)) + goto rearm_timer; + + pcount = tcp_skb_pcount(skb); + if (WARN_ON(!pcount)) + goto rearm_timer; + + if ((pcount > 1) && (skb->len > (pcount - 1) * mss)) { + if (unlikely(tcp_fragment(sk, skb, (pcount - 1) * mss, mss))) + goto rearm_timer; + skb = tcp_write_queue_tail(sk); + } + + if (WARN_ON(!skb || !tcp_skb_pcount(skb))) + goto rearm_timer; + + /* Probe with zero data doesn't trigger fast recovery. */ + if (skb->len > 0) + err = __tcp_retransmit_skb(sk, skb); + +rearm_timer: + inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, + inet_csk(sk)->icsk_rto, + TCP_RTO_MAX); + + if (likely(!err)) + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPLOSSPROBES); + return; } /* Push out any pending frames which were held back due to diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index b78aac30c49..ecd61d54147 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -342,10 +342,6 @@ void tcp_retransmit_timer(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); - if (tp->early_retrans_delayed) { - tcp_resume_early_retransmit(sk); - return; - } if (tp->fastopen_rsk) { WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV && sk->sk_state != TCP_FIN_WAIT1); @@ -495,13 +491,20 @@ void tcp_write_timer_handler(struct sock *sk) } event = icsk->icsk_pending; - icsk->icsk_pending = 0; switch (event) { + case ICSK_TIME_EARLY_RETRANS: + tcp_resume_early_retransmit(sk); + break; + case ICSK_TIME_LOSS_PROBE: + tcp_send_loss_probe(sk); + break; case ICSK_TIME_RETRANS: + icsk->icsk_pending = 0; tcp_retransmit_timer(sk); break; case ICSK_TIME_PROBE0: + icsk->icsk_pending = 0; tcp_probe_timer(sk); break; } -- cgit v1.2.3-70-g09d2 From 9b717a8d245075ffb8e95a2dfb4ee97ce4747457 Mon Sep 17 00:00:00 2001 From: Nandita Dukkipati Date: Mon, 11 Mar 2013 10:00:44 +0000 Subject: tcp: TLP loss detection. This is the second of the TLP patch series; it augments the basic TLP algorithm with a loss detection scheme. This patch implements a mechanism for loss detection when a Tail loss probe retransmission plugs a hole thereby masking packet loss from the sender. The loss detection algorithm relies on counting TLP dupacks as outlined in Sec. 3 of: http://tools.ietf.org/html/draft-dukkipati-tcpm-tcp-loss-probe-01 The basic idea is: Sender keeps track of TLP "episode" upon retransmission of a TLP packet. An episode ends when the sender receives an ACK above the SND.NXT (tracked by tlp_high_seq) at the time of the episode. We want to make sure that before the episode ends the sender receives a "TLP dupack", indicating that the TLP retransmission was unnecessary, so there was no loss/hole that needed plugging. If the sender gets no TLP dupack before the end of the episode, then it reduces ssthresh and the congestion window, because the TLP packet arriving at the receiver probably plugged a hole. Signed-off-by: Nandita Dukkipati Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 + include/uapi/linux/snmp.h | 1 + net/ipv4/proc.c | 1 + net/ipv4/tcp_input.c | 39 +++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_minisocks.c | 1 + net/ipv4/tcp_output.c | 9 +++++++++ net/ipv4/tcp_timer.c | 2 ++ 7 files changed, 54 insertions(+) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 01860d74555..763c108ee03 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -204,6 +204,7 @@ struct tcp_sock { syn_data:1, /* SYN includes data */ syn_fastopen:1, /* SYN includes Fast Open option */ syn_data_acked:1;/* data in SYN is acked by SYN-ACK */ + u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */ /* RTT measurement */ u32 srtt; /* smoothed round trip time << 3 */ diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index 290bed6b085..e00013a1deb 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -203,6 +203,7 @@ enum LINUX_MIB_TCPSLOWSTARTRETRANS, /* TCPSlowStartRetrans */ LINUX_MIB_TCPTIMEOUTS, /* TCPTimeouts */ LINUX_MIB_TCPLOSSPROBES, /* TCPLossProbes */ + LINUX_MIB_TCPLOSSPROBERECOVERY, /* TCPLossProbeRecovery */ LINUX_MIB_TCPRENORECOVERYFAIL, /* TCPRenoRecoveryFail */ LINUX_MIB_TCPSACKRECOVERYFAIL, /* TCPSackRecoveryFail */ LINUX_MIB_TCPSCHEDULERFAILED, /* TCPSchedulerFailed */ diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 4c35911d935..b6f2ea17489 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -225,6 +225,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPSlowStartRetrans", LINUX_MIB_TCPSLOWSTARTRETRANS), SNMP_MIB_ITEM("TCPTimeouts", LINUX_MIB_TCPTIMEOUTS), SNMP_MIB_ITEM("TCPLossProbes", LINUX_MIB_TCPLOSSPROBES), + SNMP_MIB_ITEM("TCPLossProbeRecovery", LINUX_MIB_TCPLOSSPROBERECOVERY), SNMP_MIB_ITEM("TCPRenoRecoveryFail", LINUX_MIB_TCPRENORECOVERYFAIL), SNMP_MIB_ITEM("TCPSackRecoveryFail", LINUX_MIB_TCPSACKRECOVERYFAIL), SNMP_MIB_ITEM("TCPSchedulerFailed", LINUX_MIB_TCPSCHEDULERFAILED), diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index b794f89ac1f..836d74dd018 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2682,6 +2682,7 @@ static void tcp_init_cwnd_reduction(struct sock *sk, const bool set_ssthresh) struct tcp_sock *tp = tcp_sk(sk); tp->high_seq = tp->snd_nxt; + tp->tlp_high_seq = 0; tp->snd_cwnd_cnt = 0; tp->prior_cwnd = tp->snd_cwnd; tp->prr_delivered = 0; @@ -3569,6 +3570,38 @@ static void tcp_send_challenge_ack(struct sock *sk) } } +/* This routine deals with acks during a TLP episode. + * Ref: loss detection algorithm in draft-dukkipati-tcpm-tcp-loss-probe. + */ +static void tcp_process_tlp_ack(struct sock *sk, u32 ack, int flag) +{ + struct tcp_sock *tp = tcp_sk(sk); + bool is_tlp_dupack = (ack == tp->tlp_high_seq) && + !(flag & (FLAG_SND_UNA_ADVANCED | + FLAG_NOT_DUP | FLAG_DATA_SACKED)); + + /* Mark the end of TLP episode on receiving TLP dupack or when + * ack is after tlp_high_seq. + */ + if (is_tlp_dupack) { + tp->tlp_high_seq = 0; + return; + } + + if (after(ack, tp->tlp_high_seq)) { + tp->tlp_high_seq = 0; + /* Don't reduce cwnd if DSACK arrives for TLP retrans. */ + if (!(flag & FLAG_DSACKING_ACK)) { + tcp_init_cwnd_reduction(sk, true); + tcp_set_ca_state(sk, TCP_CA_CWR); + tcp_end_cwnd_reduction(sk); + tcp_set_ca_state(sk, TCP_CA_Open); + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPLOSSPROBERECOVERY); + } + } +} + /* This routine deals with incoming acks, but not outgoing ones. */ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) { @@ -3676,6 +3709,9 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) tcp_cong_avoid(sk, ack, prior_in_flight); } + if (tp->tlp_high_seq) + tcp_process_tlp_ack(sk, ack, flag); + if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP)) { struct dst_entry *dst = __sk_dst_get(sk); if (dst) @@ -3697,6 +3733,9 @@ no_queue: */ if (tcp_send_head(sk)) tcp_ack_probe(sk); + + if (tp->tlp_high_seq) + tcp_process_tlp_ack(sk, ack, flag); return 1; invalid_ack: diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index b83a49cc381..4bdb09fca40 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -440,6 +440,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->fackets_out = 0; newtp->snd_ssthresh = TCP_INFINITE_SSTHRESH; tcp_enable_early_retrans(newtp); + newtp->tlp_high_seq = 0; /* So many TCP implementations out there (incorrectly) count the * initial SYN frame in their delayed-ACK and congestion control diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index beb63dbc85f..8e7742f0b5d 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2132,6 +2132,7 @@ bool tcp_schedule_loss_probe(struct sock *sk) */ void tcp_send_loss_probe(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; int pcount; int mss = tcp_current_mss(sk); @@ -2142,6 +2143,10 @@ void tcp_send_loss_probe(struct sock *sk) goto rearm_timer; } + /* At most one outstanding TLP retransmission. */ + if (tp->tlp_high_seq) + goto rearm_timer; + /* Retransmit last segment. */ skb = tcp_write_queue_tail(sk); if (WARN_ON(!skb)) @@ -2164,6 +2169,10 @@ void tcp_send_loss_probe(struct sock *sk) if (skb->len > 0) err = __tcp_retransmit_skb(sk, skb); + /* Record snd_nxt for loss detection. */ + if (likely(!err)) + tp->tlp_high_seq = tp->snd_nxt; + rearm_timer: inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, inet_csk(sk)->icsk_rto, diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index ecd61d54147..eeccf795e91 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -356,6 +356,8 @@ void tcp_retransmit_timer(struct sock *sk) WARN_ON(tcp_write_queue_empty(sk)); + tp->tlp_high_seq = 0; + if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) && !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) { /* Receiver dastardly shrinks window. Our retransmits -- cgit v1.2.3-70-g09d2 From 06a3307975aac2d5b5a0e0f2e05d23e769f176b4 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 12 Mar 2013 10:26:45 +0200 Subject: videomode: combine videomode dmt_flags and data_flags Both videomode and display_timing contain flags describing the modes. These are stored in dmt_flags and data_flags. There's no need to separate these flags, and having separate fields just makes the flags more difficult to use. This patch combines the fields and renames VESA_DMT_* flags to DISPLAY_FLAGS_*. Signed-off-by: Tomi Valkeinen Cc: Steffen Trumtrar --- drivers/gpu/drm/drm_modes.c | 12 ++++++------ drivers/video/fbmon.c | 8 ++++---- drivers/video/of_display_timing.c | 19 +++++++++---------- drivers/video/videomode.c | 3 +-- include/video/display_timing.h | 26 +++++++++++--------------- include/video/videomode.h | 3 +-- 6 files changed, 32 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 0698c0e9bc2..f83f0719922 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -523,17 +523,17 @@ int drm_display_mode_from_videomode(const struct videomode *vm, dmode->clock = vm->pixelclock / 1000; dmode->flags = 0; - if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH) + if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) dmode->flags |= DRM_MODE_FLAG_PHSYNC; - else if (vm->dmt_flags & VESA_DMT_HSYNC_LOW) + else if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW) dmode->flags |= DRM_MODE_FLAG_NHSYNC; - if (vm->dmt_flags & VESA_DMT_VSYNC_HIGH) + if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH) dmode->flags |= DRM_MODE_FLAG_PVSYNC; - else if (vm->dmt_flags & VESA_DMT_VSYNC_LOW) + else if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW) dmode->flags |= DRM_MODE_FLAG_NVSYNC; - if (vm->data_flags & DISPLAY_FLAGS_INTERLACED) + if (vm->flags & DISPLAY_FLAGS_INTERLACED) dmode->flags |= DRM_MODE_FLAG_INTERLACE; - if (vm->data_flags & DISPLAY_FLAGS_DOUBLESCAN) + if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN) dmode->flags |= DRM_MODE_FLAG_DBLSCAN; drm_mode_set_name(dmode); diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 368cedfeaf1..e5cc2fdb4c8 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -1398,13 +1398,13 @@ int fb_videomode_from_videomode(const struct videomode *vm, fbmode->sync = 0; fbmode->vmode = 0; - if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH) + if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) fbmode->sync |= FB_SYNC_HOR_HIGH_ACT; - if (vm->dmt_flags & VESA_DMT_HSYNC_HIGH) + if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH) fbmode->sync |= FB_SYNC_VERT_HIGH_ACT; - if (vm->data_flags & DISPLAY_FLAGS_INTERLACED) + if (vm->flags & DISPLAY_FLAGS_INTERLACED) fbmode->vmode |= FB_VMODE_INTERLACED; - if (vm->data_flags & DISPLAY_FLAGS_DOUBLESCAN) + if (vm->flags & DISPLAY_FLAGS_DOUBLESCAN) fbmode->vmode |= FB_VMODE_DOUBLE; fbmode->flag = 0; diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c index 13ecd989701..56009bc02b0 100644 --- a/drivers/video/of_display_timing.c +++ b/drivers/video/of_display_timing.c @@ -79,25 +79,24 @@ static struct display_timing *of_get_display_timing(struct device_node *np) ret |= parse_timing_property(np, "vsync-len", &dt->vsync_len); ret |= parse_timing_property(np, "clock-frequency", &dt->pixelclock); - dt->dmt_flags = 0; - dt->data_flags = 0; + dt->flags = 0; if (!of_property_read_u32(np, "vsync-active", &val)) - dt->dmt_flags |= val ? VESA_DMT_VSYNC_HIGH : - VESA_DMT_VSYNC_LOW; + dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH : + DISPLAY_FLAGS_VSYNC_LOW; if (!of_property_read_u32(np, "hsync-active", &val)) - dt->dmt_flags |= val ? VESA_DMT_HSYNC_HIGH : - VESA_DMT_HSYNC_LOW; + dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH : + DISPLAY_FLAGS_HSYNC_LOW; if (!of_property_read_u32(np, "de-active", &val)) - dt->data_flags |= val ? DISPLAY_FLAGS_DE_HIGH : + dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH : DISPLAY_FLAGS_DE_LOW; if (!of_property_read_u32(np, "pixelclk-active", &val)) - dt->data_flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : + dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE : DISPLAY_FLAGS_PIXDATA_NEGEDGE; if (of_property_read_bool(np, "interlaced")) - dt->data_flags |= DISPLAY_FLAGS_INTERLACED; + dt->flags |= DISPLAY_FLAGS_INTERLACED; if (of_property_read_bool(np, "doublescan")) - dt->data_flags |= DISPLAY_FLAGS_DOUBLESCAN; + dt->flags |= DISPLAY_FLAGS_DOUBLESCAN; if (ret) { pr_err("%s: error reading timing properties\n", diff --git a/drivers/video/videomode.c b/drivers/video/videomode.c index 21c47a202af..810afff79bc 100644 --- a/drivers/video/videomode.c +++ b/drivers/video/videomode.c @@ -31,8 +31,7 @@ int videomode_from_timing(const struct display_timings *disp, vm->vback_porch = display_timing_get_value(&dt->vback_porch, TE_TYP); vm->vsync_len = display_timing_get_value(&dt->vsync_len, TE_TYP); - vm->dmt_flags = dt->dmt_flags; - vm->data_flags = dt->data_flags; + vm->flags = dt->flags; return 0; } diff --git a/include/video/display_timing.h b/include/video/display_timing.h index 71e9a383a98..a8a4be5b0af 100644 --- a/include/video/display_timing.h +++ b/include/video/display_timing.h @@ -12,19 +12,16 @@ #include #include -/* VESA display monitor timing parameters */ -#define VESA_DMT_HSYNC_LOW BIT(0) -#define VESA_DMT_HSYNC_HIGH BIT(1) -#define VESA_DMT_VSYNC_LOW BIT(2) -#define VESA_DMT_VSYNC_HIGH BIT(3) - -/* display specific flags */ -#define DISPLAY_FLAGS_DE_LOW BIT(0) /* data enable flag */ -#define DISPLAY_FLAGS_DE_HIGH BIT(1) -#define DISPLAY_FLAGS_PIXDATA_POSEDGE BIT(2) /* drive data on pos. edge */ -#define DISPLAY_FLAGS_PIXDATA_NEGEDGE BIT(3) /* drive data on neg. edge */ -#define DISPLAY_FLAGS_INTERLACED BIT(4) -#define DISPLAY_FLAGS_DOUBLESCAN BIT(5) +#define DISPLAY_FLAGS_HSYNC_LOW BIT(0) +#define DISPLAY_FLAGS_HSYNC_HIGH BIT(1) +#define DISPLAY_FLAGS_VSYNC_LOW BIT(2) +#define DISPLAY_FLAGS_VSYNC_HIGH BIT(3) +#define DISPLAY_FLAGS_DE_LOW BIT(4) /* data enable flag */ +#define DISPLAY_FLAGS_DE_HIGH BIT(5) +#define DISPLAY_FLAGS_PIXDATA_POSEDGE BIT(6) /* drive data on pos. edge */ +#define DISPLAY_FLAGS_PIXDATA_NEGEDGE BIT(7) /* drive data on neg. edge */ +#define DISPLAY_FLAGS_INTERLACED BIT(8) +#define DISPLAY_FLAGS_DOUBLESCAN BIT(9) /* * A single signal can be specified via a range of minimal and maximal values @@ -72,8 +69,7 @@ struct display_timing { struct timing_entry vback_porch; /* ver. back porch */ struct timing_entry vsync_len; /* ver. sync len */ - unsigned int dmt_flags; /* VESA DMT flags */ - unsigned int data_flags; /* video data flags */ + unsigned int flags; /* display flags */ }; /* diff --git a/include/video/videomode.h b/include/video/videomode.h index a42156234dd..f4ae6edfeb0 100644 --- a/include/video/videomode.h +++ b/include/video/videomode.h @@ -29,8 +29,7 @@ struct videomode { u32 vback_porch; u32 vsync_len; - unsigned int dmt_flags; /* VESA DMT flags */ - unsigned int data_flags; /* video data flags */ + unsigned int flags; /* display flags */ }; /** -- cgit v1.2.3-70-g09d2 From 32ed6ef133f95f960d19e3f3b30246aebf8ecd36 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 12 Mar 2013 10:31:29 +0200 Subject: videomode: create enum for videomode's display flags Instead of having plain defines for the videomode's flags, add an enum for the flags. This makes the flags clearer to use, as the enum tells which values can be used with the flags field. Signed-off-by: Tomi Valkeinen Cc: Steffen Trumtrar --- include/video/display_timing.h | 28 +++++++++++++++++----------- include/video/videomode.h | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/video/display_timing.h b/include/video/display_timing.h index a8a4be5b0af..b63471d1409 100644 --- a/include/video/display_timing.h +++ b/include/video/display_timing.h @@ -12,16 +12,22 @@ #include #include -#define DISPLAY_FLAGS_HSYNC_LOW BIT(0) -#define DISPLAY_FLAGS_HSYNC_HIGH BIT(1) -#define DISPLAY_FLAGS_VSYNC_LOW BIT(2) -#define DISPLAY_FLAGS_VSYNC_HIGH BIT(3) -#define DISPLAY_FLAGS_DE_LOW BIT(4) /* data enable flag */ -#define DISPLAY_FLAGS_DE_HIGH BIT(5) -#define DISPLAY_FLAGS_PIXDATA_POSEDGE BIT(6) /* drive data on pos. edge */ -#define DISPLAY_FLAGS_PIXDATA_NEGEDGE BIT(7) /* drive data on neg. edge */ -#define DISPLAY_FLAGS_INTERLACED BIT(8) -#define DISPLAY_FLAGS_DOUBLESCAN BIT(9) +enum display_flags { + DISPLAY_FLAGS_HSYNC_LOW = BIT(0), + DISPLAY_FLAGS_HSYNC_HIGH = BIT(1), + DISPLAY_FLAGS_VSYNC_LOW = BIT(2), + DISPLAY_FLAGS_VSYNC_HIGH = BIT(3), + + /* data enable flag */ + DISPLAY_FLAGS_DE_LOW = BIT(4), + DISPLAY_FLAGS_DE_HIGH = BIT(5), + /* drive data on pos. edge */ + DISPLAY_FLAGS_PIXDATA_POSEDGE = BIT(6), + /* drive data on neg. edge */ + DISPLAY_FLAGS_PIXDATA_NEGEDGE = BIT(7), + DISPLAY_FLAGS_INTERLACED = BIT(8), + DISPLAY_FLAGS_DOUBLESCAN = BIT(9), +}; /* * A single signal can be specified via a range of minimal and maximal values @@ -69,7 +75,7 @@ struct display_timing { struct timing_entry vback_porch; /* ver. back porch */ struct timing_entry vsync_len; /* ver. sync len */ - unsigned int flags; /* display flags */ + enum display_flags flags; /* display flags */ }; /* diff --git a/include/video/videomode.h b/include/video/videomode.h index f4ae6edfeb0..8b12e60b617 100644 --- a/include/video/videomode.h +++ b/include/video/videomode.h @@ -29,7 +29,7 @@ struct videomode { u32 vback_porch; u32 vsync_len; - unsigned int flags; /* display flags */ + enum display_flags flags; /* display flags */ }; /** -- cgit v1.2.3-70-g09d2 From 694f050650798b82f2c7b9983e80117d58b34bf3 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 12 Mar 2013 10:35:16 +0200 Subject: videomode: remove timing_entry_index Display timing's fields have minimum, typical and maximum values. These can be accessed by using timing_entry_index enum, and display_timing_get_value() function. There's no real need for this extra complexity. The values can be accessed more easily by just using the min/typ/max fields. Signed-off-by: Tomi Valkeinen Cc: Steffen Trumtrar --- drivers/video/videomode.c | 18 +++++++++--------- include/video/display_timing.h | 25 ------------------------- 2 files changed, 9 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/video/videomode.c b/drivers/video/videomode.c index 810afff79bc..a3d95f263cd 100644 --- a/drivers/video/videomode.c +++ b/drivers/video/videomode.c @@ -20,16 +20,16 @@ int videomode_from_timing(const struct display_timings *disp, if (!dt) return -EINVAL; - vm->pixelclock = display_timing_get_value(&dt->pixelclock, TE_TYP); - vm->hactive = display_timing_get_value(&dt->hactive, TE_TYP); - vm->hfront_porch = display_timing_get_value(&dt->hfront_porch, TE_TYP); - vm->hback_porch = display_timing_get_value(&dt->hback_porch, TE_TYP); - vm->hsync_len = display_timing_get_value(&dt->hsync_len, TE_TYP); + vm->pixelclock = dt->pixelclock.typ; + vm->hactive = dt->hactive.typ; + vm->hfront_porch = dt->hfront_porch.typ; + vm->hback_porch = dt->hback_porch.typ; + vm->hsync_len = dt->hsync_len.typ; - vm->vactive = display_timing_get_value(&dt->vactive, TE_TYP); - vm->vfront_porch = display_timing_get_value(&dt->vfront_porch, TE_TYP); - vm->vback_porch = display_timing_get_value(&dt->vback_porch, TE_TYP); - vm->vsync_len = display_timing_get_value(&dt->vsync_len, TE_TYP); + vm->vactive = dt->vactive.typ; + vm->vfront_porch = dt->vfront_porch.typ; + vm->vback_porch = dt->vback_porch.typ; + vm->vsync_len = dt->vsync_len.typ; vm->flags = dt->flags; diff --git a/include/video/display_timing.h b/include/video/display_timing.h index b63471d1409..5d0259b08e0 100644 --- a/include/video/display_timing.h +++ b/include/video/display_timing.h @@ -39,12 +39,6 @@ struct timing_entry { u32 max; }; -enum timing_entry_index { - TE_MIN = 0, - TE_TYP = 1, - TE_MAX = 2, -}; - /* * Single "mode" entry. This describes one set of signal timings a display can * have in one setting. This struct can later be converted to struct videomode @@ -91,25 +85,6 @@ struct display_timings { struct display_timing **timings; }; -/* get value specified by index from struct timing_entry */ -static inline u32 display_timing_get_value(const struct timing_entry *te, - enum timing_entry_index index) -{ - switch (index) { - case TE_MIN: - return te->min; - break; - case TE_TYP: - return te->typ; - break; - case TE_MAX: - return te->max; - break; - default: - return te->typ; - } -} - /* get one entry from struct display_timings */ static inline struct display_timing *display_timings_get(const struct display_timings *disp, -- cgit v1.2.3-70-g09d2 From 42e836eb4527fb635cb799a701fe4c9fe741c03a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 11 Mar 2013 13:56:44 +0000 Subject: phy: add set_wol/get_wol functions This allows ethernet drivers (such as the mv643xx_eth) to support Wake on LAN on platforms where PHY registers have to be configured for Wake on LAN (e.g. the Marvell Kirkwood based qnap TS-119P II). Signed-off-by: Michael Stapelberg Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 16 ++++++++++++++++ include/linux/phy.h | 10 ++++++++++ 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ef9ea924822..298b4c20173 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1188,3 +1188,19 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) return 0; } EXPORT_SYMBOL(phy_ethtool_set_eee); + +int phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + if (phydev->drv->set_wol) + return phydev->drv->set_wol(phydev, wol); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(phy_ethtool_set_wol); + +void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + if (phydev->drv->get_wol) + phydev->drv->get_wol(phydev, wol); +} +EXPORT_SYMBOL(phy_ethtool_get_wol); diff --git a/include/linux/phy.h b/include/linux/phy.h index 33999adbf8c..9e11039dd7a 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -455,6 +455,14 @@ struct phy_driver { */ void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb, int type); + /* Some devices (e.g. qnap TS-119P II) require PHY register changes to + * enable Wake on LAN, so set_wol is provided to be called in the + * ethernet driver's set_wol function. */ + int (*set_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol); + + /* See set_wol, but for checking whether Wake on LAN is enabled. */ + void (*get_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol); + struct device_driver driver; }; #define to_phy_driver(d) container_of(d, struct phy_driver, driver) @@ -560,6 +568,8 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable); int phy_get_eee_err(struct phy_device *phydev); int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data); int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data); +int phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol); +void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol); int __init mdio_bus_init(void); void mdio_bus_exit(void); -- cgit v1.2.3-70-g09d2 From 5a1bbf21325bd4f2641f6141fb8c47f6095578dd Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 2 Feb 2013 11:53:47 -0800 Subject: Input: add new keycodes for passenger control units Entertainment systems used in aircraft need additional keycodes for their Passenger Control Units, so let's add them. Signed-off-by: Dmitry Torokhov --- include/uapi/linux/input.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h index 558828590a6..6e4e3c6b396 100644 --- a/include/uapi/linux/input.h +++ b/include/uapi/linux/input.h @@ -702,6 +702,11 @@ struct input_keymap_entry { #define KEY_CAMERA_LEFT 0x219 #define KEY_CAMERA_RIGHT 0x21a +#define KEY_ATTENDANT_ON 0x21b +#define KEY_ATTENDANT_OFF 0x21c +#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */ +#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */ + #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 -- cgit v1.2.3-70-g09d2 From d84ff0512f1bfc0d8c864efadb4523fce68919cc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 12 Mar 2013 11:29:59 -0700 Subject: workqueue: consistently use int for @cpu variables Workqueue is mixing unsigned int and int for @cpu variables. There's no point in using unsigned int for cpus - many of cpu related APIs take int anyway. Consistently use int for @cpu variables so that we can use negative values to mark special ones. This patch doesn't introduce any visible behavior changes. Signed-off-by: Tejun Heo Reviewed-by: Lai Jiangshan --- include/linux/workqueue.h | 6 +++--- kernel/workqueue.c | 24 +++++++++++------------- kernel/workqueue_internal.h | 5 ++--- 3 files changed, 16 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 5bd030f630a..899be6636d2 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -435,7 +435,7 @@ extern bool cancel_delayed_work_sync(struct delayed_work *dwork); extern void workqueue_set_max_active(struct workqueue_struct *wq, int max_active); -extern bool workqueue_congested(unsigned int cpu, struct workqueue_struct *wq); +extern bool workqueue_congested(int cpu, struct workqueue_struct *wq); extern unsigned int work_busy(struct work_struct *work); /* @@ -466,12 +466,12 @@ static inline bool __deprecated flush_delayed_work_sync(struct delayed_work *dwo } #ifndef CONFIG_SMP -static inline long work_on_cpu(unsigned int cpu, long (*fn)(void *), void *arg) +static inline long work_on_cpu(int cpu, long (*fn)(void *), void *arg) { return fn(arg); } #else -long work_on_cpu(unsigned int cpu, long (*fn)(void *), void *arg); +long work_on_cpu(int cpu, long (*fn)(void *), void *arg); #endif /* CONFIG_SMP */ #ifdef CONFIG_FREEZER diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 26c67c76b6c..73c5f68065b 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -124,7 +124,7 @@ enum { struct worker_pool { spinlock_t lock; /* the pool lock */ - unsigned int cpu; /* I: the associated cpu */ + int cpu; /* I: the associated cpu */ int id; /* I: pool ID */ unsigned int flags; /* X: flags */ @@ -467,8 +467,7 @@ static struct worker_pool *get_std_worker_pool(int cpu, bool highpri) return &pools[highpri]; } -static struct pool_workqueue *get_pwq(unsigned int cpu, - struct workqueue_struct *wq) +static struct pool_workqueue *get_pwq(int cpu, struct workqueue_struct *wq) { if (!(wq->flags & WQ_UNBOUND)) { if (likely(cpu < nr_cpu_ids)) @@ -730,7 +729,7 @@ static void wake_up_worker(struct worker_pool *pool) * CONTEXT: * spin_lock_irq(rq->lock) */ -void wq_worker_waking_up(struct task_struct *task, unsigned int cpu) +void wq_worker_waking_up(struct task_struct *task, int cpu) { struct worker *worker = kthread_data(task); @@ -755,8 +754,7 @@ void wq_worker_waking_up(struct task_struct *task, unsigned int cpu) * RETURNS: * Worker task on @cpu to wake up, %NULL if none. */ -struct task_struct *wq_worker_sleeping(struct task_struct *task, - unsigned int cpu) +struct task_struct *wq_worker_sleeping(struct task_struct *task, int cpu) { struct worker *worker = kthread_data(task), *to_wakeup = NULL; struct worker_pool *pool; @@ -1159,7 +1157,7 @@ static bool is_chained_work(struct workqueue_struct *wq) return worker && worker->current_pwq->wq == wq; } -static void __queue_work(unsigned int cpu, struct workqueue_struct *wq, +static void __queue_work(int cpu, struct workqueue_struct *wq, struct work_struct *work) { struct pool_workqueue *pwq; @@ -1714,7 +1712,7 @@ static struct worker *create_worker(struct worker_pool *pool) if (pool->cpu != WORK_CPU_UNBOUND) worker->task = kthread_create_on_node(worker_thread, worker, cpu_to_node(pool->cpu), - "kworker/%u:%d%s", pool->cpu, id, pri); + "kworker/%d:%d%s", pool->cpu, id, pri); else worker->task = kthread_create(worker_thread, worker, "kworker/u:%d%s", id, pri); @@ -3345,7 +3343,7 @@ EXPORT_SYMBOL_GPL(workqueue_set_max_active); * RETURNS: * %true if congested, %false otherwise. */ -bool workqueue_congested(unsigned int cpu, struct workqueue_struct *wq) +bool workqueue_congested(int cpu, struct workqueue_struct *wq) { struct pool_workqueue *pwq = get_pwq(cpu, wq); @@ -3461,7 +3459,7 @@ static int __cpuinit workqueue_cpu_up_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { - unsigned int cpu = (unsigned long)hcpu; + int cpu = (unsigned long)hcpu; struct worker_pool *pool; switch (action & ~CPU_TASKS_FROZEN) { @@ -3507,7 +3505,7 @@ static int __cpuinit workqueue_cpu_down_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { - unsigned int cpu = (unsigned long)hcpu; + int cpu = (unsigned long)hcpu; struct work_struct unbind_work; switch (action & ~CPU_TASKS_FROZEN) { @@ -3547,7 +3545,7 @@ static void work_for_cpu_fn(struct work_struct *work) * It is up to the caller to ensure that the cpu doesn't go offline. * The caller must not hold any locks which would prevent @fn from completing. */ -long work_on_cpu(unsigned int cpu, long (*fn)(void *), void *arg) +long work_on_cpu(int cpu, long (*fn)(void *), void *arg) { struct work_for_cpu wfc = { .fn = fn, .arg = arg }; @@ -3705,7 +3703,7 @@ out_unlock: static int __init init_workqueues(void) { - unsigned int cpu; + int cpu; /* make sure we have enough bits for OFFQ pool ID */ BUILD_BUG_ON((1LU << (BITS_PER_LONG - WORK_OFFQ_POOL_SHIFT)) < diff --git a/kernel/workqueue_internal.h b/kernel/workqueue_internal.h index f9c887731e2..f116f071d91 100644 --- a/kernel/workqueue_internal.h +++ b/kernel/workqueue_internal.h @@ -59,8 +59,7 @@ static inline struct worker *current_wq_worker(void) * Scheduler hooks for concurrency managed workqueue. Only to be used from * sched.c and workqueue.c. */ -void wq_worker_waking_up(struct task_struct *task, unsigned int cpu); -struct task_struct *wq_worker_sleeping(struct task_struct *task, - unsigned int cpu); +void wq_worker_waking_up(struct task_struct *task, int cpu); +struct task_struct *wq_worker_sleeping(struct task_struct *task, int cpu); #endif /* _KERNEL_WORKQUEUE_INTERNAL_H */ -- cgit v1.2.3-70-g09d2 From 7a4e344c5675eefbde93ed9a98ef45e0e4957bc2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 12 Mar 2013 11:30:00 -0700 Subject: workqueue: introduce workqueue_attrs Introduce struct workqueue_attrs which carries worker attributes - currently the nice level and allowed cpumask along with helper routines alloc_workqueue_attrs() and free_workqueue_attrs(). Each worker_pool now carries ->attrs describing the attributes of its workers. All functions dealing with cpumask and nice level of workers are updated to follow worker_pool->attrs instead of determining them from other characteristics of the worker_pool, and init_workqueues() is updated to set worker_pool->attrs appropriately for all standard pools. Note that create_worker() is updated to always perform set_user_nice() and use set_cpus_allowed_ptr() combined with manual assertion of PF_THREAD_BOUND instead of kthread_bind(). This simplifies handling random attributes without affecting the outcome. This patch doesn't introduce any behavior changes. v2: Missing cpumask_var_t definition caused build failure on some archs. linux/cpumask.h included. Signed-off-by: Tejun Heo Reported-by: kbuild test robot Reviewed-by: Lai Jiangshan --- include/linux/workqueue.h | 13 ++++++ kernel/workqueue.c | 103 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 94 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 899be6636d2..00c1b9ba825 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -11,6 +11,7 @@ #include #include #include +#include struct workqueue_struct; @@ -115,6 +116,15 @@ struct delayed_work { int cpu; }; +/* + * A struct for workqueue attributes. This can be used to change + * attributes of an unbound workqueue. + */ +struct workqueue_attrs { + int nice; /* nice level */ + cpumask_var_t cpumask; /* allowed CPUs */ +}; + static inline struct delayed_work *to_delayed_work(struct work_struct *work) { return container_of(work, struct delayed_work, work); @@ -399,6 +409,9 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, extern void destroy_workqueue(struct workqueue_struct *wq); +struct workqueue_attrs *alloc_workqueue_attrs(gfp_t gfp_mask); +void free_workqueue_attrs(struct workqueue_attrs *attrs); + extern bool queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work); extern bool queue_work(struct workqueue_struct *wq, struct work_struct *work); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 094f16668e1..b0d3cbb83f6 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -148,6 +148,8 @@ struct worker_pool { struct mutex assoc_mutex; /* protect POOL_DISASSOCIATED */ struct ida worker_ida; /* L: for worker IDs */ + struct workqueue_attrs *attrs; /* I: worker attributes */ + /* * The current concurrency level. As it's likely to be accessed * from other CPUs during try_to_wake_up(), put it in a separate @@ -1566,14 +1568,13 @@ __acquires(&pool->lock) * against POOL_DISASSOCIATED. */ if (!(pool->flags & POOL_DISASSOCIATED)) - set_cpus_allowed_ptr(current, get_cpu_mask(pool->cpu)); + set_cpus_allowed_ptr(current, pool->attrs->cpumask); spin_lock_irq(&pool->lock); if (pool->flags & POOL_DISASSOCIATED) return false; if (task_cpu(current) == pool->cpu && - cpumask_equal(¤t->cpus_allowed, - get_cpu_mask(pool->cpu))) + cpumask_equal(¤t->cpus_allowed, pool->attrs->cpumask)) return true; spin_unlock_irq(&pool->lock); @@ -1679,7 +1680,7 @@ static void rebind_workers(struct worker_pool *pool) * wq doesn't really matter but let's keep @worker->pool * and @pwq->pool consistent for sanity. */ - if (std_worker_pool_pri(worker->pool)) + if (worker->pool->attrs->nice < 0) wq = system_highpri_wq; else wq = system_wq; @@ -1721,7 +1722,7 @@ static struct worker *alloc_worker(void) */ static struct worker *create_worker(struct worker_pool *pool) { - const char *pri = std_worker_pool_pri(pool) ? "H" : ""; + const char *pri = pool->attrs->nice < 0 ? "H" : ""; struct worker *worker = NULL; int id = -1; @@ -1751,24 +1752,23 @@ static struct worker *create_worker(struct worker_pool *pool) if (IS_ERR(worker->task)) goto fail; - if (std_worker_pool_pri(pool)) - set_user_nice(worker->task, HIGHPRI_NICE_LEVEL); + set_user_nice(worker->task, pool->attrs->nice); + set_cpus_allowed_ptr(worker->task, pool->attrs->cpumask); /* - * Determine CPU binding of the new worker depending on - * %POOL_DISASSOCIATED. The caller is responsible for ensuring the - * flag remains stable across this function. See the comments - * above the flag definition for details. - * - * As an unbound worker may later become a regular one if CPU comes - * online, make sure every worker has %PF_THREAD_BOUND set. + * %PF_THREAD_BOUND is used to prevent userland from meddling with + * cpumask of workqueue workers. This is an abuse. We need + * %PF_NO_SETAFFINITY. */ - if (!(pool->flags & POOL_DISASSOCIATED)) { - kthread_bind(worker->task, pool->cpu); - } else { - worker->task->flags |= PF_THREAD_BOUND; + worker->task->flags |= PF_THREAD_BOUND; + + /* + * The caller is responsible for ensuring %POOL_DISASSOCIATED + * remains stable across this function. See the comments above the + * flag definition for details. + */ + if (pool->flags & POOL_DISASSOCIATED) worker->flags |= WORKER_UNBOUND; - } return worker; fail: @@ -3123,7 +3123,52 @@ int keventd_up(void) return system_wq != NULL; } -static void init_worker_pool(struct worker_pool *pool) +/** + * free_workqueue_attrs - free a workqueue_attrs + * @attrs: workqueue_attrs to free + * + * Undo alloc_workqueue_attrs(). + */ +void free_workqueue_attrs(struct workqueue_attrs *attrs) +{ + if (attrs) { + free_cpumask_var(attrs->cpumask); + kfree(attrs); + } +} + +/** + * alloc_workqueue_attrs - allocate a workqueue_attrs + * @gfp_mask: allocation mask to use + * + * Allocate a new workqueue_attrs, initialize with default settings and + * return it. Returns NULL on failure. + */ +struct workqueue_attrs *alloc_workqueue_attrs(gfp_t gfp_mask) +{ + struct workqueue_attrs *attrs; + + attrs = kzalloc(sizeof(*attrs), gfp_mask); + if (!attrs) + goto fail; + if (!alloc_cpumask_var(&attrs->cpumask, gfp_mask)) + goto fail; + + cpumask_setall(attrs->cpumask); + return attrs; +fail: + free_workqueue_attrs(attrs); + return NULL; +} + +/** + * init_worker_pool - initialize a newly zalloc'd worker_pool + * @pool: worker_pool to initialize + * + * Initiailize a newly zalloc'd @pool. It also allocates @pool->attrs. + * Returns 0 on success, -errno on failure. + */ +static int init_worker_pool(struct worker_pool *pool) { spin_lock_init(&pool->lock); pool->flags |= POOL_DISASSOCIATED; @@ -3141,6 +3186,11 @@ static void init_worker_pool(struct worker_pool *pool) mutex_init(&pool->manager_arb); mutex_init(&pool->assoc_mutex); ida_init(&pool->worker_ida); + + pool->attrs = alloc_workqueue_attrs(GFP_KERNEL); + if (!pool->attrs) + return -ENOMEM; + return 0; } static int alloc_and_link_pwqs(struct workqueue_struct *wq) @@ -3792,7 +3842,8 @@ out_unlock: static int __init init_workqueues(void) { - int cpu; + int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL }; + int i, cpu; /* make sure we have enough bits for OFFQ pool ID */ BUILD_BUG_ON((1LU << (BITS_PER_LONG - WORK_OFFQ_POOL_SHIFT)) < @@ -3809,10 +3860,18 @@ static int __init init_workqueues(void) for_each_wq_cpu(cpu) { struct worker_pool *pool; + i = 0; for_each_std_worker_pool(pool, cpu) { - init_worker_pool(pool); + BUG_ON(init_worker_pool(pool)); pool->cpu = cpu; + if (cpu != WORK_CPU_UNBOUND) + cpumask_copy(pool->attrs->cpumask, cpumask_of(cpu)); + else + cpumask_setall(pool->attrs->cpumask); + + pool->attrs->nice = std_nice[i++]; + /* alloc pool ID */ BUG_ON(worker_pool_assign_id(pool)); } -- cgit v1.2.3-70-g09d2 From 493008a8e475771a2126e0ce95a73e35b371d277 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 12 Mar 2013 11:30:03 -0700 Subject: workqueue: drop WQ_RESCUER and test workqueue->rescuer for NULL instead WQ_RESCUER is superflous. WQ_MEM_RECLAIM indicates that the user wants a rescuer and testing wq->rescuer for NULL can answer whether a given workqueue has a rescuer or not. Drop WQ_RESCUER and test wq->rescuer directly. This will help simplifying __alloc_workqueue_key() failure path by allowing it to use destroy_workqueue() on a partially constructed workqueue, which in turn will help implementing dynamic management of pool_workqueues. While at it, clear wq->rescuer after freeing it in destroy_workqueue(). This is a precaution as scheduled changes will make destruction more complex. This patch doesn't introduce any functional changes. Signed-off-by: Tejun Heo Reviewed-by: Lai Jiangshan --- include/linux/workqueue.h | 1 - kernel/workqueue.c | 22 ++++++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 00c1b9ba825..c270b4eedf1 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -295,7 +295,6 @@ enum { WQ_CPU_INTENSIVE = 1 << 5, /* cpu instensive workqueue */ WQ_DRAINING = 1 << 6, /* internal: workqueue is draining */ - WQ_RESCUER = 1 << 7, /* internal: workqueue has rescuer */ WQ_MAX_ACTIVE = 512, /* I like 512, better ideas? */ WQ_MAX_UNBOUND_PER_CPU = 4, /* 4 * #cpus for unbound wq */ diff --git a/kernel/workqueue.c b/kernel/workqueue.c index a8b86f7b6e3..7ff2b9c5cc3 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1827,7 +1827,7 @@ static void send_mayday(struct work_struct *work) lockdep_assert_held(&workqueue_lock); - if (!(wq->flags & WQ_RESCUER)) + if (!wq->rescuer) return; /* mayday mayday mayday */ @@ -2285,7 +2285,7 @@ sleep: * @__rescuer: self * * Workqueue rescuer thread function. There's one rescuer for each - * workqueue which has WQ_RESCUER set. + * workqueue which has WQ_MEM_RECLAIM set. * * Regular work processing on a pool may block trying to create a new * worker which uses GFP_KERNEL allocation which has slight chance of @@ -2769,7 +2769,7 @@ static bool start_flush_work(struct work_struct *work, struct wq_barrier *barr) * flusher is not running on the same workqueue by verifying write * access. */ - if (pwq->wq->saved_max_active == 1 || pwq->wq->flags & WQ_RESCUER) + if (pwq->wq->saved_max_active == 1 || pwq->wq->rescuer) lock_map_acquire(&pwq->wq->lockdep_map); else lock_map_acquire_read(&pwq->wq->lockdep_map); @@ -3412,13 +3412,6 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt, va_end(args); va_end(args1); - /* - * Workqueues which may be used during memory reclaim should - * have a rescuer to guarantee forward progress. - */ - if (flags & WQ_MEM_RECLAIM) - flags |= WQ_RESCUER; - max_active = max_active ?: WQ_DFL_ACTIVE; max_active = wq_clamp_max_active(max_active, flags, wq->name); @@ -3449,7 +3442,11 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt, } local_irq_enable(); - if (flags & WQ_RESCUER) { + /* + * Workqueues which may be used during memory reclaim should + * have a rescuer to guarantee forward progress. + */ + if (flags & WQ_MEM_RECLAIM) { struct worker *rescuer; wq->rescuer = rescuer = alloc_worker(); @@ -3533,9 +3530,10 @@ void destroy_workqueue(struct workqueue_struct *wq) spin_unlock_irq(&workqueue_lock); - if (wq->flags & WQ_RESCUER) { + if (wq->rescuer) { kthread_stop(wq->rescuer->task); kfree(wq->rescuer); + wq->rescuer = NULL; } /* -- cgit v1.2.3-70-g09d2 From 9e8cd2f5898ab6710ad81f4583fada08bf8049a4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 12 Mar 2013 11:30:04 -0700 Subject: workqueue: implement apply_workqueue_attrs() Implement apply_workqueue_attrs() which applies workqueue_attrs to the specified unbound workqueue by creating a new pwq (pool_workqueue) linked to worker_pool with the specified attributes. A new pwq is linked at the head of wq->pwqs instead of tail and __queue_work() verifies that the first unbound pwq has positive refcnt before choosing it for the actual queueing. This is to cover the case where creation of a new pwq races with queueing. As base ref on a pwq won't be dropped without making another pwq the first one, __queue_work() is guaranteed to make progress and not add work item to a dead pwq. init_and_link_pwq() is updated to return the last first pwq the new pwq replaced, which is put by apply_workqueue_attrs(). Note that apply_workqueue_attrs() is almost identical to unbound pwq part of alloc_and_link_pwqs(). The only difference is that there is no previous first pwq. apply_workqueue_attrs() is implemented to handle such cases and replaces unbound pwq handling in alloc_and_link_pwqs(). Signed-off-by: Tejun Heo Reviewed-by: Lai Jiangshan --- include/linux/workqueue.h | 2 ++ kernel/workqueue.c | 91 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 73 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index c270b4eedf1..e152394fa7e 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -410,6 +410,8 @@ extern void destroy_workqueue(struct workqueue_struct *wq); struct workqueue_attrs *alloc_workqueue_attrs(gfp_t gfp_mask); void free_workqueue_attrs(struct workqueue_attrs *attrs); +int apply_workqueue_attrs(struct workqueue_struct *wq, + const struct workqueue_attrs *attrs); extern bool queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 16fb6747276..2a67fbbd192 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1228,7 +1228,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, if (unlikely(wq->flags & WQ_DRAINING) && WARN_ON_ONCE(!is_chained_work(wq))) return; - +retry: /* pwq which will be used unless @work is executing elsewhere */ if (!(wq->flags & WQ_UNBOUND)) { if (cpu == WORK_CPU_UNBOUND) @@ -1262,6 +1262,25 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, spin_lock(&pwq->pool->lock); } + /* + * pwq is determined and locked. For unbound pools, we could have + * raced with pwq release and it could already be dead. If its + * refcnt is zero, repeat pwq selection. Note that pwqs never die + * without another pwq replacing it as the first pwq or while a + * work item is executing on it, so the retying is guaranteed to + * make forward-progress. + */ + if (unlikely(!pwq->refcnt)) { + if (wq->flags & WQ_UNBOUND) { + spin_unlock(&pwq->pool->lock); + cpu_relax(); + goto retry; + } + /* oops */ + WARN_ONCE(true, "workqueue: per-cpu pwq for %s on cpu%d has 0 refcnt", + wq->name, cpu); + } + /* pwq determined, queue */ trace_workqueue_queue_work(req_cpu, pwq, work); @@ -3425,7 +3444,8 @@ static void pwq_unbound_release_workfn(struct work_struct *work) static void init_and_link_pwq(struct pool_workqueue *pwq, struct workqueue_struct *wq, - struct worker_pool *pool) + struct worker_pool *pool, + struct pool_workqueue **p_last_pwq) { BUG_ON((unsigned long)pwq & WORK_STRUCT_FLAG_MASK); @@ -3445,13 +3465,58 @@ static void init_and_link_pwq(struct pool_workqueue *pwq, mutex_lock(&wq->flush_mutex); spin_lock_irq(&workqueue_lock); + if (p_last_pwq) + *p_last_pwq = first_pwq(wq); pwq->work_color = wq->work_color; - list_add_tail_rcu(&pwq->pwqs_node, &wq->pwqs); + list_add_rcu(&pwq->pwqs_node, &wq->pwqs); spin_unlock_irq(&workqueue_lock); mutex_unlock(&wq->flush_mutex); } +/** + * apply_workqueue_attrs - apply new workqueue_attrs to an unbound workqueue + * @wq: the target workqueue + * @attrs: the workqueue_attrs to apply, allocated with alloc_workqueue_attrs() + * + * Apply @attrs to an unbound workqueue @wq. If @attrs doesn't match the + * current attributes, a new pwq is created and made the first pwq which + * will serve all new work items. Older pwqs are released as in-flight + * work items finish. Note that a work item which repeatedly requeues + * itself back-to-back will stay on its current pwq. + * + * Performs GFP_KERNEL allocations. Returns 0 on success and -errno on + * failure. + */ +int apply_workqueue_attrs(struct workqueue_struct *wq, + const struct workqueue_attrs *attrs) +{ + struct pool_workqueue *pwq, *last_pwq; + struct worker_pool *pool; + + if (WARN_ON(!(wq->flags & WQ_UNBOUND))) + return -EINVAL; + + pwq = kmem_cache_zalloc(pwq_cache, GFP_KERNEL); + if (!pwq) + return -ENOMEM; + + pool = get_unbound_pool(attrs); + if (!pool) { + kmem_cache_free(pwq_cache, pwq); + return -ENOMEM; + } + + init_and_link_pwq(pwq, wq, pool, &last_pwq); + if (last_pwq) { + spin_lock_irq(&last_pwq->pool->lock); + put_pwq(last_pwq); + spin_unlock_irq(&last_pwq->pool->lock); + } + + return 0; +} + static int alloc_and_link_pwqs(struct workqueue_struct *wq) { bool highpri = wq->flags & WQ_HIGHPRI; @@ -3468,26 +3533,12 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) struct worker_pool *cpu_pools = per_cpu(cpu_worker_pools, cpu); - init_and_link_pwq(pwq, wq, &cpu_pools[highpri]); + init_and_link_pwq(pwq, wq, &cpu_pools[highpri], NULL); } + return 0; } else { - struct pool_workqueue *pwq; - struct worker_pool *pool; - - pwq = kmem_cache_zalloc(pwq_cache, GFP_KERNEL); - if (!pwq) - return -ENOMEM; - - pool = get_unbound_pool(unbound_std_wq_attrs[highpri]); - if (!pool) { - kmem_cache_free(pwq_cache, pwq); - return -ENOMEM; - } - - init_and_link_pwq(pwq, wq, pool); + return apply_workqueue_attrs(wq, unbound_std_wq_attrs[highpri]); } - - return 0; } static int wq_clamp_max_active(int max_active, unsigned int flags, -- cgit v1.2.3-70-g09d2 From 618b01eb426dd2d73a4b5e5ebc6379e4eee3b123 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 12 Mar 2013 11:30:04 -0700 Subject: workqueue: make it clear that WQ_DRAINING is an internal flag We're gonna add another internal WQ flag. Let's make the distinction clear. Prefix WQ_DRAINING with __ and move it to bit 16. Signed-off-by: Tejun Heo Reviewed-by: Lai Jiangshan --- include/linux/workqueue.h | 2 +- kernel/workqueue.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index e152394fa7e..1751ec4c47c 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -294,7 +294,7 @@ enum { WQ_HIGHPRI = 1 << 4, /* high priority */ WQ_CPU_INTENSIVE = 1 << 5, /* cpu instensive workqueue */ - WQ_DRAINING = 1 << 6, /* internal: workqueue is draining */ + __WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */ WQ_MAX_ACTIVE = 512, /* I like 512, better ideas? */ WQ_MAX_UNBOUND_PER_CPU = 4, /* 4 * #cpus for unbound wq */ diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 2a67fbbd192..590f4d048ec 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1225,7 +1225,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, debug_work_activate(work); /* if dying, only works from the same workqueue are allowed */ - if (unlikely(wq->flags & WQ_DRAINING) && + if (unlikely(wq->flags & __WQ_DRAINING) && WARN_ON_ONCE(!is_chained_work(wq))) return; retry: @@ -2763,11 +2763,11 @@ void drain_workqueue(struct workqueue_struct *wq) /* * __queue_work() needs to test whether there are drainers, is much * hotter than drain_workqueue() and already looks at @wq->flags. - * Use WQ_DRAINING so that queue doesn't have to check nr_drainers. + * Use __WQ_DRAINING so that queue doesn't have to check nr_drainers. */ spin_lock_irq(&workqueue_lock); if (!wq->nr_drainers++) - wq->flags |= WQ_DRAINING; + wq->flags |= __WQ_DRAINING; spin_unlock_irq(&workqueue_lock); reflush: flush_workqueue(wq); @@ -2795,7 +2795,7 @@ reflush: spin_lock(&workqueue_lock); if (!--wq->nr_drainers) - wq->flags &= ~WQ_DRAINING; + wq->flags &= ~__WQ_DRAINING; spin_unlock(&workqueue_lock); local_irq_enable(); -- cgit v1.2.3-70-g09d2 From 8719dceae2f98a578507c0f6b49c93f320bd729c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 12 Mar 2013 11:30:04 -0700 Subject: workqueue: reject adjusting max_active or applying attrs to ordered workqueues Adjusting max_active of or applying new workqueue_attrs to an ordered workqueue breaks its ordering guarantee. The former is obvious. The latter is because applying attrs creates a new pwq (pool_workqueue) and there is no ordering constraint between the old and new pwqs. Make apply_workqueue_attrs() and workqueue_set_max_active() trigger WARN_ON() if those operations are requested on an ordered workqueue and fail / ignore respectively. Signed-off-by: Tejun Heo Reviewed-by: Lai Jiangshan --- include/linux/workqueue.h | 3 ++- kernel/workqueue.c | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 1751ec4c47c..5668ab249af 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -295,6 +295,7 @@ enum { WQ_CPU_INTENSIVE = 1 << 5, /* cpu instensive workqueue */ __WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */ + __WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */ WQ_MAX_ACTIVE = 512, /* I like 512, better ideas? */ WQ_MAX_UNBOUND_PER_CPU = 4, /* 4 * #cpus for unbound wq */ @@ -397,7 +398,7 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, * Pointer to the allocated workqueue on success, %NULL on failure. */ #define alloc_ordered_workqueue(fmt, flags, args...) \ - alloc_workqueue(fmt, WQ_UNBOUND | (flags), 1, ##args) + alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args) #define create_workqueue(name) \ alloc_workqueue((name), WQ_MEM_RECLAIM, 1) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 590f4d048ec..cecd4ffe2c4 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -3494,9 +3494,14 @@ int apply_workqueue_attrs(struct workqueue_struct *wq, struct pool_workqueue *pwq, *last_pwq; struct worker_pool *pool; + /* only unbound workqueues can change attributes */ if (WARN_ON(!(wq->flags & WQ_UNBOUND))) return -EINVAL; + /* creating multiple pwqs breaks ordering guarantee */ + if (WARN_ON((wq->flags & __WQ_ORDERED) && !list_empty(&wq->pwqs))) + return -EINVAL; + pwq = kmem_cache_zalloc(pwq_cache, GFP_KERNEL); if (!pwq) return -ENOMEM; @@ -3752,6 +3757,10 @@ void workqueue_set_max_active(struct workqueue_struct *wq, int max_active) { struct pool_workqueue *pwq; + /* disallow meddling with max_active for ordered workqueues */ + if (WARN_ON(wq->flags & __WQ_ORDERED)) + return; + max_active = wq_clamp_max_active(max_active, wq->flags, wq->name); spin_lock_irq(&workqueue_lock); -- cgit v1.2.3-70-g09d2 From ba630e4940924ad1962883c207a62890778ced63 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 12 Mar 2013 11:30:04 -0700 Subject: cpumask: implement cpumask_parse() We have cpulist_parse() but not cpumask_parse(). Implement it using bitmap_parse(). bitmap_parse() is weird in that it takes @len for a string in kernel-memory which also is inconsistent with bitmap_parselist(). Make cpumask_parse() calculate the length and don't expose the inconsistency to cpumask users. Maybe we can fix up bitmap_parse() later. This will be used to expose workqueue cpumask knobs to userland via sysfs. Signed-off-by: Tejun Heo Cc: Rusty Russell --- include/linux/cpumask.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'include') diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 032560295fc..d08e4d2a9b9 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -590,6 +590,21 @@ static inline int cpulist_scnprintf(char *buf, int len, nr_cpumask_bits); } +/** + * cpumask_parse - extract a cpumask from from a string + * @buf: the buffer to extract from + * @dstp: the cpumask to set. + * + * Returns -errno, or 0 for success. + */ +static inline int cpumask_parse(const char *buf, struct cpumask *dstp) +{ + char *nl = strchr(buf, '\n'); + int len = nl ? nl - buf : strlen(buf); + + return bitmap_parse(buf, len, cpumask_bits(dstp), nr_cpumask_bits); +} + /** * cpulist_parse - extract a cpumask from a user string of ranges * @buf: the buffer to extract from -- cgit v1.2.3-70-g09d2 From d73ce004225a7b2ed75f4340bb63721d55552265 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 12 Mar 2013 11:30:05 -0700 Subject: driver/base: implement subsys_virtual_register() Kay tells me the most appropriate place to expose workqueues to userland would be /sys/devices/virtual/workqueues/WQ_NAME which is symlinked to /sys/bus/workqueue/devices/WQ_NAME and that we're lacking a way to do that outside of driver core as virtual_device_parent() isn't exported and there's no inteface to conveniently create a virtual subsystem. This patch implements subsys_virtual_register() by factoring out subsys_register() from subsys_system_register() and using it with virtual_device_parent() as the origin directory. It's identical to subsys_system_register() other than the origin directory but we aren't gonna restrict the device names which should be used under it. This will be used to expose workqueue attributes to userland. Signed-off-by: Tejun Heo Acked-by: Greg Kroah-Hartman Cc: Kay Sievers --- drivers/base/base.h | 2 ++ drivers/base/bus.c | 73 +++++++++++++++++++++++++++++++++++--------------- drivers/base/core.c | 2 +- include/linux/device.h | 2 ++ 4 files changed, 57 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/base/base.h b/drivers/base/base.h index 6ee17bb391a..b8bdfe61daa 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -101,6 +101,8 @@ static inline int hypervisor_init(void) { return 0; } extern int platform_bus_init(void); extern void cpu_dev_init(void); +struct kobject *virtual_device_parent(struct device *dev); + extern int bus_add_device(struct device *dev); extern void bus_probe_device(struct device *dev); extern void bus_remove_device(struct device *dev); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 519865b53f7..2ae2d2f92b6 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1205,26 +1205,10 @@ static void system_root_device_release(struct device *dev) { kfree(dev); } -/** - * subsys_system_register - register a subsystem at /sys/devices/system/ - * @subsys: system subsystem - * @groups: default attributes for the root device - * - * All 'system' subsystems have a /sys/devices/system/ root device - * with the name of the subsystem. The root device can carry subsystem- - * wide attributes. All registered devices are below this single root - * device and are named after the subsystem with a simple enumeration - * number appended. The registered devices are not explicitely named; - * only 'id' in the device needs to be set. - * - * Do not use this interface for anything new, it exists for compatibility - * with bad ideas only. New subsystems should use plain subsystems; and - * add the subsystem-wide attributes should be added to the subsystem - * directory itself and not some create fake root-device placed in - * /sys/devices/system/. - */ -int subsys_system_register(struct bus_type *subsys, - const struct attribute_group **groups) + +static int subsys_register(struct bus_type *subsys, + const struct attribute_group **groups, + struct kobject *parent_of_root) { struct device *dev; int err; @@ -1243,7 +1227,7 @@ int subsys_system_register(struct bus_type *subsys, if (err < 0) goto err_name; - dev->kobj.parent = &system_kset->kobj; + dev->kobj.parent = parent_of_root; dev->groups = groups; dev->release = system_root_device_release; @@ -1263,8 +1247,55 @@ err_dev: bus_unregister(subsys); return err; } + +/** + * subsys_system_register - register a subsystem at /sys/devices/system/ + * @subsys: system subsystem + * @groups: default attributes for the root device + * + * All 'system' subsystems have a /sys/devices/system/ root device + * with the name of the subsystem. The root device can carry subsystem- + * wide attributes. All registered devices are below this single root + * device and are named after the subsystem with a simple enumeration + * number appended. The registered devices are not explicitely named; + * only 'id' in the device needs to be set. + * + * Do not use this interface for anything new, it exists for compatibility + * with bad ideas only. New subsystems should use plain subsystems; and + * add the subsystem-wide attributes should be added to the subsystem + * directory itself and not some create fake root-device placed in + * /sys/devices/system/. + */ +int subsys_system_register(struct bus_type *subsys, + const struct attribute_group **groups) +{ + return subsys_register(subsys, groups, &system_kset->kobj); +} EXPORT_SYMBOL_GPL(subsys_system_register); +/** + * subsys_virtual_register - register a subsystem at /sys/devices/virtual/ + * @subsys: virtual subsystem + * @groups: default attributes for the root device + * + * All 'virtual' subsystems have a /sys/devices/system/ root device + * with the name of the subystem. The root device can carry subsystem-wide + * attributes. All registered devices are below this single root device. + * There's no restriction on device naming. This is for kernel software + * constructs which need sysfs interface. + */ +int subsys_virtual_register(struct bus_type *subsys, + const struct attribute_group **groups) +{ + struct kobject *virtual_dir; + + virtual_dir = virtual_device_parent(NULL); + if (!virtual_dir) + return -ENOMEM; + + return subsys_register(subsys, groups, virtual_dir); +} + int __init buses_init(void) { bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); diff --git a/drivers/base/core.c b/drivers/base/core.c index 56536f4b0f6..f58084a86e8 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -690,7 +690,7 @@ void device_initialize(struct device *dev) set_dev_node(dev, -1); } -static struct kobject *virtual_device_parent(struct device *dev) +struct kobject *virtual_device_parent(struct device *dev) { static struct kobject *virtual_dir = NULL; diff --git a/include/linux/device.h b/include/linux/device.h index 9d6464ea99c..ee10d4e7be1 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -302,6 +302,8 @@ void subsys_interface_unregister(struct subsys_interface *sif); int subsys_system_register(struct bus_type *subsys, const struct attribute_group **groups); +int subsys_virtual_register(struct bus_type *subsys, + const struct attribute_group **groups); /** * struct class - device classes -- cgit v1.2.3-70-g09d2 From 226223ab3c4118ddd10688cc2c131135848371ab Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 12 Mar 2013 11:30:05 -0700 Subject: workqueue: implement sysfs interface for workqueues There are cases where workqueue users want to expose control knobs to userland. e.g. Unbound workqueues with custom attributes are scheduled to be used for writeback workers and depending on configuration it can be useful to allow admins to tinker with the priority or allowed CPUs. This patch implements workqueue_sysfs_register(), which makes the workqueue visible under /sys/bus/workqueue/devices/WQ_NAME. There currently are two attributes common to both per-cpu and unbound pools and extra attributes for unbound pools including nice level and cpumask. If alloc_workqueue*() is called with WQ_SYSFS, workqueue_sysfs_register() is called automatically as part of workqueue creation. This is the preferred method unless the workqueue user wants to apply workqueue_attrs before making the workqueue visible to userland. v2: Disallow exposing ordered workqueues as ordered workqueues can't be tuned in any way. Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 8 ++ kernel/workqueue.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 296 insertions(+) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 5668ab249af..7f6d29a417c 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -293,6 +293,7 @@ enum { WQ_MEM_RECLAIM = 1 << 3, /* may be used for memory reclaim */ WQ_HIGHPRI = 1 << 4, /* high priority */ WQ_CPU_INTENSIVE = 1 << 5, /* cpu instensive workqueue */ + WQ_SYSFS = 1 << 6, /* visible in sysfs, see wq_sysfs_register() */ __WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */ __WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */ @@ -495,4 +496,11 @@ extern bool freeze_workqueues_busy(void); extern void thaw_workqueues(void); #endif /* CONFIG_FREEZER */ +#ifdef CONFIG_SYSFS +int workqueue_sysfs_register(struct workqueue_struct *wq); +#else /* CONFIG_SYSFS */ +static inline int workqueue_sysfs_register(struct workqueue_struct *wq) +{ return 0; } +#endif /* CONFIG_SYSFS */ + #endif diff --git a/kernel/workqueue.c b/kernel/workqueue.c index cecd4ffe2c4..c82feac0a87 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -210,6 +210,8 @@ struct wq_flusher { struct completion done; /* flush completion */ }; +struct wq_device; + /* * The externally visible workqueue abstraction is an array of * per-CPU workqueues: @@ -233,6 +235,10 @@ struct workqueue_struct { int nr_drainers; /* W: drain in progress */ int saved_max_active; /* W: saved pwq max_active */ + +#ifdef CONFIG_SYSFS + struct wq_device *wq_dev; /* I: for sysfs interface */ +#endif #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif @@ -442,6 +448,8 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(struct worker_pool [NR_STD_WORKER_POOLS], static DEFINE_IDR(worker_pool_idr); static int worker_thread(void *__worker); +static void copy_workqueue_attrs(struct workqueue_attrs *to, + const struct workqueue_attrs *from); /* allocate ID and assign it to @pool */ static int worker_pool_assign_id(struct worker_pool *pool) @@ -3153,6 +3161,281 @@ int keventd_up(void) return system_wq != NULL; } +#ifdef CONFIG_SYSFS +/* + * Workqueues with WQ_SYSFS flag set is visible to userland via + * /sys/bus/workqueue/devices/WQ_NAME. All visible workqueues have the + * following attributes. + * + * per_cpu RO bool : whether the workqueue is per-cpu or unbound + * max_active RW int : maximum number of in-flight work items + * + * Unbound workqueues have the following extra attributes. + * + * id RO int : the associated pool ID + * nice RW int : nice value of the workers + * cpumask RW mask : bitmask of allowed CPUs for the workers + */ +struct wq_device { + struct workqueue_struct *wq; + struct device dev; +}; + +static struct workqueue_struct *dev_to_wq(struct device *dev) +{ + struct wq_device *wq_dev = container_of(dev, struct wq_device, dev); + + return wq_dev->wq; +} + +static ssize_t wq_per_cpu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)!(wq->flags & WQ_UNBOUND)); +} + +static ssize_t wq_max_active_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", wq->saved_max_active); +} + +static ssize_t wq_max_active_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + int val; + + if (sscanf(buf, "%d", &val) != 1 || val <= 0) + return -EINVAL; + + workqueue_set_max_active(wq, val); + return count; +} + +static struct device_attribute wq_sysfs_attrs[] = { + __ATTR(per_cpu, 0444, wq_per_cpu_show, NULL), + __ATTR(max_active, 0644, wq_max_active_show, wq_max_active_store), + __ATTR_NULL, +}; + +static ssize_t wq_pool_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + struct worker_pool *pool; + int written; + + rcu_read_lock_sched(); + pool = first_pwq(wq)->pool; + written = scnprintf(buf, PAGE_SIZE, "%d\n", pool->id); + rcu_read_unlock_sched(); + + return written; +} + +static ssize_t wq_nice_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + int written; + + rcu_read_lock_sched(); + written = scnprintf(buf, PAGE_SIZE, "%d\n", + first_pwq(wq)->pool->attrs->nice); + rcu_read_unlock_sched(); + + return written; +} + +/* prepare workqueue_attrs for sysfs store operations */ +static struct workqueue_attrs *wq_sysfs_prep_attrs(struct workqueue_struct *wq) +{ + struct workqueue_attrs *attrs; + + attrs = alloc_workqueue_attrs(GFP_KERNEL); + if (!attrs) + return NULL; + + rcu_read_lock_sched(); + copy_workqueue_attrs(attrs, first_pwq(wq)->pool->attrs); + rcu_read_unlock_sched(); + return attrs; +} + +static ssize_t wq_nice_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + struct workqueue_attrs *attrs; + int ret; + + attrs = wq_sysfs_prep_attrs(wq); + if (!attrs) + return -ENOMEM; + + if (sscanf(buf, "%d", &attrs->nice) == 1 && + attrs->nice >= -20 && attrs->nice <= 19) + ret = apply_workqueue_attrs(wq, attrs); + else + ret = -EINVAL; + + free_workqueue_attrs(attrs); + return ret ?: count; +} + +static ssize_t wq_cpumask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + int written; + + rcu_read_lock_sched(); + written = cpumask_scnprintf(buf, PAGE_SIZE, + first_pwq(wq)->pool->attrs->cpumask); + rcu_read_unlock_sched(); + + written += scnprintf(buf + written, PAGE_SIZE - written, "\n"); + return written; +} + +static ssize_t wq_cpumask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + struct workqueue_attrs *attrs; + int ret; + + attrs = wq_sysfs_prep_attrs(wq); + if (!attrs) + return -ENOMEM; + + ret = cpumask_parse(buf, attrs->cpumask); + if (!ret) + ret = apply_workqueue_attrs(wq, attrs); + + free_workqueue_attrs(attrs); + return ret ?: count; +} + +static struct device_attribute wq_sysfs_unbound_attrs[] = { + __ATTR(pool_id, 0444, wq_pool_id_show, NULL), + __ATTR(nice, 0644, wq_nice_show, wq_nice_store), + __ATTR(cpumask, 0644, wq_cpumask_show, wq_cpumask_store), + __ATTR_NULL, +}; + +static struct bus_type wq_subsys = { + .name = "workqueue", + .dev_attrs = wq_sysfs_attrs, +}; + +static int __init wq_sysfs_init(void) +{ + return subsys_virtual_register(&wq_subsys, NULL); +} +core_initcall(wq_sysfs_init); + +static void wq_device_release(struct device *dev) +{ + struct wq_device *wq_dev = container_of(dev, struct wq_device, dev); + + kfree(wq_dev); +} + +/** + * workqueue_sysfs_register - make a workqueue visible in sysfs + * @wq: the workqueue to register + * + * Expose @wq in sysfs under /sys/bus/workqueue/devices. + * alloc_workqueue*() automatically calls this function if WQ_SYSFS is set + * which is the preferred method. + * + * Workqueue user should use this function directly iff it wants to apply + * workqueue_attrs before making the workqueue visible in sysfs; otherwise, + * apply_workqueue_attrs() may race against userland updating the + * attributes. + * + * Returns 0 on success, -errno on failure. + */ +int workqueue_sysfs_register(struct workqueue_struct *wq) +{ + struct wq_device *wq_dev; + int ret; + + /* + * Adjusting max_active or creating new pwqs by applyting + * attributes breaks ordering guarantee. Disallow exposing ordered + * workqueues. + */ + if (WARN_ON(wq->flags & __WQ_ORDERED)) + return -EINVAL; + + wq->wq_dev = wq_dev = kzalloc(sizeof(*wq_dev), GFP_KERNEL); + if (!wq_dev) + return -ENOMEM; + + wq_dev->wq = wq; + wq_dev->dev.bus = &wq_subsys; + wq_dev->dev.init_name = wq->name; + wq_dev->dev.release = wq_device_release; + + /* + * unbound_attrs are created separately. Suppress uevent until + * everything is ready. + */ + dev_set_uevent_suppress(&wq_dev->dev, true); + + ret = device_register(&wq_dev->dev); + if (ret) { + kfree(wq_dev); + wq->wq_dev = NULL; + return ret; + } + + if (wq->flags & WQ_UNBOUND) { + struct device_attribute *attr; + + for (attr = wq_sysfs_unbound_attrs; attr->attr.name; attr++) { + ret = device_create_file(&wq_dev->dev, attr); + if (ret) { + device_unregister(&wq_dev->dev); + wq->wq_dev = NULL; + return ret; + } + } + } + + kobject_uevent(&wq_dev->dev.kobj, KOBJ_ADD); + return 0; +} + +/** + * workqueue_sysfs_unregister - undo workqueue_sysfs_register() + * @wq: the workqueue to unregister + * + * If @wq is registered to sysfs by workqueue_sysfs_register(), unregister. + */ +static void workqueue_sysfs_unregister(struct workqueue_struct *wq) +{ + struct wq_device *wq_dev = wq->wq_dev; + + if (!wq->wq_dev) + return; + + wq->wq_dev = NULL; + device_unregister(&wq_dev->dev); +} +#else /* CONFIG_SYSFS */ +static void workqueue_sysfs_unregister(struct workqueue_struct *wq) { } +#endif /* CONFIG_SYSFS */ + /** * free_workqueue_attrs - free a workqueue_attrs * @attrs: workqueue_attrs to free @@ -3625,6 +3908,9 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt, wake_up_process(rescuer->task); } + if ((wq->flags & WQ_SYSFS) && workqueue_sysfs_register(wq)) + goto err_destroy; + /* * workqueue_lock protects global freeze state and workqueues * list. Grab it, set max_active accordingly and add the new @@ -3693,6 +3979,8 @@ void destroy_workqueue(struct workqueue_struct *wq) spin_unlock_irq(&workqueue_lock); + workqueue_sysfs_unregister(wq); + if (wq->rescuer) { kthread_stop(wq->rescuer->task); kfree(wq->rescuer); -- cgit v1.2.3-70-g09d2 From cc2a8b1a5595a435191fb197d92d1f3e193c9a6d Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Tue, 12 Mar 2013 13:59:14 -0700 Subject: async: remove unused @node from struct async_domain The @node in struct async_domain is unused after we introduce async_global_pending, remove it. tj: Unnecessary whitespace adjustments dropped. Signed-off-by: Lai Jiangshan Signed-off-by: Tejun Heo Cc: Arjan van de Ven --- include/linux/async.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/async.h b/include/linux/async.h index a2e3f18b2ad..98ea0fef30d 100644 --- a/include/linux/async.h +++ b/include/linux/async.h @@ -18,7 +18,6 @@ typedef u64 async_cookie_t; typedef void (async_func_ptr) (void *data, async_cookie_t cookie); struct async_domain { - struct list_head node; struct list_head pending; unsigned registered:1; }; @@ -27,8 +26,7 @@ struct async_domain { * domain participates in global async_synchronize_full */ #define ASYNC_DOMAIN(_name) \ - struct async_domain _name = { .node = LIST_HEAD_INIT(_name.node), \ - .pending = LIST_HEAD_INIT(_name.pending), \ + struct async_domain _name = { .pending = LIST_HEAD_INIT(_name.pending), \ .registered = 1 } /* @@ -36,8 +34,7 @@ struct async_domain { * complete, this domain does not participate in async_synchronize_full */ #define ASYNC_DOMAIN_EXCLUSIVE(_name) \ - struct async_domain _name = { .node = LIST_HEAD_INIT(_name.node), \ - .pending = LIST_HEAD_INIT(_name.pending), \ + struct async_domain _name = { .pending = LIST_HEAD_INIT(_name.pending), \ .registered = 0 } extern async_cookie_t async_schedule(async_func_ptr *ptr, void *data); -- cgit v1.2.3-70-g09d2 From 362f2b098b188ede9c4350cc20e58040dbfa515e Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Tue, 12 Mar 2013 13:59:14 -0700 Subject: async: rename and redefine async_func_ptr A function type is typically defined as typedef ret_type (*func)(args..) but async_func_ptr is not. Redefine it. Also rename async_func_ptr to async_func_t for _func_t suffix is more generic. Signed-off-by: Lai Jiangshan Signed-off-by: Tejun Heo Cc: Arjan van de Ven --- arch/sh/drivers/pci/pcie-sh7786.c | 2 +- include/linux/async.h | 6 +++--- kernel/async.c | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/arch/sh/drivers/pci/pcie-sh7786.c b/arch/sh/drivers/pci/pcie-sh7786.c index c2c85f6cd73..a162a7f86b2 100644 --- a/arch/sh/drivers/pci/pcie-sh7786.c +++ b/arch/sh/drivers/pci/pcie-sh7786.c @@ -35,7 +35,7 @@ static unsigned int nr_ports; static struct sh7786_pcie_hwops { int (*core_init)(void); - async_func_ptr *port_init_hw; + async_func_t port_init_hw; } *sh7786_pcie_hwops; static struct resource sh7786_pci0_resources[] = { diff --git a/include/linux/async.h b/include/linux/async.h index 98ea0fef30d..6b0226bdaad 100644 --- a/include/linux/async.h +++ b/include/linux/async.h @@ -16,7 +16,7 @@ #include typedef u64 async_cookie_t; -typedef void (async_func_ptr) (void *data, async_cookie_t cookie); +typedef void (*async_func_t) (void *data, async_cookie_t cookie); struct async_domain { struct list_head pending; unsigned registered:1; @@ -37,8 +37,8 @@ struct async_domain { struct async_domain _name = { .pending = LIST_HEAD_INIT(_name.pending), \ .registered = 0 } -extern async_cookie_t async_schedule(async_func_ptr *ptr, void *data); -extern async_cookie_t async_schedule_domain(async_func_ptr *ptr, void *data, +extern async_cookie_t async_schedule(async_func_t func, void *data); +extern async_cookie_t async_schedule_domain(async_func_t func, void *data, struct async_domain *domain); void async_unregister_domain(struct async_domain *domain); extern void async_synchronize_full(void); diff --git a/kernel/async.c b/kernel/async.c index ab99c92f6b6..61f023ce022 100644 --- a/kernel/async.c +++ b/kernel/async.c @@ -73,7 +73,7 @@ struct async_entry { struct list_head global_list; struct work_struct work; async_cookie_t cookie; - async_func_ptr *func; + async_func_t func; void *data; struct async_domain *domain; }; @@ -145,7 +145,7 @@ static void async_run_entry_fn(struct work_struct *work) wake_up(&async_done); } -static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct async_domain *domain) +static async_cookie_t __async_schedule(async_func_t func, void *data, struct async_domain *domain) { struct async_entry *entry; unsigned long flags; @@ -165,13 +165,13 @@ static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct a spin_unlock_irqrestore(&async_lock, flags); /* low on memory.. run synchronously */ - ptr(data, newcookie); + func(data, newcookie); return newcookie; } INIT_LIST_HEAD(&entry->domain_list); INIT_LIST_HEAD(&entry->global_list); INIT_WORK(&entry->work, async_run_entry_fn); - entry->func = ptr; + entry->func = func; entry->data = data; entry->domain = domain; @@ -198,21 +198,21 @@ static async_cookie_t __async_schedule(async_func_ptr *ptr, void *data, struct a /** * async_schedule - schedule a function for asynchronous execution - * @ptr: function to execute asynchronously + * @func: function to execute asynchronously * @data: data pointer to pass to the function * * Returns an async_cookie_t that may be used for checkpointing later. * Note: This function may be called from atomic or non-atomic contexts. */ -async_cookie_t async_schedule(async_func_ptr *ptr, void *data) +async_cookie_t async_schedule(async_func_t func, void *data) { - return __async_schedule(ptr, data, &async_dfl_domain); + return __async_schedule(func, data, &async_dfl_domain); } EXPORT_SYMBOL_GPL(async_schedule); /** * async_schedule_domain - schedule a function for asynchronous execution within a certain domain - * @ptr: function to execute asynchronously + * @func: function to execute asynchronously * @data: data pointer to pass to the function * @domain: the domain * @@ -222,10 +222,10 @@ EXPORT_SYMBOL_GPL(async_schedule); * synchronization domain is specified via @domain. Note: This function * may be called from atomic or non-atomic contexts. */ -async_cookie_t async_schedule_domain(async_func_ptr *ptr, void *data, +async_cookie_t async_schedule_domain(async_func_t func, void *data, struct async_domain *domain) { - return __async_schedule(ptr, data, domain); + return __async_schedule(func, data, domain); } EXPORT_SYMBOL_GPL(async_schedule_domain); -- cgit v1.2.3-70-g09d2 From 49d0de082c31de34cc896c14eec5f1c2ade0415a Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Thu, 14 Feb 2013 16:42:34 -0800 Subject: rcu: Fix hlist_bl_set_first_rcu() annotation Abhi noticed that we were getting a complaint from the RCU subsystem about access of an RCU protected list under the write side bit lock. This commit adds additional annotation to check both the RCU read lock and the write side bit lock before printing a message. Reported by: Abhijith Das Signed-off-by: Steven Whitehouse Tested-by: Abhijith Das Signed-off-by: Paul E. McKenney --- include/linux/list_bl.h | 5 +++++ include/linux/rculist_bl.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/list_bl.h b/include/linux/list_bl.h index 31f9d75adc5..2eb88556c5c 100644 --- a/include/linux/list_bl.h +++ b/include/linux/list_bl.h @@ -125,6 +125,11 @@ static inline void hlist_bl_unlock(struct hlist_bl_head *b) __bit_spin_unlock(0, (unsigned long *)b); } +static inline bool hlist_bl_is_locked(struct hlist_bl_head *b) +{ + return bit_spin_is_locked(0, (unsigned long *)b); +} + /** * hlist_bl_for_each_entry - iterate over list of given type * @tpos: the type * to use as a loop cursor. diff --git a/include/linux/rculist_bl.h b/include/linux/rculist_bl.h index cf1244fbf3b..4f216c59e7d 100644 --- a/include/linux/rculist_bl.h +++ b/include/linux/rculist_bl.h @@ -20,7 +20,7 @@ static inline void hlist_bl_set_first_rcu(struct hlist_bl_head *h, static inline struct hlist_bl_node *hlist_bl_first_rcu(struct hlist_bl_head *h) { return (struct hlist_bl_node *) - ((unsigned long)rcu_dereference(h->first) & ~LIST_BL_LOCKMASK); + ((unsigned long)rcu_dereference_check(h->first, hlist_bl_is_locked(h)) & ~LIST_BL_LOCKMASK); } /** -- cgit v1.2.3-70-g09d2 From e7b2dcc52b0e2d598a469f01cc460ccdde6869f2 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 12 Mar 2013 15:35:58 -0700 Subject: cgroup: remove cgroup_is_descendant() It was used by ns cgroup, and ns cgroup was removed long ago. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 3 --- kernel/cgroup.c | 28 ---------------------------- 2 files changed, 31 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 5f76829dd75..7e818a3ef60 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -448,9 +448,6 @@ int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen); int cgroup_task_count(const struct cgroup *cgrp); -/* Return true if cgrp is a descendant of the task's cgroup */ -int cgroup_is_descendant(const struct cgroup *cgrp, struct task_struct *task); - /* * Control Group taskset, used to pass around set of tasks to cgroup_subsys * methods. diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 7a6c4c72ca5..f51443fd5f7 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -5035,34 +5035,6 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks) put_css_set_taskexit(cg); } -/** - * cgroup_is_descendant - see if @cgrp is a descendant of @task's cgrp - * @cgrp: the cgroup in question - * @task: the task in question - * - * See if @cgrp is a descendant of @task's cgroup in the appropriate - * hierarchy. - * - * If we are sending in dummytop, then presumably we are creating - * the top cgroup in the subsystem. - * - * Called only by the ns (nsproxy) cgroup. - */ -int cgroup_is_descendant(const struct cgroup *cgrp, struct task_struct *task) -{ - int ret; - struct cgroup *target; - - if (cgrp == dummytop) - return 1; - - target = task_cgroup_from_root(task, cgrp->root); - while (cgrp != target && cgrp!= cgrp->top_cgroup) - cgrp = cgrp->parent; - ret = (cgrp == target); - return ret; -} - static void check_for_release(struct cgroup *cgrp) { /* All of these checks rely on RCU to keep the cgroup -- cgit v1.2.3-70-g09d2 From e62676169118bc2d42e5008b3f8872646313f077 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 12 Mar 2013 17:41:37 -0700 Subject: workqueue: implement current_is_workqueue_rescuer() Implement a function which queries whether it currently is running off a workqueue rescuer. This will be used to convert writeback to workqueue. Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 1 + kernel/workqueue.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 7f6d29a417c..df30763c868 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -451,6 +451,7 @@ extern bool cancel_delayed_work_sync(struct delayed_work *dwork); extern void workqueue_set_max_active(struct workqueue_struct *wq, int max_active); +extern bool current_is_workqueue_rescuer(void); extern bool workqueue_congested(int cpu, struct workqueue_struct *wq); extern unsigned int work_busy(struct work_struct *work); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index c82feac0a87..f5c8bbb9ada 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -4071,6 +4071,19 @@ void workqueue_set_max_active(struct workqueue_struct *wq, int max_active) } EXPORT_SYMBOL_GPL(workqueue_set_max_active); +/** + * current_is_workqueue_rescuer - is %current workqueue rescuer? + * + * Determine whether %current is a workqueue rescuer. Can be used from + * work functions to determine whether it's being run off the rescuer task. + */ +bool current_is_workqueue_rescuer(void) +{ + struct worker *worker = current_wq_worker(); + + return worker && worker == worker->current_pwq->wq->rescuer; +} + /** * workqueue_congested - test whether a workqueue is congested * @cpu: CPU in question -- cgit v1.2.3-70-g09d2 From e86ac13b031cf71d8f40ff513e627aac80e6b765 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Mon, 11 Mar 2013 23:16:35 +0000 Subject: drivers: net: ethernet: cpsw: change cpts_active_slave to active_slave Change cpts_active_slave to active_slave so that the same DT property can be used to ethtool and SIOCGMIIPHY. CC: Richard Cochran Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 7 ++++--- arch/arm/boot/dts/am33xx.dtsi | 2 +- drivers/net/ethernet/ti/cpsw.c | 10 +++++----- include/linux/platform_data/cpsw.h | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 8e49c420092..4f2ca6b4a18 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -15,7 +15,8 @@ Required properties: - mac_control : Specifies Default MAC control register content for the specific platform - slaves : Specifies number for slaves -- cpts_active_slave : Specifies the slave to use for time stamping +- active_slave : Specifies the slave to use for time stamping, + ethtool and SIOCGMIIPHY - cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds - cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds @@ -52,7 +53,7 @@ Examples: rx_descs = <64>; mac_control = <0x20>; slaves = <2>; - cpts_active_slave = <0>; + active_slave = <0>; cpts_clock_mult = <0x80000000>; cpts_clock_shift = <29>; cpsw_emac0: slave@0 { @@ -78,7 +79,7 @@ Examples: rx_descs = <64>; mac_control = <0x20>; slaves = <2>; - cpts_active_slave = <0>; + active_slave = <0>; cpts_clock_mult = <0x80000000>; cpts_clock_shift = <29>; cpsw_emac0: slave@0 { diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 0957645b73a..91fe4f148f8 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -349,7 +349,7 @@ rx_descs = <64>; mac_control = <0x20>; slaves = <2>; - cpts_active_slave = <0>; + active_slave = <0>; cpts_clock_mult = <0x80000000>; cpts_clock_shift = <29>; reg = <0x4a100000 0x800 diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 01ffbc48698..98aa17a9516 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -942,7 +942,7 @@ static void cpsw_ndo_change_rx_flags(struct net_device *ndev, int flags) static void cpsw_hwtstamp_v1(struct cpsw_priv *priv) { - struct cpsw_slave *slave = &priv->slaves[priv->data.cpts_active_slave]; + struct cpsw_slave *slave = &priv->slaves[priv->data.active_slave]; u32 ts_en, seq_id; if (!priv->cpts->tx_enable && !priv->cpts->rx_enable) { @@ -971,7 +971,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) if (priv->data.dual_emac) slave = &priv->slaves[priv->emac_port]; else - slave = &priv->slaves[priv->data.cpts_active_slave]; + slave = &priv->slaves[priv->data.active_slave]; ctrl = slave_read(slave, CPSW2_CONTROL); ctrl &= ~CTRL_ALL_TS_MASK; @@ -1282,12 +1282,12 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->slaves = prop; - if (of_property_read_u32(node, "cpts_active_slave", &prop)) { - pr_err("Missing cpts_active_slave property in the DT.\n"); + if (of_property_read_u32(node, "active_slave", &prop)) { + pr_err("Missing active_slave property in the DT.\n"); ret = -EINVAL; goto error_ret; } - data->cpts_active_slave = prop; + data->active_slave = prop; if (of_property_read_u32(node, "cpts_clock_mult", &prop)) { pr_err("Missing cpts_clock_mult property in the DT.\n"); diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h index 798fb80b024..bb3cd58d71e 100644 --- a/include/linux/platform_data/cpsw.h +++ b/include/linux/platform_data/cpsw.h @@ -30,7 +30,7 @@ struct cpsw_platform_data { u32 channels; /* number of cpdma channels (symmetric) */ u32 slaves; /* number of slave cpgmac ports */ struct cpsw_slave_data *slave_data; - u32 cpts_active_slave; /* time stamping slave */ + u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ u32 ale_entries; /* ale table size */ -- cgit v1.2.3-70-g09d2 From bbd44f6bd9d1aa735b180b29b5719d63a8e87b55 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 7 Feb 2013 16:31:58 +0100 Subject: ARM: at91/avr32/atmel_lcdfb: add platform device-id table Add platform device-id table in order to identify the controller and determine its configuration. The currently used configuration parameters are: have_alt_pixclock - SOC uses an alternate pixel-clock calculation formula (at91sam9g45 non-ES) have_hozval - SOC has a HOZVAL field in LCDFRMCFG which is used to determine the linesize for STN displays (at91sam9261, at921sam9g10 and at32ap) have_intensity_bit - SOC uses IBGR:555 rather than BGR:565 16-bit pixel layout (at91sam9261, at91sam9263 and at91sam9rl) This allows us to remove all the remaining uses of cpu_is macros from the driver. Tested on at91sam9263 and at91sam9g45, compile-tested for other AT91-SOCs, and untested for AVR32. Signed-off-by: Johan Hovold Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam9261.c | 3 +- arch/arm/mach-at91/at91sam9261_devices.c | 6 ++- arch/arm/mach-at91/at91sam9263.c | 2 +- arch/arm/mach-at91/at91sam9263_devices.c | 2 +- arch/arm/mach-at91/at91sam9g45.c | 3 +- arch/arm/mach-at91/at91sam9g45_devices.c | 6 ++- arch/arm/mach-at91/at91sam9rl.c | 2 +- arch/arm/mach-at91/at91sam9rl_devices.c | 2 +- arch/avr32/mach-at32ap/at32ap700x.c | 2 + drivers/video/atmel_lcdfb.c | 89 ++++++++++++++++++++++++++++---- include/video/atmel_lcdc.h | 4 +- 11 files changed, 102 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index 5838f12e669..0204f4cc9eb 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -169,7 +169,8 @@ static struct clk *periph_clocks[] __initdata = { }; static struct clk_lookup periph_clocks_lookups[] = { - CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &hck1), + CLKDEV_CON_DEV_ID("hclk", "at91sam9261-lcdfb.0", &hck1), + CLKDEV_CON_DEV_ID("hclk", "at91sam9g10-lcdfb.0", &hck1), CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk), CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk), CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk), diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c index 92e0f861084..629ea5fc95c 100644 --- a/arch/arm/mach-at91/at91sam9261_devices.c +++ b/arch/arm/mach-at91/at91sam9261_devices.c @@ -488,7 +488,6 @@ static struct resource lcdc_resources[] = { }; static struct platform_device at91_lcdc_device = { - .name = "atmel_lcdfb", .id = 0, .dev = { .dma_mask = &lcdc_dmamask, @@ -505,6 +504,11 @@ void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) return; } + if (cpu_is_at91sam9g10()) + at91_lcdc_device.name = "at91sam9g10-lcdfb"; + else + at91_lcdc_device.name = "at91sam9261-lcdfb"; + #if defined(CONFIG_FB_ATMEL_STN) at91_set_A_periph(AT91_PIN_PB0, 0); /* LCDVSYNC */ at91_set_A_periph(AT91_PIN_PB1, 0); /* LCDHSYNC */ diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index 520a63d3132..2282fd7ad3e 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -190,7 +190,7 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk), - CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk), + CLKDEV_CON_DEV_ID("hclk", "at91sam9263-lcdfb.0", &lcdc_clk), CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk), CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk), CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk), diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c index ed666f5cb01..858c8aac2da 100644 --- a/arch/arm/mach-at91/at91sam9263_devices.c +++ b/arch/arm/mach-at91/at91sam9263_devices.c @@ -848,7 +848,7 @@ static struct resource lcdc_resources[] = { }; static struct platform_device at91_lcdc_device = { - .name = "atmel_lcdfb", + .name = "at91sam9263-lcdfb", .id = 0, .dev = { .dma_mask = &lcdc_dmamask, diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index ea6b62bcbe7..c68960d8224 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -228,7 +228,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_ID("hclk", &macb_clk), /* One additional fake clock for ohci */ CLKDEV_CON_ID("ohci_clk", &uhphs_clk), - CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk), + CLKDEV_CON_DEV_ID("hclk", "at91sam9g45-lcdfb.0", &lcdc_clk), + CLKDEV_CON_DEV_ID("hclk", "at91sam9g45es-lcdfb.0", &lcdc_clk), CLKDEV_CON_DEV_ID("ehci_clk", "atmel-ehci", &uhphs_clk), CLKDEV_CON_DEV_ID("hclk", "atmel_usba_udc", &utmi_clk), CLKDEV_CON_DEV_ID("pclk", "atmel_usba_udc", &udphs_clk), diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index 827c9f2a70f..fe626d431b6 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -981,7 +981,6 @@ static struct resource lcdc_resources[] = { }; static struct platform_device at91_lcdc_device = { - .name = "atmel_lcdfb", .id = 0, .dev = { .dma_mask = &lcdc_dmamask, @@ -997,6 +996,11 @@ void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) if (!data) return; + if (cpu_is_at91sam9g45es()) + at91_lcdc_device.name = "at91sam9g45es-lcdfb"; + else + at91_lcdc_device.name = "at91sam9g45-lcdfb"; + at91_set_A_periph(AT91_PIN_PE0, 0); /* LCDDPWR */ at91_set_A_periph(AT91_PIN_PE2, 0); /* LCDCC */ diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index 4cd4fa985d0..3de3e04d0f8 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -179,7 +179,7 @@ static struct clk *periph_clocks[] __initdata = { }; static struct clk_lookup periph_clocks_lookups[] = { - CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk), + CLKDEV_CON_DEV_ID("hclk", "at91sam9rl-lcdfb.0", &lcdc_clk), CLKDEV_CON_DEV_ID("hclk", "atmel_usba_udc", &utmi_clk), CLKDEV_CON_DEV_ID("pclk", "atmel_usba_udc", &udphs_clk), CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk), diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c index ddf223ff35c..352468f265a 100644 --- a/arch/arm/mach-at91/at91sam9rl_devices.c +++ b/arch/arm/mach-at91/at91sam9rl_devices.c @@ -514,7 +514,7 @@ static struct resource lcdc_resources[] = { }; static struct platform_device at91_lcdc_device = { - .name = "atmel_lcdfb", + .name = "at91sam9rl-lcdfb", .id = 0, .dev = { .dma_mask = &lcdc_dmamask, diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index cd25b01b584..7c2f6685bf4 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1530,6 +1530,8 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, memcpy(info, data, sizeof(struct atmel_lcdfb_info)); info->default_monspecs = monspecs; + pdev->name = "at32ap-lcdfb"; + platform_device_register(pdev); return pdev; diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 2effd35da58..c1a2914447e 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -34,6 +34,77 @@ #define ATMEL_LCDC_DMA_BURST_LEN 8 /* words */ #define ATMEL_LCDC_FIFO_SIZE 512 /* words */ +struct atmel_lcdfb_config { + bool have_alt_pixclock; + bool have_hozval; + bool have_intensity_bit; +}; + +static struct atmel_lcdfb_config at91sam9261_config = { + .have_hozval = true, + .have_intensity_bit = true, +}; + +static struct atmel_lcdfb_config at91sam9263_config = { + .have_intensity_bit = true, +}; + +static struct atmel_lcdfb_config at91sam9g10_config = { + .have_hozval = true, +}; + +static struct atmel_lcdfb_config at91sam9g45_config = { + .have_alt_pixclock = true, +}; + +static struct atmel_lcdfb_config at91sam9g45es_config = { +}; + +static struct atmel_lcdfb_config at91sam9rl_config = { + .have_intensity_bit = true, +}; + +static struct atmel_lcdfb_config at32ap_config = { + .have_hozval = true, +}; + +static const struct platform_device_id atmel_lcdfb_devtypes[] = { + { + .name = "at91sam9261-lcdfb", + .driver_data = (unsigned long)&at91sam9261_config, + }, { + .name = "at91sam9263-lcdfb", + .driver_data = (unsigned long)&at91sam9263_config, + }, { + .name = "at91sam9g10-lcdfb", + .driver_data = (unsigned long)&at91sam9g10_config, + }, { + .name = "at91sam9g45-lcdfb", + .driver_data = (unsigned long)&at91sam9g45_config, + }, { + .name = "at91sam9g45es-lcdfb", + .driver_data = (unsigned long)&at91sam9g45es_config, + }, { + .name = "at91sam9rl-lcdfb", + .driver_data = (unsigned long)&at91sam9rl_config, + }, { + .name = "at32ap-lcdfb", + .driver_data = (unsigned long)&at32ap_config, + }, { + /* terminator */ + } +}; + +static struct atmel_lcdfb_config * +atmel_lcdfb_get_config(struct platform_device *pdev) +{ + unsigned long data; + + data = platform_get_device_id(pdev)->driver_data; + + return (struct atmel_lcdfb_config *)data; +} + #if defined(CONFIG_ARCH_AT91) #define ATMEL_LCDFB_FBINFO_DEFAULT (FBINFO_DEFAULT \ | FBINFO_PARTIAL_PAN_OK \ @@ -199,8 +270,7 @@ static unsigned long compute_hozval(struct atmel_lcdfb_info *sinfo, unsigned long lcdcon2; unsigned long value; - if (!(cpu_is_at91sam9261() || cpu_is_at91sam9g10() - || cpu_is_at32ap7000())) + if (!sinfo->config->have_hozval) return xres; lcdcon2 = lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2); @@ -426,7 +496,7 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, break; case 16: /* Older SOCs use IBGR:555 rather than BGR:565. */ - if (sinfo->have_intensity_bit) + if (sinfo->config->have_intensity_bit) var->green.length = 5; else var->green.length = 6; @@ -534,7 +604,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info) /* Now, the LCDC core... */ /* Set pixel clock */ - if (cpu_is_at91sam9g45() && !cpu_is_at91sam9g45es()) + if (sinfo->config->have_alt_pixclock) pix_factor = 1; clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; @@ -686,7 +756,7 @@ static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, case FB_VISUAL_PSEUDOCOLOR: if (regno < 256) { - if (sinfo->have_intensity_bit) { + if (sinfo->config->have_intensity_bit) { /* old style I+BGR:555 */ val = ((red >> 11) & 0x001f); val |= ((green >> 6) & 0x03e0); @@ -874,10 +944,9 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) } sinfo->info = info; sinfo->pdev = pdev; - if (cpu_is_at91sam9261() || cpu_is_at91sam9263() || - cpu_is_at91sam9rl()) { - sinfo->have_intensity_bit = true; - } + sinfo->config = atmel_lcdfb_get_config(pdev); + if (!sinfo->config) + goto free_info; strcpy(info->fix.id, sinfo->pdev->name); info->flags = ATMEL_LCDFB_FBINFO_DEFAULT; @@ -1146,7 +1215,7 @@ static struct platform_driver atmel_lcdfb_driver = { .remove = __exit_p(atmel_lcdfb_remove), .suspend = atmel_lcdfb_suspend, .resume = atmel_lcdfb_resume, - + .id_table = atmel_lcdfb_devtypes, .driver = { .name = "atmel_lcdfb", .owner = THIS_MODULE, diff --git a/include/video/atmel_lcdc.h b/include/video/atmel_lcdc.h index 8deb22672ad..0f5a2fc69af 100644 --- a/include/video/atmel_lcdc.h +++ b/include/video/atmel_lcdc.h @@ -31,6 +31,7 @@ #define ATMEL_LCDC_WIRING_BGR 0 #define ATMEL_LCDC_WIRING_RGB 1 +struct atmel_lcdfb_config; /* LCD Controller info data structure, stored in device platform_data */ struct atmel_lcdfb_info { @@ -61,7 +62,8 @@ struct atmel_lcdfb_info { void (*atmel_lcdfb_power_control)(int on); struct fb_monspecs *default_monspecs; u32 pseudo_palette[16]; - bool have_intensity_bit; + + struct atmel_lcdfb_config *config; }; #define ATMEL_LCDC_DMABADDR1 0x00 -- cgit v1.2.3-70-g09d2 From eaa907c546f76222227dfc41784b22588af1e3d7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 6 Mar 2013 11:18:36 +0000 Subject: tick: Provide a check for a forced broadcast pending On the CPU which gets woken along with the target CPU of the broadcast the following happens: deep_idle() <-- spurious wakeup broadcast_exit() set forced bit enable interrupts <-- Nothing happens disable interrupts broadcast_enter() <-- Here we observe the forced bit is set deep_idle() Now after that the target CPU of the broadcast runs the broadcast handler and finds the other CPU in both the broadcast and the forced mask, sends the IPI and stuff gets back to normal. So it's not actually harmful, just more evidence for the theory, that hardware designers have access to very special drug supplies. Now there is no point in going back to deep idle just to wake up again right away via an IPI. Provide a check which allows the idle code to avoid the deep idle transition. Signed-off-by: Thomas Gleixner Cc: LAK Cc: John Stultz Cc: Arjan van de Veen Cc: Lorenzo Pieralisi Tested-by: Santosh Shilimkar Cc: Jason Liu Link: http://lkml.kernel.org/r/20130306111537.565418308@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/clockchips.h | 6 ++++++ kernel/time/tick-broadcast.c | 12 ++++++++++++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index 494d33ea78f..646aac136ee 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -175,6 +175,12 @@ extern void tick_broadcast(const struct cpumask *mask); extern int tick_receive_broadcast(void); #endif +#if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) && defined(CONFIG_TICK_ONESHOT) +extern int tick_check_broadcast_expired(void); +#else +static inline int tick_check_broadcast_expired(void) { return 0; } +#endif + #ifdef CONFIG_GENERIC_CLOCKEVENTS extern void clockevents_notify(unsigned long reason, void *arg); #else diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 2100aad6b5f..d76d816afc5 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -403,6 +403,18 @@ struct cpumask *tick_get_broadcast_oneshot_mask(void) return tick_broadcast_oneshot_mask; } +/* + * Called before going idle with interrupts disabled. Checks whether a + * broadcast event from the other core is about to happen. We detected + * that in tick_broadcast_oneshot_control(). The callsite can use this + * to avoid a deep idle transition as we are about to get the + * broadcast IPI right away. + */ +int tick_check_broadcast_expired(void) +{ + return cpumask_test_cpu(smp_processor_id(), tick_broadcast_force_mask); +} + /* * Set broadcast interrupt affinity */ -- cgit v1.2.3-70-g09d2 From 030e79f658de11da43d32e7ad814b5d2d64c8bac Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 11 Mar 2013 18:27:21 -0700 Subject: ASoC: add snd_soc_register_component() Current ASoC has register function for platform/codec/dai/card, but doesn't have for cpu. It often produces confusion and fault on ASoC. As result of ASoC community discussion, we consider new struct snd_soc_component for CPU/CODEC, and will switch over to use it. This patch adds very basic struct snd_soc_component, and register function for it. Signed-off-by: Kuninori Morimoto Acked-by: Liam Girdwood Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- include/sound/soc.h | 19 +++++++++++++ sound/soc/soc-core.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index a6a059ca387..8c46d0a7e2c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -324,6 +324,8 @@ struct snd_soc_dai_link; struct snd_soc_platform_driver; struct snd_soc_codec; struct snd_soc_codec_driver; +struct snd_soc_component; +struct snd_soc_component_driver; struct soc_enum; struct snd_soc_jack; struct snd_soc_jack_zone; @@ -377,6 +379,10 @@ int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_codec(struct device *dev); +int snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, int num_dai); +void snd_soc_unregister_component(struct device *dev); int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, unsigned int reg); int snd_soc_codec_readable_register(struct snd_soc_codec *codec, @@ -841,6 +847,19 @@ struct snd_soc_platform { #endif }; +struct snd_soc_component_driver { +}; + +struct snd_soc_component { + const char *name; + int id; + int num_dai; + struct device *dev; + struct list_head list; + + const struct snd_soc_component_driver *driver; +}; + struct snd_soc_dai_link { /* config - must be set by machine driver */ const char *name; /* Codec name */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b7e84a7cd9e..9e6118573fe 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -58,6 +58,7 @@ static DEFINE_MUTEX(client_mutex); static LIST_HEAD(dai_list); static LIST_HEAD(platform_list); static LIST_HEAD(codec_list); +static LIST_HEAD(component_list); /* * This is a timeout to do a DAPM powerdown after a stream is closed(). @@ -4137,6 +4138,82 @@ found: } EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); + +/** + * snd_soc_register_component - Register a component with the ASoC core + * + */ +int snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *cmpnt_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + struct snd_soc_component *cmpnt; + int ret; + + dev_dbg(dev, "component register %s\n", dev_name(dev)); + + cmpnt = devm_kzalloc(dev, sizeof(*cmpnt), GFP_KERNEL); + if (!cmpnt) { + dev_err(dev, "ASoC: Failed to allocate memory\n"); + return -ENOMEM; + } + + cmpnt->name = fmt_single_name(dev, &cmpnt->id); + if (!cmpnt->name) { + dev_err(dev, "ASoC: Failed to simplifying name\n"); + return -ENOMEM; + } + + cmpnt->dev = dev; + cmpnt->driver = cmpnt_drv; + cmpnt->num_dai = num_dai; + + ret = snd_soc_register_dais(dev, dai_drv, num_dai); + if (ret < 0) { + dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret); + goto error_component_name; + } + + mutex_lock(&client_mutex); + list_add(&cmpnt->list, &component_list); + mutex_unlock(&client_mutex); + + dev_dbg(cmpnt->dev, "ASoC: Registered component '%s'\n", cmpnt->name); + + return ret; + +error_component_name: + kfree(cmpnt->name); + + return ret; +} + +/** + * snd_soc_unregister_component - Unregister a component from the ASoC core + * + */ +void snd_soc_unregister_component(struct device *dev) +{ + struct snd_soc_component *cmpnt; + + list_for_each_entry(cmpnt, &component_list, list) { + if (dev == cmpnt->dev) + goto found; + } + return; + +found: + snd_soc_unregister_dais(dev, cmpnt->num_dai); + + mutex_lock(&client_mutex); + list_del(&cmpnt->list); + mutex_unlock(&client_mutex); + + dev_dbg(dev, "ASoC: Unregistered component '%s'\n", cmpnt->name); + kfree(cmpnt->name); +} + /* Retrieve a card's name from device tree */ int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname) -- cgit v1.2.3-70-g09d2 From be871b7e54711479d3b9d3617d49898770830db2 Mon Sep 17 00:00:00 2001 From: Michal Hocko Date: Tue, 12 Mar 2013 17:21:19 +0100 Subject: device: separate all subsys mutexes ca22e56d (driver-core: implement 'sysdev' functionality for regular devices and buses) has introduced bus_register macro with a static key to distinguish different subsys mutex classes. This however doesn't work for different subsys which use a common registering function. One example is subsys_system_register (and mce_device and cpu_device). In the end this leads to the following lockdep splat: [ 207.271924] ====================================================== [ 207.271932] [ INFO: possible circular locking dependency detected ] [ 207.271942] 3.9.0-rc1-0.7-default+ #34 Not tainted [ 207.271948] ------------------------------------------------------- [ 207.271957] bash/10493 is trying to acquire lock: [ 207.271963] (subsys mutex){+.+.+.}, at: [] bus_remove_device+0x37/0x1c0 [ 207.271987] [ 207.271987] but task is already holding lock: [ 207.271995] (cpu_hotplug.lock){+.+.+.}, at: [] cpu_hotplug_begin+0x2f/0x60 [ 207.272012] [ 207.272012] which lock already depends on the new lock. [ 207.272012] [ 207.272023] [ 207.272023] the existing dependency chain (in reverse order) is: [ 207.272033] [ 207.272033] -> #4 (cpu_hotplug.lock){+.+.+.}: [ 207.272044] [] lock_acquire+0xe9/0x120 [ 207.272056] [] mutex_lock_nested+0x37/0x360 [ 207.272069] [] get_online_cpus+0x29/0x40 [ 207.272082] [] drain_all_stock+0x30/0x150 [ 207.272094] [] mem_cgroup_reclaim+0xaa/0xe0 [ 207.272104] [] __mem_cgroup_try_charge+0x51e/0xcf0 [ 207.272114] [] mem_cgroup_charge_common+0x36/0x60 [ 207.272125] [] mem_cgroup_newpage_charge+0x2a/0x30 [ 207.272135] [] do_wp_page+0x231/0x830 [ 207.272147] [] handle_pte_fault+0x19e/0x8d0 [ 207.272157] [] handle_mm_fault+0x158/0x1e0 [ 207.272166] [] do_page_fault+0x2a3/0x4e0 [ 207.272178] [] page_fault+0x28/0x30 [ 207.272189] [ 207.272189] -> #3 (&mm->mmap_sem){++++++}: [ 207.272199] [] lock_acquire+0xe9/0x120 [ 207.272208] [] might_fault+0x6d/0x90 [ 207.272218] [] filldir64+0xb3/0x120 [ 207.272229] [] call_filldir+0x89/0x130 [ext3] [ 207.272248] [] ext3_readdir+0x6b7/0x7e0 [ext3] [ 207.272263] [] vfs_readdir+0xa9/0xc0 [ 207.272273] [] sys_getdents64+0x9b/0x110 [ 207.272284] [] system_call_fastpath+0x16/0x1b [ 207.272296] [ 207.272296] -> #2 (&type->i_mutex_dir_key#3){+.+.+.}: [ 207.272309] [] lock_acquire+0xe9/0x120 [ 207.272319] [] mutex_lock_nested+0x37/0x360 [ 207.272329] [] link_path_walk+0x6f4/0x9a0 [ 207.272339] [] path_openat+0xba/0x470 [ 207.272349] [] do_filp_open+0x48/0xa0 [ 207.272358] [] file_open_name+0xdc/0x110 [ 207.272369] [] filp_open+0x35/0x40 [ 207.272378] [] _request_firmware+0x52e/0xb20 [ 207.272389] [] request_firmware+0x16/0x20 [ 207.272399] [] request_microcode_fw+0x61/0xd0 [microcode] [ 207.272416] [] microcode_init_cpu+0x104/0x150 [microcode] [ 207.272431] [] mc_device_add+0x7c/0xb0 [microcode] [ 207.272444] [] subsys_interface_register+0xc9/0x100 [ 207.272457] [] 0xffffffffa04fc0f4 [ 207.272472] [] do_one_initcall+0x42/0x180 [ 207.272485] [] load_module+0x19df/0x1b70 [ 207.272499] [] sys_init_module+0xe6/0x130 [ 207.272511] [] system_call_fastpath+0x16/0x1b [ 207.272523] [ 207.272523] -> #1 (umhelper_sem){++++.+}: [ 207.272537] [] lock_acquire+0xe9/0x120 [ 207.272548] [] down_read+0x34/0x50 [ 207.272559] [] usermodehelper_read_trylock+0x4f/0x100 [ 207.272575] [] _request_firmware+0x59d/0xb20 [ 207.272587] [] request_firmware+0x16/0x20 [ 207.272599] [] request_microcode_fw+0x61/0xd0 [microcode] [ 207.272613] [] microcode_init_cpu+0x104/0x150 [microcode] [ 207.272627] [] mc_device_add+0x7c/0xb0 [microcode] [ 207.272641] [] subsys_interface_register+0xc9/0x100 [ 207.272654] [] 0xffffffffa04fc0f4 [ 207.272666] [] do_one_initcall+0x42/0x180 [ 207.272678] [] load_module+0x19df/0x1b70 [ 207.272690] [] sys_init_module+0xe6/0x130 [ 207.272702] [] system_call_fastpath+0x16/0x1b [ 207.272715] [ 207.272715] -> #0 (subsys mutex){+.+.+.}: [ 207.272729] [] __lock_acquire+0x13b2/0x15f0 [ 207.272740] [] lock_acquire+0xe9/0x120 [ 207.272751] [] mutex_lock_nested+0x37/0x360 [ 207.272763] [] bus_remove_device+0x37/0x1c0 [ 207.272775] [] device_del+0x134/0x1f0 [ 207.272786] [] device_unregister+0x22/0x60 [ 207.272798] [] mce_cpu_callback+0x15e/0x1ad [ 207.272812] [] notifier_call_chain+0x72/0x130 [ 207.272824] [] __raw_notifier_call_chain+0xe/0x10 [ 207.272839] [] _cpu_down+0x1d6/0x350 [ 207.272851] [] cpu_down+0x40/0x60 [ 207.272862] [] store_online+0x75/0xe0 [ 207.272874] [] dev_attr_store+0x20/0x30 [ 207.272886] [] sysfs_write_file+0xd9/0x150 [ 207.272900] [] vfs_write+0xcb/0x130 [ 207.272911] [] sys_write+0x64/0xa0 [ 207.272923] [] system_call_fastpath+0x16/0x1b [ 207.272936] [ 207.272936] other info that might help us debug this: [ 207.272936] [ 207.272952] Chain exists of: [ 207.272952] subsys mutex --> &mm->mmap_sem --> cpu_hotplug.lock [ 207.272952] [ 207.272973] Possible unsafe locking scenario: [ 207.272973] [ 207.272984] CPU0 CPU1 [ 207.272992] ---- ---- [ 207.273000] lock(cpu_hotplug.lock); [ 207.273009] lock(&mm->mmap_sem); [ 207.273020] lock(cpu_hotplug.lock); [ 207.273031] lock(subsys mutex); [ 207.273040] [ 207.273040] *** DEADLOCK *** [ 207.273040] [ 207.273055] 5 locks held by bash/10493: [ 207.273062] #0: (&buffer->mutex){+.+.+.}, at: [] sysfs_write_file+0x49/0x150 [ 207.273080] #1: (s_active#150){.+.+.+}, at: [] sysfs_write_file+0xc2/0x150 [ 207.273099] #2: (x86_cpu_hotplug_driver_mutex){+.+.+.}, at: [] cpu_hotplug_driver_lock+0x17/0x20 [ 207.273121] #3: (cpu_add_remove_lock){+.+.+.}, at: [] cpu_down+0x2c/0x60 [ 207.273140] #4: (cpu_hotplug.lock){+.+.+.}, at: [] cpu_hotplug_begin+0x2f/0x60 [ 207.273158] [ 207.273158] stack backtrace: [ 207.273170] Pid: 10493, comm: bash Not tainted 3.9.0-rc1-0.7-default+ #34 [ 207.273180] Call Trace: [ 207.273192] [] print_circular_bug+0x223/0x310 [ 207.273204] [] __lock_acquire+0x13b2/0x15f0 [ 207.273216] [] ? sysfs_hash_and_remove+0x60/0xc0 [ 207.273227] [] lock_acquire+0xe9/0x120 [ 207.273239] [] ? bus_remove_device+0x37/0x1c0 [ 207.273251] [] mutex_lock_nested+0x37/0x360 [ 207.273263] [] ? bus_remove_device+0x37/0x1c0 [ 207.273274] [] ? sysfs_hash_and_remove+0x60/0xc0 [ 207.273286] [] bus_remove_device+0x37/0x1c0 [ 207.273298] [] device_del+0x134/0x1f0 [ 207.273309] [] device_unregister+0x22/0x60 [ 207.273321] [] mce_cpu_callback+0x15e/0x1ad [ 207.273332] [] notifier_call_chain+0x72/0x130 [ 207.273344] [] __raw_notifier_call_chain+0xe/0x10 [ 207.273356] [] _cpu_down+0x1d6/0x350 [ 207.273368] [] ? cpu_hotplug_driver_lock+0x17/0x20 [ 207.273380] [] cpu_down+0x40/0x60 [ 207.273391] [] store_online+0x75/0xe0 [ 207.273402] [] dev_attr_store+0x20/0x30 [ 207.273413] [] sysfs_write_file+0xd9/0x150 [ 207.273425] [] vfs_write+0xcb/0x130 [ 207.273436] [] sys_write+0x64/0xa0 [ 207.273447] [] system_call_fastpath+0x16/0x1b Which reports a false possitive deadlock because it sees: 1) load_module -> subsys_interface_register -> mc_deveice_add (*) -> subsys->p->mutex -> link_path_walk -> lookup_slow -> i_mutex 2) sys_write -> _cpu_down -> cpu_hotplug_begin -> cpu_hotplug.lock -> mce_cpu_callback -> mce_device_remove(**) -> device_unregister -> bus_remove_device -> subsys mutex 3) vfs_readdir -> i_mutex -> filldir64 -> might_fault -> might_lock_read(mmap_sem) -> page_fault -> mmap_sem -> drain_all_stock -> cpu_hotplug.lock but 1) takes cpu_subsys subsys (*) but 2) takes mce_device subsys (**) so the deadlock is not possible AFAICS. The fix is quite simple. We can pull the key inside bus_type structure because they are defined per device so the pointer will be unique as well. bus_register doesn't need to be a macro anymore so change it to the inline. We could get rid of __bus_register as there is no other caller but maybe somebody will want to use a different key so keep it around for now. Reported-by: Li Zefan Signed-off-by: Michal Hocko Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 8 ++++---- include/linux/device.h | 12 +++--------- 2 files changed, 7 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 519865b53f7..8a00dec574d 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -898,18 +898,18 @@ static ssize_t bus_uevent_store(struct bus_type *bus, static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store); /** - * __bus_register - register a driver-core subsystem + * bus_register - register a driver-core subsystem * @bus: bus to register - * @key: lockdep class key * * Once we have that, we register the bus with the kobject * infrastructure, then register the children subsystems it has: * the devices and drivers that belong to the subsystem. */ -int __bus_register(struct bus_type *bus, struct lock_class_key *key) +int bus_register(struct bus_type *bus) { int retval; struct subsys_private *priv; + struct lock_class_key *key = &bus->lock_key; priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); if (!priv) @@ -981,7 +981,7 @@ out: bus->p = NULL; return retval; } -EXPORT_SYMBOL_GPL(__bus_register); +EXPORT_SYMBOL_GPL(bus_register); /** * bus_unregister - remove a bus from the system diff --git a/include/linux/device.h b/include/linux/device.h index 9d6464ea99c..4a7c4a84afe 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -111,17 +111,11 @@ struct bus_type { struct iommu_ops *iommu_ops; struct subsys_private *p; + struct lock_class_key lock_key; }; -/* This is a #define to keep the compiler from merging different - * instances of the __key variable */ -#define bus_register(subsys) \ -({ \ - static struct lock_class_key __key; \ - __bus_register(subsys, &__key); \ -}) -extern int __must_check __bus_register(struct bus_type *bus, - struct lock_class_key *key); +extern int __must_check bus_register(struct bus_type *bus); + extern void bus_unregister(struct bus_type *bus); extern int __must_check bus_rescan_devices(struct bus_type *bus); -- cgit v1.2.3-70-g09d2 From f792685006274a850e6cc0ea9ade275ccdfc90bc Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 5 Mar 2013 18:05:46 +0100 Subject: math64: New div64_u64_rem helper Provide an extended version of div64_u64() that also returns the remainder of the division. We are going to need this to refine the cputime scaling code. Signed-off-by: Frederic Weisbecker Cc: Stanislaw Gruszka Cc: Steven Rostedt Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Andrew Morton --- include/linux/math64.h | 19 ++++++++++++++++++- lib/div64.c | 19 +++++++++++++------ 2 files changed, 31 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/math64.h b/include/linux/math64.h index b8ba8554472..931a619407b 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -29,6 +29,15 @@ static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder) return dividend / divisor; } +/** + * div64_u64_rem - unsigned 64bit divide with 64bit divisor + */ +static inline u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + /** * div64_u64 - unsigned 64bit divide with 64bit divisor */ @@ -61,8 +70,16 @@ static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); #endif +#ifndef div64_u64_rem +extern u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder); +#endif + #ifndef div64_u64 -extern u64 div64_u64(u64 dividend, u64 divisor); +static inline u64 div64_u64(u64 dividend, u64 divisor) +{ + u64 remainder; + return div64_u64_rem(dividend, divisor, &remainder); +} #endif #ifndef div64_s64 diff --git a/lib/div64.c b/lib/div64.c index a163b6caef7..3af5728d95f 100644 --- a/lib/div64.c +++ b/lib/div64.c @@ -79,9 +79,10 @@ EXPORT_SYMBOL(div_s64_rem); #endif /** - * div64_u64 - unsigned 64bit divide with 64bit divisor + * div64_u64_rem - unsigned 64bit divide with 64bit divisor and 64bit remainder * @dividend: 64bit dividend * @divisor: 64bit divisor + * @remainder: 64bit remainder * * This implementation is a modified version of the algorithm proposed * by the book 'Hacker's Delight'. The original source and full proof @@ -89,27 +90,33 @@ EXPORT_SYMBOL(div_s64_rem); * * 'http://www.hackersdelight.org/HDcode/newCode/divDouble.c.txt' */ -#ifndef div64_u64 -u64 div64_u64(u64 dividend, u64 divisor) +#ifndef div64_u64_rem +u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder) { u32 high = divisor >> 32; u64 quot; if (high == 0) { - quot = div_u64(dividend, divisor); + u32 rem32; + quot = div_u64_rem(dividend, divisor, &rem32); + *remainder = rem32; } else { int n = 1 + fls(high); quot = div_u64(dividend >> n, divisor >> n); if (quot != 0) quot--; - if ((dividend - quot * divisor) >= divisor) + + *remainder = dividend - quot * divisor; + if (*remainder >= divisor) { quot++; + *remainder -= divisor; + } } return quot; } -EXPORT_SYMBOL(div64_u64); +EXPORT_SYMBOL(div64_u64_rem); #endif /** -- cgit v1.2.3-70-g09d2 From 8425e3d5bdbe8e741d2c73cf3189ed59b4038b84 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 13 Mar 2013 16:51:36 -0700 Subject: workqueue: inline trivial wrappers There's no reason to make these trivial wrappers full (exported) functions. Inline the followings. queue_work() queue_delayed_work() mod_delayed_work() schedule_work_on() schedule_work() schedule_delayed_work_on() schedule_delayed_work() keventd_up() Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 123 +++++++++++++++++++++++++++++++++++++++++----- kernel/workqueue.c | 111 ----------------------------------------- 2 files changed, 111 insertions(+), 123 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index df30763c868..835d12b7696 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -417,28 +417,16 @@ int apply_workqueue_attrs(struct workqueue_struct *wq, extern bool queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work); -extern bool queue_work(struct workqueue_struct *wq, struct work_struct *work); extern bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay); -extern bool queue_delayed_work(struct workqueue_struct *wq, - struct delayed_work *work, unsigned long delay); extern bool mod_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay); -extern bool mod_delayed_work(struct workqueue_struct *wq, - struct delayed_work *dwork, unsigned long delay); extern void flush_workqueue(struct workqueue_struct *wq); extern void drain_workqueue(struct workqueue_struct *wq); extern void flush_scheduled_work(void); -extern bool schedule_work_on(int cpu, struct work_struct *work); -extern bool schedule_work(struct work_struct *work); -extern bool schedule_delayed_work_on(int cpu, struct delayed_work *work, - unsigned long delay); -extern bool schedule_delayed_work(struct delayed_work *work, - unsigned long delay); extern int schedule_on_each_cpu(work_func_t func); -extern int keventd_up(void); int execute_in_process_context(work_func_t fn, struct execute_work *); @@ -455,6 +443,117 @@ extern bool current_is_workqueue_rescuer(void); extern bool workqueue_congested(int cpu, struct workqueue_struct *wq); extern unsigned int work_busy(struct work_struct *work); +/** + * queue_work - queue work on a workqueue + * @wq: workqueue to use + * @work: work to queue + * + * Returns %false if @work was already on a queue, %true otherwise. + * + * We queue the work to the CPU on which it was submitted, but if the CPU dies + * it can be processed by another CPU. + */ +static inline bool queue_work(struct workqueue_struct *wq, + struct work_struct *work) +{ + return queue_work_on(WORK_CPU_UNBOUND, wq, work); +} + +/** + * queue_delayed_work - queue work on a workqueue after delay + * @wq: workqueue to use + * @dwork: delayable work to queue + * @delay: number of jiffies to wait before queueing + * + * Equivalent to queue_delayed_work_on() but tries to use the local CPU. + */ +static inline bool queue_delayed_work(struct workqueue_struct *wq, + struct delayed_work *dwork, + unsigned long delay) +{ + return queue_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay); +} + +/** + * mod_delayed_work - modify delay of or queue a delayed work + * @wq: workqueue to use + * @dwork: work to queue + * @delay: number of jiffies to wait before queueing + * + * mod_delayed_work_on() on local CPU. + */ +static inline bool mod_delayed_work(struct workqueue_struct *wq, + struct delayed_work *dwork, + unsigned long delay) +{ + return mod_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay); +} + +/** + * schedule_work_on - put work task on a specific cpu + * @cpu: cpu to put the work task on + * @work: job to be done + * + * This puts a job on a specific cpu + */ +static inline bool schedule_work_on(int cpu, struct work_struct *work) +{ + return queue_work_on(cpu, system_wq, work); +} + +/** + * schedule_work - put work task in global workqueue + * @work: job to be done + * + * Returns %false if @work was already on the kernel-global workqueue and + * %true otherwise. + * + * This puts a job in the kernel-global workqueue if it was not already + * queued and leaves it in the same position on the kernel-global + * workqueue otherwise. + */ +static inline bool schedule_work(struct work_struct *work) +{ + return queue_work(system_wq, work); +} + +/** + * schedule_delayed_work_on - queue work in global workqueue on CPU after delay + * @cpu: cpu to use + * @dwork: job to be done + * @delay: number of jiffies to wait + * + * After waiting for a given time this puts a job in the kernel-global + * workqueue on the specified CPU. + */ +static inline bool schedule_delayed_work_on(int cpu, struct delayed_work *dwork, + unsigned long delay) +{ + return queue_delayed_work_on(cpu, system_wq, dwork, delay); +} + +/** + * schedule_delayed_work - put work task in global workqueue after delay + * @dwork: job to be done + * @delay: number of jiffies to wait or 0 for immediate execution + * + * After waiting for a given time this puts a job in the kernel-global + * workqueue. + */ +static inline bool schedule_delayed_work(struct delayed_work *dwork, + unsigned long delay) +{ + return queue_delayed_work(system_wq, dwork, delay); +} + +/** + * keventd_up - is workqueue initialized yet? + */ +static inline bool keventd_up(void) +{ + return system_wq != NULL; +} + /* * Like above, but uses del_timer() instead of del_timer_sync(). This means, * if it returns 0 the timer function may be running and the queueing is in diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 147fc5a784f..f37421fb4f3 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1340,22 +1340,6 @@ bool queue_work_on(int cpu, struct workqueue_struct *wq, } EXPORT_SYMBOL_GPL(queue_work_on); -/** - * queue_work - queue work on a workqueue - * @wq: workqueue to use - * @work: work to queue - * - * Returns %false if @work was already on a queue, %true otherwise. - * - * We queue the work to the CPU on which it was submitted, but if the CPU dies - * it can be processed by another CPU. - */ -bool queue_work(struct workqueue_struct *wq, struct work_struct *work) -{ - return queue_work_on(WORK_CPU_UNBOUND, wq, work); -} -EXPORT_SYMBOL_GPL(queue_work); - void delayed_work_timer_fn(unsigned long __data) { struct delayed_work *dwork = (struct delayed_work *)__data; @@ -1430,21 +1414,6 @@ bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, } EXPORT_SYMBOL_GPL(queue_delayed_work_on); -/** - * queue_delayed_work - queue work on a workqueue after delay - * @wq: workqueue to use - * @dwork: delayable work to queue - * @delay: number of jiffies to wait before queueing - * - * Equivalent to queue_delayed_work_on() but tries to use the local CPU. - */ -bool queue_delayed_work(struct workqueue_struct *wq, - struct delayed_work *dwork, unsigned long delay) -{ - return queue_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay); -} -EXPORT_SYMBOL_GPL(queue_delayed_work); - /** * mod_delayed_work_on - modify delay of or queue a delayed work on specific CPU * @cpu: CPU number to execute work on @@ -1483,21 +1452,6 @@ bool mod_delayed_work_on(int cpu, struct workqueue_struct *wq, } EXPORT_SYMBOL_GPL(mod_delayed_work_on); -/** - * mod_delayed_work - modify delay of or queue a delayed work - * @wq: workqueue to use - * @dwork: work to queue - * @delay: number of jiffies to wait before queueing - * - * mod_delayed_work_on() on local CPU. - */ -bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, - unsigned long delay) -{ - return mod_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay); -} -EXPORT_SYMBOL_GPL(mod_delayed_work); - /** * worker_enter_idle - enter idle state * @worker: worker which is entering idle state @@ -3001,66 +2955,6 @@ bool cancel_delayed_work_sync(struct delayed_work *dwork) } EXPORT_SYMBOL(cancel_delayed_work_sync); -/** - * schedule_work_on - put work task on a specific cpu - * @cpu: cpu to put the work task on - * @work: job to be done - * - * This puts a job on a specific cpu - */ -bool schedule_work_on(int cpu, struct work_struct *work) -{ - return queue_work_on(cpu, system_wq, work); -} -EXPORT_SYMBOL(schedule_work_on); - -/** - * schedule_work - put work task in global workqueue - * @work: job to be done - * - * Returns %false if @work was already on the kernel-global workqueue and - * %true otherwise. - * - * This puts a job in the kernel-global workqueue if it was not already - * queued and leaves it in the same position on the kernel-global - * workqueue otherwise. - */ -bool schedule_work(struct work_struct *work) -{ - return queue_work(system_wq, work); -} -EXPORT_SYMBOL(schedule_work); - -/** - * schedule_delayed_work_on - queue work in global workqueue on CPU after delay - * @cpu: cpu to use - * @dwork: job to be done - * @delay: number of jiffies to wait - * - * After waiting for a given time this puts a job in the kernel-global - * workqueue on the specified CPU. - */ -bool schedule_delayed_work_on(int cpu, struct delayed_work *dwork, - unsigned long delay) -{ - return queue_delayed_work_on(cpu, system_wq, dwork, delay); -} -EXPORT_SYMBOL(schedule_delayed_work_on); - -/** - * schedule_delayed_work - put work task in global workqueue after delay - * @dwork: job to be done - * @delay: number of jiffies to wait or 0 for immediate execution - * - * After waiting for a given time this puts a job in the kernel-global - * workqueue. - */ -bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay) -{ - return queue_delayed_work(system_wq, dwork, delay); -} -EXPORT_SYMBOL(schedule_delayed_work); - /** * schedule_on_each_cpu - execute a function synchronously on each online CPU * @func: the function to call @@ -3154,11 +3048,6 @@ int execute_in_process_context(work_func_t fn, struct execute_work *ew) } EXPORT_SYMBOL_GPL(execute_in_process_context); -int keventd_up(void) -{ - return system_wq != NULL; -} - #ifdef CONFIG_SYSFS /* * Workqueues with WQ_SYSFS flag set is visible to userland via -- cgit v1.2.3-70-g09d2 From 61782e4f5eb593958582524aad9b14dc98b1b56c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 14 Mar 2013 00:18:41 -0700 Subject: ASoC: add .name for snd_soc_component_driver This patch adds .name member on snd_soc_component_driver. But this patch doesn't care about whether cmpnt_drv was NULL, and/or its name was NULL in snd_soc_register_component() at this point. Because, it is easy to switch over to snd_soc_register_component() from snd_soc_register_dais() if it doesn't care cmpnt_drv was NULL. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 8c46d0a7e2c..44c9cbdc9fa 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -848,6 +848,7 @@ struct snd_soc_platform { }; struct snd_soc_component_driver { + const char *name; }; struct snd_soc_component { -- cgit v1.2.3-70-g09d2 From ae63b31e4d0e2ec09c569306ea46f664508ef717 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 3 May 2012 23:09:03 -0400 Subject: tracing: Separate out trace events from global variables The trace events for ftrace are all defined via global variables. The arrays of events and event systems are linked to a global list. This prevents multiple users of the event system (what to enable and what not to). By adding descriptors to represent the event/file relation, as well as to which trace_array descriptor they are associated with, allows for more than one set of events to be defined. Once the trace events files have a link between the trace event and the trace_array they are associated with, we can create multiple trace_arrays that can record separate events in separate buffers. Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 51 ++- include/trace/ftrace.h | 3 +- kernel/trace/trace.c | 8 + kernel/trace/trace.h | 39 +- kernel/trace/trace_events.c | 776 +++++++++++++++++++++++++------------ kernel/trace/trace_events_filter.c | 5 +- 6 files changed, 622 insertions(+), 260 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 13a54d0bdfa..c7191d482f9 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -182,18 +182,20 @@ extern int ftrace_event_reg(struct ftrace_event_call *event, enum trace_reg type, void *data); enum { - TRACE_EVENT_FL_ENABLED_BIT, TRACE_EVENT_FL_FILTERED_BIT, - TRACE_EVENT_FL_RECORDED_CMD_BIT, TRACE_EVENT_FL_CAP_ANY_BIT, TRACE_EVENT_FL_NO_SET_FILTER_BIT, TRACE_EVENT_FL_IGNORE_ENABLE_BIT, }; +/* + * Event flags: + * FILTERED - The event has a filter attached + * CAP_ANY - Any user can enable for perf + * NO_SET_FILTER - Set when filter has error and is to be ignored + */ enum { - TRACE_EVENT_FL_ENABLED = (1 << TRACE_EVENT_FL_ENABLED_BIT), TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT), - TRACE_EVENT_FL_RECORDED_CMD = (1 << TRACE_EVENT_FL_RECORDED_CMD_BIT), TRACE_EVENT_FL_CAP_ANY = (1 << TRACE_EVENT_FL_CAP_ANY_BIT), TRACE_EVENT_FL_NO_SET_FILTER = (1 << TRACE_EVENT_FL_NO_SET_FILTER_BIT), TRACE_EVENT_FL_IGNORE_ENABLE = (1 << TRACE_EVENT_FL_IGNORE_ENABLE_BIT), @@ -203,12 +205,44 @@ struct ftrace_event_call { struct list_head list; struct ftrace_event_class *class; char *name; - struct dentry *dir; struct trace_event event; const char *print_fmt; struct event_filter *filter; + struct list_head *files; void *mod; void *data; + int flags; /* static flags of different events */ + +#ifdef CONFIG_PERF_EVENTS + int perf_refcount; + struct hlist_head __percpu *perf_events; +#endif +}; + +struct trace_array; +struct ftrace_subsystem_dir; + +enum { + FTRACE_EVENT_FL_ENABLED_BIT, + FTRACE_EVENT_FL_RECORDED_CMD_BIT, +}; + +/* + * Ftrace event file flags: + * ENABELD - The event is enabled + * RECORDED_CMD - The comms should be recorded at sched_switch + */ +enum { + FTRACE_EVENT_FL_ENABLED = (1 << FTRACE_EVENT_FL_ENABLED_BIT), + FTRACE_EVENT_FL_RECORDED_CMD = (1 << FTRACE_EVENT_FL_RECORDED_CMD_BIT), +}; + +struct ftrace_event_file { + struct list_head list; + struct ftrace_event_call *event_call; + struct dentry *dir; + struct trace_array *tr; + struct ftrace_subsystem_dir *system; /* * 32 bit flags: @@ -223,17 +257,12 @@ struct ftrace_event_call { * * Note: Reads of flags do not hold the event_mutex since * they occur in critical sections. But the way flags - * is currently used, these changes do no affect the code + * is currently used, these changes do not affect the code * except that when a change is made, it may have a slight * delay in propagating the changes to other CPUs due to * caching and such. */ unsigned int flags; - -#ifdef CONFIG_PERF_EVENTS - int perf_refcount; - struct hlist_head __percpu *perf_events; -#endif }; #define __TRACE_EVENT_FLAGS(name, value) \ diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 40dc5e8fe34..191d9661e27 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -518,7 +518,8 @@ static inline notrace int ftrace_get_offsets_##call( \ static notrace void \ ftrace_raw_event_##call(void *__data, proto) \ { \ - struct ftrace_event_call *event_call = __data; \ + struct ftrace_event_file *ftrace_file = __data; \ + struct ftrace_event_call *event_call = ftrace_file->event_call; \ struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\ struct ring_buffer_event *event; \ struct ftrace_raw_##call *entry; \ diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 4f1dade5698..932931897b8 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -189,6 +189,8 @@ unsigned long long ns2usecs(cycle_t nsec) */ static struct trace_array global_trace; +LIST_HEAD(ftrace_trace_arrays); + static DEFINE_PER_CPU(struct trace_array_cpu, global_trace_cpu); int filter_current_check_discard(struct ring_buffer *buffer, @@ -5359,6 +5361,12 @@ __init static int tracer_alloc_buffers(void) register_die_notifier(&trace_die_notifier); + global_trace.flags = TRACE_ARRAY_FL_GLOBAL; + + INIT_LIST_HEAD(&global_trace.systems); + INIT_LIST_HEAD(&global_trace.events); + list_add(&global_trace.list, &ftrace_trace_arrays); + while (trace_boot_options) { char *option; diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 2081971367e..037f7eb03d6 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -158,13 +158,39 @@ struct trace_array_cpu { */ struct trace_array { struct ring_buffer *buffer; + struct list_head list; int cpu; int buffer_disabled; + unsigned int flags; cycle_t time_start; + struct dentry *dir; + struct dentry *event_dir; + struct list_head systems; + struct list_head events; struct task_struct *waiter; struct trace_array_cpu *data[NR_CPUS]; }; +enum { + TRACE_ARRAY_FL_GLOBAL = (1 << 0) +}; + +extern struct list_head ftrace_trace_arrays; + +/* + * The global tracer (top) should be the first trace array added, + * but we check the flag anyway. + */ +static inline struct trace_array *top_trace_array(void) +{ + struct trace_array *tr; + + tr = list_entry(ftrace_trace_arrays.prev, + typeof(*tr), list); + WARN_ON(!(tr->flags & TRACE_ARRAY_FL_GLOBAL)); + return tr; +} + #define FTRACE_CMP_TYPE(var, type) \ __builtin_types_compatible_p(typeof(var), type *) @@ -851,12 +877,19 @@ struct event_filter { struct event_subsystem { struct list_head list; const char *name; - struct dentry *entry; struct event_filter *filter; - int nr_events; int ref_count; }; +struct ftrace_subsystem_dir { + struct list_head list; + struct event_subsystem *subsystem; + struct trace_array *tr; + struct dentry *entry; + int ref_count; + int nr_events; +}; + #define FILTER_PRED_INVALID ((unsigned short)-1) #define FILTER_PRED_IS_RIGHT (1 << 15) #define FILTER_PRED_FOLD (1 << 15) @@ -914,7 +947,7 @@ extern void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s); extern int apply_event_filter(struct ftrace_event_call *call, char *filter_string); -extern int apply_subsystem_event_filter(struct event_subsystem *system, +extern int apply_subsystem_event_filter(struct ftrace_subsystem_dir *dir, char *filter_string); extern void print_subsystem_event_filter(struct event_subsystem *system, struct trace_seq *s); diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 57e9b284250..439955239ba 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -36,6 +36,19 @@ EXPORT_SYMBOL_GPL(event_storage); LIST_HEAD(ftrace_events); LIST_HEAD(ftrace_common_fields); +/* Double loops, do not use break, only goto's work */ +#define do_for_each_event_file(tr, file) \ + list_for_each_entry(tr, &ftrace_trace_arrays, list) { \ + list_for_each_entry(file, &tr->events, list) + +#define do_for_each_event_file_safe(tr, file) \ + list_for_each_entry(tr, &ftrace_trace_arrays, list) { \ + struct ftrace_event_file *___n; \ + list_for_each_entry_safe(file, ___n, &tr->events, list) + +#define while_for_each_event_file() \ + } + struct list_head * trace_get_fields(struct ftrace_event_call *event_call) { @@ -149,15 +162,17 @@ EXPORT_SYMBOL_GPL(trace_event_raw_init); int ftrace_event_reg(struct ftrace_event_call *call, enum trace_reg type, void *data) { + struct ftrace_event_file *file = data; + switch (type) { case TRACE_REG_REGISTER: return tracepoint_probe_register(call->name, call->class->probe, - call); + file); case TRACE_REG_UNREGISTER: tracepoint_probe_unregister(call->name, call->class->probe, - call); + file); return 0; #ifdef CONFIG_PERF_EVENTS @@ -183,54 +198,57 @@ EXPORT_SYMBOL_GPL(ftrace_event_reg); void trace_event_enable_cmd_record(bool enable) { - struct ftrace_event_call *call; + struct ftrace_event_file *file; + struct trace_array *tr; mutex_lock(&event_mutex); - list_for_each_entry(call, &ftrace_events, list) { - if (!(call->flags & TRACE_EVENT_FL_ENABLED)) + do_for_each_event_file(tr, file) { + + if (!(file->flags & FTRACE_EVENT_FL_ENABLED)) continue; if (enable) { tracing_start_cmdline_record(); - call->flags |= TRACE_EVENT_FL_RECORDED_CMD; + file->flags |= FTRACE_EVENT_FL_RECORDED_CMD; } else { tracing_stop_cmdline_record(); - call->flags &= ~TRACE_EVENT_FL_RECORDED_CMD; + file->flags &= ~FTRACE_EVENT_FL_RECORDED_CMD; } - } + } while_for_each_event_file(); mutex_unlock(&event_mutex); } -static int ftrace_event_enable_disable(struct ftrace_event_call *call, - int enable) +static int ftrace_event_enable_disable(struct ftrace_event_file *file, + int enable) { + struct ftrace_event_call *call = file->event_call; int ret = 0; switch (enable) { case 0: - if (call->flags & TRACE_EVENT_FL_ENABLED) { - call->flags &= ~TRACE_EVENT_FL_ENABLED; - if (call->flags & TRACE_EVENT_FL_RECORDED_CMD) { + if (file->flags & FTRACE_EVENT_FL_ENABLED) { + file->flags &= ~FTRACE_EVENT_FL_ENABLED; + if (file->flags & FTRACE_EVENT_FL_RECORDED_CMD) { tracing_stop_cmdline_record(); - call->flags &= ~TRACE_EVENT_FL_RECORDED_CMD; + file->flags &= ~FTRACE_EVENT_FL_RECORDED_CMD; } - call->class->reg(call, TRACE_REG_UNREGISTER, NULL); + call->class->reg(call, TRACE_REG_UNREGISTER, file); } break; case 1: - if (!(call->flags & TRACE_EVENT_FL_ENABLED)) { + if (!(file->flags & FTRACE_EVENT_FL_ENABLED)) { if (trace_flags & TRACE_ITER_RECORD_CMD) { tracing_start_cmdline_record(); - call->flags |= TRACE_EVENT_FL_RECORDED_CMD; + file->flags |= FTRACE_EVENT_FL_RECORDED_CMD; } - ret = call->class->reg(call, TRACE_REG_REGISTER, NULL); + ret = call->class->reg(call, TRACE_REG_REGISTER, file); if (ret) { tracing_stop_cmdline_record(); pr_info("event trace: Could not enable event " "%s\n", call->name); break; } - call->flags |= TRACE_EVENT_FL_ENABLED; + file->flags |= FTRACE_EVENT_FL_ENABLED; } break; } @@ -238,13 +256,13 @@ static int ftrace_event_enable_disable(struct ftrace_event_call *call, return ret; } -static void ftrace_clear_events(void) +static void ftrace_clear_events(struct trace_array *tr) { - struct ftrace_event_call *call; + struct ftrace_event_file *file; mutex_lock(&event_mutex); - list_for_each_entry(call, &ftrace_events, list) { - ftrace_event_enable_disable(call, 0); + list_for_each_entry(file, &tr->events, list) { + ftrace_event_enable_disable(file, 0); } mutex_unlock(&event_mutex); } @@ -257,6 +275,8 @@ static void __put_system(struct event_subsystem *system) if (--system->ref_count) return; + list_del(&system->list); + if (filter) { kfree(filter->filter_string); kfree(filter); @@ -271,24 +291,45 @@ static void __get_system(struct event_subsystem *system) system->ref_count++; } -static void put_system(struct event_subsystem *system) +static void __get_system_dir(struct ftrace_subsystem_dir *dir) +{ + WARN_ON_ONCE(dir->ref_count == 0); + dir->ref_count++; + __get_system(dir->subsystem); +} + +static void __put_system_dir(struct ftrace_subsystem_dir *dir) +{ + WARN_ON_ONCE(dir->ref_count == 0); + /* If the subsystem is about to be freed, the dir must be too */ + WARN_ON_ONCE(dir->subsystem->ref_count == 1 && dir->ref_count != 1); + + __put_system(dir->subsystem); + if (!--dir->ref_count) + kfree(dir); +} + +static void put_system(struct ftrace_subsystem_dir *dir) { mutex_lock(&event_mutex); - __put_system(system); + __put_system_dir(dir); mutex_unlock(&event_mutex); } /* * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events. */ -static int __ftrace_set_clr_event(const char *match, const char *sub, - const char *event, int set) +static int __ftrace_set_clr_event(struct trace_array *tr, const char *match, + const char *sub, const char *event, int set) { + struct ftrace_event_file *file; struct ftrace_event_call *call; int ret = -EINVAL; mutex_lock(&event_mutex); - list_for_each_entry(call, &ftrace_events, list) { + list_for_each_entry(file, &tr->events, list) { + + call = file->event_call; if (!call->name || !call->class || !call->class->reg) continue; @@ -307,7 +348,7 @@ static int __ftrace_set_clr_event(const char *match, const char *sub, if (event && strcmp(event, call->name) != 0) continue; - ftrace_event_enable_disable(call, set); + ftrace_event_enable_disable(file, set); ret = 0; } @@ -316,7 +357,7 @@ static int __ftrace_set_clr_event(const char *match, const char *sub, return ret; } -static int ftrace_set_clr_event(char *buf, int set) +static int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set) { char *event = NULL, *sub = NULL, *match; @@ -344,7 +385,7 @@ static int ftrace_set_clr_event(char *buf, int set) event = NULL; } - return __ftrace_set_clr_event(match, sub, event, set); + return __ftrace_set_clr_event(tr, match, sub, event, set); } /** @@ -361,7 +402,9 @@ static int ftrace_set_clr_event(char *buf, int set) */ int trace_set_clr_event(const char *system, const char *event, int set) { - return __ftrace_set_clr_event(NULL, system, event, set); + struct trace_array *tr = top_trace_array(); + + return __ftrace_set_clr_event(tr, NULL, system, event, set); } EXPORT_SYMBOL_GPL(trace_set_clr_event); @@ -373,6 +416,8 @@ ftrace_event_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct trace_parser parser; + struct seq_file *m = file->private_data; + struct trace_array *tr = m->private; ssize_t read, ret; if (!cnt) @@ -395,7 +440,7 @@ ftrace_event_write(struct file *file, const char __user *ubuf, parser.buffer[parser.idx] = 0; - ret = ftrace_set_clr_event(parser.buffer + !set, set); + ret = ftrace_set_clr_event(tr, parser.buffer + !set, set); if (ret) goto out_put; } @@ -411,17 +456,20 @@ ftrace_event_write(struct file *file, const char __user *ubuf, static void * t_next(struct seq_file *m, void *v, loff_t *pos) { - struct ftrace_event_call *call = v; + struct ftrace_event_file *file = v; + struct ftrace_event_call *call; + struct trace_array *tr = m->private; (*pos)++; - list_for_each_entry_continue(call, &ftrace_events, list) { + list_for_each_entry_continue(file, &tr->events, list) { + call = file->event_call; /* * The ftrace subsystem is for showing formats only. * They can not be enabled or disabled via the event files. */ if (call->class && call->class->reg) - return call; + return file; } return NULL; @@ -429,30 +477,32 @@ t_next(struct seq_file *m, void *v, loff_t *pos) static void *t_start(struct seq_file *m, loff_t *pos) { - struct ftrace_event_call *call; + struct ftrace_event_file *file; + struct trace_array *tr = m->private; loff_t l; mutex_lock(&event_mutex); - call = list_entry(&ftrace_events, struct ftrace_event_call, list); + file = list_entry(&tr->events, struct ftrace_event_file, list); for (l = 0; l <= *pos; ) { - call = t_next(m, call, &l); - if (!call) + file = t_next(m, file, &l); + if (!file) break; } - return call; + return file; } static void * s_next(struct seq_file *m, void *v, loff_t *pos) { - struct ftrace_event_call *call = v; + struct ftrace_event_file *file = v; + struct trace_array *tr = m->private; (*pos)++; - list_for_each_entry_continue(call, &ftrace_events, list) { - if (call->flags & TRACE_EVENT_FL_ENABLED) - return call; + list_for_each_entry_continue(file, &tr->events, list) { + if (file->flags & FTRACE_EVENT_FL_ENABLED) + return file; } return NULL; @@ -460,23 +510,25 @@ s_next(struct seq_file *m, void *v, loff_t *pos) static void *s_start(struct seq_file *m, loff_t *pos) { - struct ftrace_event_call *call; + struct ftrace_event_file *file; + struct trace_array *tr = m->private; loff_t l; mutex_lock(&event_mutex); - call = list_entry(&ftrace_events, struct ftrace_event_call, list); + file = list_entry(&tr->events, struct ftrace_event_file, list); for (l = 0; l <= *pos; ) { - call = s_next(m, call, &l); - if (!call) + file = s_next(m, file, &l); + if (!file) break; } - return call; + return file; } static int t_show(struct seq_file *m, void *v) { - struct ftrace_event_call *call = v; + struct ftrace_event_file *file = v; + struct ftrace_event_call *call = file->event_call; if (strcmp(call->class->system, TRACE_SYSTEM) != 0) seq_printf(m, "%s:", call->class->system); @@ -494,10 +546,10 @@ static ssize_t event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - struct ftrace_event_call *call = filp->private_data; + struct ftrace_event_file *file = filp->private_data; char *buf; - if (call->flags & TRACE_EVENT_FL_ENABLED) + if (file->flags & FTRACE_EVENT_FL_ENABLED) buf = "1\n"; else buf = "0\n"; @@ -509,10 +561,13 @@ static ssize_t event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - struct ftrace_event_call *call = filp->private_data; + struct ftrace_event_file *file = filp->private_data; unsigned long val; int ret; + if (!file) + return -EINVAL; + ret = kstrtoul_from_user(ubuf, cnt, 10, &val); if (ret) return ret; @@ -525,7 +580,7 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, case 0: case 1: mutex_lock(&event_mutex); - ret = ftrace_event_enable_disable(call, val); + ret = ftrace_event_enable_disable(file, val); mutex_unlock(&event_mutex); break; @@ -543,14 +598,18 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { const char set_to_char[4] = { '?', '0', '1', 'X' }; - struct event_subsystem *system = filp->private_data; + struct ftrace_subsystem_dir *dir = filp->private_data; + struct event_subsystem *system = dir->subsystem; struct ftrace_event_call *call; + struct ftrace_event_file *file; + struct trace_array *tr = dir->tr; char buf[2]; int set = 0; int ret; mutex_lock(&event_mutex); - list_for_each_entry(call, &ftrace_events, list) { + list_for_each_entry(file, &tr->events, list) { + call = file->event_call; if (!call->name || !call->class || !call->class->reg) continue; @@ -562,7 +621,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt, * or if all events or cleared, or if we have * a mixture. */ - set |= (1 << !!(call->flags & TRACE_EVENT_FL_ENABLED)); + set |= (1 << !!(file->flags & FTRACE_EVENT_FL_ENABLED)); /* * If we have a mixture, no need to look further. @@ -584,7 +643,8 @@ static ssize_t system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - struct event_subsystem *system = filp->private_data; + struct ftrace_subsystem_dir *dir = filp->private_data; + struct event_subsystem *system = dir->subsystem; const char *name = NULL; unsigned long val; ssize_t ret; @@ -607,7 +667,7 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, if (system) name = system->name; - ret = __ftrace_set_clr_event(NULL, name, NULL, val); + ret = __ftrace_set_clr_event(dir->tr, NULL, name, NULL, val); if (ret) goto out; @@ -845,43 +905,75 @@ static LIST_HEAD(event_subsystems); static int subsystem_open(struct inode *inode, struct file *filp) { struct event_subsystem *system = NULL; + struct ftrace_subsystem_dir *dir = NULL; /* Initialize for gcc */ + struct trace_array *tr; int ret; - if (!inode->i_private) - goto skip_search; - /* Make sure the system still exists */ mutex_lock(&event_mutex); - list_for_each_entry(system, &event_subsystems, list) { - if (system == inode->i_private) { - /* Don't open systems with no events */ - if (!system->nr_events) { - system = NULL; - break; + list_for_each_entry(tr, &ftrace_trace_arrays, list) { + list_for_each_entry(dir, &tr->systems, list) { + if (dir == inode->i_private) { + /* Don't open systems with no events */ + if (dir->nr_events) { + __get_system_dir(dir); + system = dir->subsystem; + } + goto exit_loop; } - __get_system(system); - break; } } + exit_loop: mutex_unlock(&event_mutex); - if (system != inode->i_private) + if (!system) return -ENODEV; - skip_search: + /* Some versions of gcc think dir can be uninitialized here */ + WARN_ON(!dir); + ret = tracing_open_generic(inode, filp); - if (ret < 0 && system) - put_system(system); + if (ret < 0) + put_system(dir); + + return ret; +} + +static int system_tr_open(struct inode *inode, struct file *filp) +{ + struct ftrace_subsystem_dir *dir; + struct trace_array *tr = inode->i_private; + int ret; + + /* Make a temporary dir that has no system but points to tr */ + dir = kzalloc(sizeof(*dir), GFP_KERNEL); + if (!dir) + return -ENOMEM; + + dir->tr = tr; + + ret = tracing_open_generic(inode, filp); + if (ret < 0) + kfree(dir); + + filp->private_data = dir; return ret; } static int subsystem_release(struct inode *inode, struct file *file) { - struct event_subsystem *system = inode->i_private; + struct ftrace_subsystem_dir *dir = file->private_data; - if (system) - put_system(system); + /* + * If dir->subsystem is NULL, then this is a temporary + * descriptor that was made for a trace_array to enable + * all subsystems. + */ + if (dir->subsystem) + put_system(dir); + else + kfree(dir); return 0; } @@ -890,7 +982,8 @@ static ssize_t subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - struct event_subsystem *system = filp->private_data; + struct ftrace_subsystem_dir *dir = filp->private_data; + struct event_subsystem *system = dir->subsystem; struct trace_seq *s; int r; @@ -915,7 +1008,7 @@ static ssize_t subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - struct event_subsystem *system = filp->private_data; + struct ftrace_subsystem_dir *dir = filp->private_data; char *buf; int err; @@ -932,7 +1025,7 @@ subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, } buf[cnt] = '\0'; - err = apply_subsystem_event_filter(system, buf); + err = apply_subsystem_event_filter(dir, buf); free_page((unsigned long) buf); if (err < 0) return err; @@ -1041,30 +1134,35 @@ static const struct file_operations ftrace_system_enable_fops = { .release = subsystem_release, }; +static const struct file_operations ftrace_tr_enable_fops = { + .open = system_tr_open, + .read = system_enable_read, + .write = system_enable_write, + .llseek = default_llseek, + .release = subsystem_release, +}; + static const struct file_operations ftrace_show_header_fops = { .open = tracing_open_generic, .read = show_header, .llseek = default_llseek, }; -static struct dentry *event_trace_events_dir(void) +static int +ftrace_event_open(struct inode *inode, struct file *file, + const struct seq_operations *seq_ops) { - static struct dentry *d_tracer; - static struct dentry *d_events; - - if (d_events) - return d_events; - - d_tracer = tracing_init_dentry(); - if (!d_tracer) - return NULL; + struct seq_file *m; + int ret; - d_events = debugfs_create_dir("events", d_tracer); - if (!d_events) - pr_warning("Could not create debugfs " - "'events' directory\n"); + ret = seq_open(file, seq_ops); + if (ret < 0) + return ret; + m = file->private_data; + /* copy tr over to seq ops */ + m->private = inode->i_private; - return d_events; + return ret; } static int @@ -1072,117 +1170,169 @@ ftrace_event_avail_open(struct inode *inode, struct file *file) { const struct seq_operations *seq_ops = &show_event_seq_ops; - return seq_open(file, seq_ops); + return ftrace_event_open(inode, file, seq_ops); } static int ftrace_event_set_open(struct inode *inode, struct file *file) { const struct seq_operations *seq_ops = &show_set_event_seq_ops; + struct trace_array *tr = inode->i_private; if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) - ftrace_clear_events(); + ftrace_clear_events(tr); - return seq_open(file, seq_ops); + return ftrace_event_open(inode, file, seq_ops); +} + +static struct event_subsystem * +create_new_subsystem(const char *name) +{ + struct event_subsystem *system; + + /* need to create new entry */ + system = kmalloc(sizeof(*system), GFP_KERNEL); + if (!system) + return NULL; + + system->ref_count = 1; + system->name = kstrdup(name, GFP_KERNEL); + + if (!system->name) + goto out_free; + + system->filter = NULL; + + system->filter = kzalloc(sizeof(struct event_filter), GFP_KERNEL); + if (!system->filter) + goto out_free; + + list_add(&system->list, &event_subsystems); + + return system; + + out_free: + kfree(system->name); + kfree(system); + return NULL; } static struct dentry * -event_subsystem_dir(const char *name, struct dentry *d_events) +event_subsystem_dir(struct trace_array *tr, const char *name, + struct ftrace_event_file *file, struct dentry *parent) { + struct ftrace_subsystem_dir *dir; struct event_subsystem *system; struct dentry *entry; /* First see if we did not already create this dir */ - list_for_each_entry(system, &event_subsystems, list) { + list_for_each_entry(dir, &tr->systems, list) { + system = dir->subsystem; if (strcmp(system->name, name) == 0) { - system->nr_events++; - return system->entry; + dir->nr_events++; + file->system = dir; + return dir->entry; } } - /* need to create new entry */ - system = kmalloc(sizeof(*system), GFP_KERNEL); - if (!system) { - pr_warning("No memory to create event subsystem %s\n", - name); - return d_events; + /* Now see if the system itself exists. */ + list_for_each_entry(system, &event_subsystems, list) { + if (strcmp(system->name, name) == 0) + break; } + /* Reset system variable when not found */ + if (&system->list == &event_subsystems) + system = NULL; - system->entry = debugfs_create_dir(name, d_events); - if (!system->entry) { - pr_warning("Could not create event subsystem %s\n", - name); - kfree(system); - return d_events; - } + dir = kmalloc(sizeof(*dir), GFP_KERNEL); + if (!dir) + goto out_fail; - system->nr_events = 1; - system->ref_count = 1; - system->name = kstrdup(name, GFP_KERNEL); - if (!system->name) { - debugfs_remove(system->entry); - kfree(system); - return d_events; + if (!system) { + system = create_new_subsystem(name); + if (!system) + goto out_free; + } else + __get_system(system); + + dir->entry = debugfs_create_dir(name, parent); + if (!dir->entry) { + pr_warning("Failed to create system directory %s\n", name); + __put_system(system); + goto out_free; } - list_add(&system->list, &event_subsystems); - - system->filter = NULL; - - system->filter = kzalloc(sizeof(struct event_filter), GFP_KERNEL); - if (!system->filter) { - pr_warning("Could not allocate filter for subsystem " - "'%s'\n", name); - return system->entry; - } + dir->tr = tr; + dir->ref_count = 1; + dir->nr_events = 1; + dir->subsystem = system; + file->system = dir; - entry = debugfs_create_file("filter", 0644, system->entry, system, + entry = debugfs_create_file("filter", 0644, dir->entry, dir, &ftrace_subsystem_filter_fops); if (!entry) { kfree(system->filter); system->filter = NULL; - pr_warning("Could not create debugfs " - "'%s/filter' entry\n", name); + pr_warning("Could not create debugfs '%s/filter' entry\n", name); } - trace_create_file("enable", 0644, system->entry, system, + trace_create_file("enable", 0644, dir->entry, dir, &ftrace_system_enable_fops); - return system->entry; + list_add(&dir->list, &tr->systems); + + return dir->entry; + + out_free: + kfree(dir); + out_fail: + /* Only print this message if failed on memory allocation */ + if (!dir || !system) + pr_warning("No memory to create event subsystem %s\n", + name); + return NULL; } static int -event_create_dir(struct ftrace_event_call *call, struct dentry *d_events, +event_create_dir(struct dentry *parent, + struct ftrace_event_file *file, const struct file_operations *id, const struct file_operations *enable, const struct file_operations *filter, const struct file_operations *format) { + struct ftrace_event_call *call = file->event_call; + struct trace_array *tr = file->tr; struct list_head *head; + struct dentry *d_events; int ret; /* * If the trace point header did not define TRACE_SYSTEM * then the system would be called "TRACE_SYSTEM". */ - if (strcmp(call->class->system, TRACE_SYSTEM) != 0) - d_events = event_subsystem_dir(call->class->system, d_events); - - call->dir = debugfs_create_dir(call->name, d_events); - if (!call->dir) { - pr_warning("Could not create debugfs " - "'%s' directory\n", call->name); + if (strcmp(call->class->system, TRACE_SYSTEM) != 0) { + d_events = event_subsystem_dir(tr, call->class->system, file, parent); + if (!d_events) + return -ENOMEM; + } else + d_events = parent; + + file->dir = debugfs_create_dir(call->name, d_events); + if (!file->dir) { + pr_warning("Could not create debugfs '%s' directory\n", + call->name); return -1; } if (call->class->reg && !(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)) - trace_create_file("enable", 0644, call->dir, call, + trace_create_file("enable", 0644, file->dir, file, enable); #ifdef CONFIG_PERF_EVENTS if (call->event.type && call->class->reg) - trace_create_file("id", 0444, call->dir, call, + trace_create_file("id", 0444, file->dir, call, id); #endif @@ -1196,23 +1346,76 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events, if (ret < 0) { pr_warning("Could not initialize trace point" " events/%s\n", call->name); - return ret; + return -1; } } - trace_create_file("filter", 0644, call->dir, call, + trace_create_file("filter", 0644, file->dir, call, filter); - trace_create_file("format", 0444, call->dir, call, + trace_create_file("format", 0444, file->dir, call, format); return 0; } +static void remove_subsystem(struct ftrace_subsystem_dir *dir) +{ + if (!dir) + return; + + if (!--dir->nr_events) { + debugfs_remove_recursive(dir->entry); + list_del(&dir->list); + __put_system_dir(dir); + } +} + +static void remove_event_from_tracers(struct ftrace_event_call *call) +{ + struct ftrace_event_file *file; + struct trace_array *tr; + + do_for_each_event_file_safe(tr, file) { + + if (file->event_call != call) + continue; + + list_del(&file->list); + debugfs_remove_recursive(file->dir); + remove_subsystem(file->system); + kfree(file); + + /* + * The do_for_each_event_file_safe() is + * a double loop. After finding the call for this + * trace_array, we use break to jump to the next + * trace_array. + */ + break; + } while_for_each_event_file(); +} + static void event_remove(struct ftrace_event_call *call) { - ftrace_event_enable_disable(call, 0); + struct trace_array *tr; + struct ftrace_event_file *file; + + do_for_each_event_file(tr, file) { + if (file->event_call != call) + continue; + ftrace_event_enable_disable(file, 0); + /* + * The do_for_each_event_file() is + * a double loop. After finding the call for this + * trace_array, we use break to jump to the next + * trace_array. + */ + break; + } while_for_each_event_file(); + if (call->event.funcs) __unregister_ftrace_event(&call->event); + remove_event_from_tracers(call); list_del(&call->list); } @@ -1234,61 +1437,58 @@ static int event_init(struct ftrace_event_call *call) } static int -__trace_add_event_call(struct ftrace_event_call *call, struct module *mod, - const struct file_operations *id, - const struct file_operations *enable, - const struct file_operations *filter, - const struct file_operations *format) +__register_event(struct ftrace_event_call *call, struct module *mod) { - struct dentry *d_events; int ret; ret = event_init(call); if (ret < 0) return ret; - d_events = event_trace_events_dir(); - if (!d_events) - return -ENOENT; - - ret = event_create_dir(call, d_events, id, enable, filter, format); - if (!ret) - list_add(&call->list, &ftrace_events); + list_add(&call->list, &ftrace_events); call->mod = mod; - return ret; + return 0; } +/* Add an event to a trace directory */ +static int +__trace_add_new_event(struct ftrace_event_call *call, + struct trace_array *tr, + const struct file_operations *id, + const struct file_operations *enable, + const struct file_operations *filter, + const struct file_operations *format) +{ + struct ftrace_event_file *file; + + file = kzalloc(sizeof(*file), GFP_KERNEL); + if (!file) + return -ENOMEM; + + file->event_call = call; + file->tr = tr; + list_add(&file->list, &tr->events); + + return event_create_dir(tr->event_dir, file, id, enable, filter, format); +} + +struct ftrace_module_file_ops; +static void __add_event_to_tracers(struct ftrace_event_call *call, + struct ftrace_module_file_ops *file_ops); + /* Add an additional event_call dynamically */ int trace_add_event_call(struct ftrace_event_call *call) { int ret; mutex_lock(&event_mutex); - ret = __trace_add_event_call(call, NULL, &ftrace_event_id_fops, - &ftrace_enable_fops, - &ftrace_event_filter_fops, - &ftrace_event_format_fops); - mutex_unlock(&event_mutex); - return ret; -} -static void remove_subsystem_dir(const char *name) -{ - struct event_subsystem *system; + ret = __register_event(call, NULL); + if (ret >= 0) + __add_event_to_tracers(call, NULL); - if (strcmp(name, TRACE_SYSTEM) == 0) - return; - - list_for_each_entry(system, &event_subsystems, list) { - if (strcmp(system->name, name) == 0) { - if (!--system->nr_events) { - debugfs_remove_recursive(system->entry); - list_del(&system->list); - __put_system(system); - } - break; - } - } + mutex_unlock(&event_mutex); + return ret; } /* @@ -1299,8 +1499,6 @@ static void __trace_remove_event_call(struct ftrace_event_call *call) event_remove(call); trace_destroy_fields(call); destroy_preds(call); - debugfs_remove_recursive(call->dir); - remove_subsystem_dir(call->class->system); } /* Remove an event_call */ @@ -1335,6 +1533,17 @@ struct ftrace_module_file_ops { struct file_operations filter; }; +static struct ftrace_module_file_ops *find_ftrace_file_ops(struct module *mod) +{ + struct ftrace_module_file_ops *file_ops; + + list_for_each_entry(file_ops, &ftrace_module_file_list, list) { + if (file_ops->mod == mod) + return file_ops; + } + return NULL; +} + static struct ftrace_module_file_ops * trace_create_file_ops(struct module *mod) { @@ -1386,9 +1595,8 @@ static void trace_module_add_events(struct module *mod) return; for_each_event(call, start, end) { - __trace_add_event_call(*call, mod, - &file_ops->id, &file_ops->enable, - &file_ops->filter, &file_ops->format); + __register_event(*call, mod); + __add_event_to_tracers(*call, file_ops); } } @@ -1444,6 +1652,10 @@ static int trace_module_notify(struct notifier_block *self, return 0; } #else +static struct ftrace_module_file_ops *find_ftrace_file_ops(struct module *mod) +{ + return NULL; +} static int trace_module_notify(struct notifier_block *self, unsigned long val, void *data) { @@ -1451,6 +1663,72 @@ static int trace_module_notify(struct notifier_block *self, } #endif /* CONFIG_MODULES */ +/* Create a new event directory structure for a trace directory. */ +static void +__trace_add_event_dirs(struct trace_array *tr) +{ + struct ftrace_module_file_ops *file_ops = NULL; + struct ftrace_event_call *call; + int ret; + + list_for_each_entry(call, &ftrace_events, list) { + if (call->mod) { + /* + * Directories for events by modules need to + * keep module ref counts when opened (as we don't + * want the module to disappear when reading one + * of these files). The file_ops keep account of + * the module ref count. + * + * As event_calls are added in groups by module, + * when we find one file_ops, we don't need to search for + * each call in that module, as the rest should be the + * same. Only search for a new one if the last one did + * not match. + */ + if (!file_ops || call->mod != file_ops->mod) + file_ops = find_ftrace_file_ops(call->mod); + if (!file_ops) + continue; /* Warn? */ + ret = __trace_add_new_event(call, tr, + &file_ops->id, &file_ops->enable, + &file_ops->filter, &file_ops->format); + if (ret < 0) + pr_warning("Could not create directory for event %s\n", + call->name); + continue; + } + ret = __trace_add_new_event(call, tr, + &ftrace_event_id_fops, + &ftrace_enable_fops, + &ftrace_event_filter_fops, + &ftrace_event_format_fops); + if (ret < 0) + pr_warning("Could not create directory for event %s\n", + call->name); + } +} + +static void +__add_event_to_tracers(struct ftrace_event_call *call, + struct ftrace_module_file_ops *file_ops) +{ + struct trace_array *tr; + + list_for_each_entry(tr, &ftrace_trace_arrays, list) { + if (file_ops) + __trace_add_new_event(call, tr, + &file_ops->id, &file_ops->enable, + &file_ops->filter, &file_ops->format); + else + __trace_add_new_event(call, tr, + &ftrace_event_id_fops, + &ftrace_enable_fops, + &ftrace_event_filter_fops, + &ftrace_event_format_fops); + } +} + static struct notifier_block trace_module_nb = { .notifier_call = trace_module_notify, .priority = 0, @@ -1471,8 +1749,43 @@ static __init int setup_trace_event(char *str) } __setup("trace_event=", setup_trace_event); +int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr) +{ + struct dentry *d_events; + struct dentry *entry; + + entry = debugfs_create_file("set_event", 0644, parent, + tr, &ftrace_set_event_fops); + if (!entry) { + pr_warning("Could not create debugfs 'set_event' entry\n"); + return -ENOMEM; + } + + d_events = debugfs_create_dir("events", parent); + if (!d_events) + pr_warning("Could not create debugfs 'events' directory\n"); + + /* ring buffer internal formats */ + trace_create_file("header_page", 0444, d_events, + ring_buffer_print_page_header, + &ftrace_show_header_fops); + + trace_create_file("header_event", 0444, d_events, + ring_buffer_print_entry_header, + &ftrace_show_header_fops); + + trace_create_file("enable", 0644, d_events, + tr, &ftrace_tr_enable_fops); + + tr->event_dir = d_events; + __trace_add_event_dirs(tr); + + return 0; +} + static __init int event_trace_enable(void) { + struct trace_array *tr = top_trace_array(); struct ftrace_event_call **iter, *call; char *buf = bootup_event_buf; char *token; @@ -1494,7 +1807,7 @@ static __init int event_trace_enable(void) if (!*token) continue; - ret = ftrace_set_clr_event(token, 1); + ret = ftrace_set_clr_event(tr, token, 1); if (ret) pr_warn("Failed to enable trace event: %s\n", token); } @@ -1506,61 +1819,29 @@ static __init int event_trace_enable(void) static __init int event_trace_init(void) { - struct ftrace_event_call *call; + struct trace_array *tr; struct dentry *d_tracer; struct dentry *entry; - struct dentry *d_events; int ret; + tr = top_trace_array(); + d_tracer = tracing_init_dentry(); if (!d_tracer) return 0; entry = debugfs_create_file("available_events", 0444, d_tracer, - NULL, &ftrace_avail_fops); + tr, &ftrace_avail_fops); if (!entry) pr_warning("Could not create debugfs " "'available_events' entry\n"); - entry = debugfs_create_file("set_event", 0644, d_tracer, - NULL, &ftrace_set_event_fops); - if (!entry) - pr_warning("Could not create debugfs " - "'set_event' entry\n"); - - d_events = event_trace_events_dir(); - if (!d_events) - return 0; - - /* ring buffer internal formats */ - trace_create_file("header_page", 0444, d_events, - ring_buffer_print_page_header, - &ftrace_show_header_fops); - - trace_create_file("header_event", 0444, d_events, - ring_buffer_print_entry_header, - &ftrace_show_header_fops); - - trace_create_file("enable", 0644, d_events, - NULL, &ftrace_system_enable_fops); - if (trace_define_common_fields()) pr_warning("tracing: Failed to allocate common fields"); - /* - * Early initialization already enabled ftrace event. - * Now it's only necessary to create the event directory. - */ - list_for_each_entry(call, &ftrace_events, list) { - - ret = event_create_dir(call, d_events, - &ftrace_event_id_fops, - &ftrace_enable_fops, - &ftrace_event_filter_fops, - &ftrace_event_format_fops); - if (ret < 0) - event_remove(call); - } + ret = event_trace_add_tracer(d_tracer, tr); + if (ret) + return ret; ret = register_module_notifier(&trace_module_nb); if (ret) @@ -1627,13 +1908,20 @@ static __init void event_test_stuff(void) */ static __init void event_trace_self_tests(void) { + struct ftrace_subsystem_dir *dir; + struct ftrace_event_file *file; struct ftrace_event_call *call; struct event_subsystem *system; + struct trace_array *tr; int ret; + tr = top_trace_array(); + pr_info("Running tests on trace events:\n"); - list_for_each_entry(call, &ftrace_events, list) { + list_for_each_entry(file, &tr->events, list) { + + call = file->event_call; /* Only test those that have a probe */ if (!call->class || !call->class->probe) @@ -1657,15 +1945,15 @@ static __init void event_trace_self_tests(void) * If an event is already enabled, someone is using * it and the self test should not be on. */ - if (call->flags & TRACE_EVENT_FL_ENABLED) { + if (file->flags & FTRACE_EVENT_FL_ENABLED) { pr_warning("Enabled event during self test!\n"); WARN_ON_ONCE(1); continue; } - ftrace_event_enable_disable(call, 1); + ftrace_event_enable_disable(file, 1); event_test_stuff(); - ftrace_event_enable_disable(call, 0); + ftrace_event_enable_disable(file, 0); pr_cont("OK\n"); } @@ -1674,7 +1962,9 @@ static __init void event_trace_self_tests(void) pr_info("Running tests on trace event systems:\n"); - list_for_each_entry(system, &event_subsystems, list) { + list_for_each_entry(dir, &tr->systems, list) { + + system = dir->subsystem; /* the ftrace system is special, skip it */ if (strcmp(system->name, "ftrace") == 0) @@ -1682,7 +1972,7 @@ static __init void event_trace_self_tests(void) pr_info("Testing event system %s: ", system->name); - ret = __ftrace_set_clr_event(NULL, system->name, NULL, 1); + ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 1); if (WARN_ON_ONCE(ret)) { pr_warning("error enabling system %s\n", system->name); @@ -1691,7 +1981,7 @@ static __init void event_trace_self_tests(void) event_test_stuff(); - ret = __ftrace_set_clr_event(NULL, system->name, NULL, 0); + ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 0); if (WARN_ON_ONCE(ret)) { pr_warning("error disabling system %s\n", system->name); @@ -1706,7 +1996,7 @@ static __init void event_trace_self_tests(void) pr_info("Running tests on all trace events:\n"); pr_info("Testing all events: "); - ret = __ftrace_set_clr_event(NULL, NULL, NULL, 1); + ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 1); if (WARN_ON_ONCE(ret)) { pr_warning("error enabling all events\n"); return; @@ -1715,7 +2005,7 @@ static __init void event_trace_self_tests(void) event_test_stuff(); /* reset sysname */ - ret = __ftrace_set_clr_event(NULL, NULL, NULL, 0); + ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 0); if (WARN_ON_ONCE(ret)) { pr_warning("error disabling all events\n"); return; diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index e5b0ca8b8d4..2a22a177ab4 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1907,16 +1907,17 @@ out_unlock: return err; } -int apply_subsystem_event_filter(struct event_subsystem *system, +int apply_subsystem_event_filter(struct ftrace_subsystem_dir *dir, char *filter_string) { + struct event_subsystem *system = dir->subsystem; struct event_filter *filter; int err = 0; mutex_lock(&event_mutex); /* Make sure the system still has events */ - if (!system->nr_events) { + if (!dir->nr_events) { err = -ENODEV; goto out_unlock; } -- cgit v1.2.3-70-g09d2 From ccb469a198cffac94a7eea0b69f715f06e2ddf15 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 2 Aug 2012 10:32:10 -0400 Subject: tracing: Pass the ftrace_file to the buffer lock reserve code Pass the struct ftrace_event_file *ftrace_file to the trace_event_buffer_lock_reserve() (new function that replaces the trace_current_buffer_lock_reserver()). The ftrace_file holds a pointer to the trace_array that is in use. In the case of multiple buffers with different trace_arrays, this allows different events to be recorded into different buffers. Also fixed some of the stale comments in include/trace/ftrace.h Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 7 +++++++ include/trace/ftrace.h | 9 +++++---- kernel/trace/trace.c | 12 ++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index c7191d482f9..fd28c170c59 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -128,6 +128,13 @@ enum print_line_t { void tracing_generic_entry_update(struct trace_entry *entry, unsigned long flags, int pc); +struct ftrace_event_file; + +struct ring_buffer_event * +trace_event_buffer_lock_reserve(struct ring_buffer **current_buffer, + struct ftrace_event_file *ftrace_file, + int type, unsigned long len, + unsigned long flags, int pc); struct ring_buffer_event * trace_current_buffer_lock_reserve(struct ring_buffer **current_buffer, int type, unsigned long len, diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 191d9661e27..e5d140a91fd 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -414,7 +414,8 @@ static inline notrace int ftrace_get_offsets_##call( \ * * static void ftrace_raw_event_(void *__data, proto) * { - * struct ftrace_event_call *event_call = __data; + * struct ftrace_event_file *ftrace_file = __data; + * struct ftrace_event_call *event_call = ftrace_file->event_call; * struct ftrace_data_offsets_ __maybe_unused __data_offsets; * struct ring_buffer_event *event; * struct ftrace_raw_ *entry; <-- defined in stage 1 @@ -428,7 +429,7 @@ static inline notrace int ftrace_get_offsets_##call( \ * * __data_size = ftrace_get_offsets_(&__data_offsets, args); * - * event = trace_current_buffer_lock_reserve(&buffer, + * event = trace_event_buffer_lock_reserve(&buffer, ftrace_file, * event_->event.type, * sizeof(*entry) + __data_size, * irq_flags, pc); @@ -440,7 +441,7 @@ static inline notrace int ftrace_get_offsets_##call( \ * __array macros. * * if (!filter_current_check_discard(buffer, event_call, entry, event)) - * trace_current_buffer_unlock_commit(buffer, + * trace_nowake_buffer_unlock_commit(buffer, * event, irq_flags, pc); * } * @@ -533,7 +534,7 @@ ftrace_raw_event_##call(void *__data, proto) \ \ __data_size = ftrace_get_offsets_##call(&__data_offsets, args); \ \ - event = trace_current_buffer_lock_reserve(&buffer, \ + event = trace_event_buffer_lock_reserve(&buffer, ftrace_file, \ event_call->event.type, \ sizeof(*entry) + __data_size, \ irq_flags, pc); \ diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 91fe4090582..29bff72f97e 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1293,6 +1293,18 @@ void trace_buffer_unlock_commit(struct ring_buffer *buffer, } EXPORT_SYMBOL_GPL(trace_buffer_unlock_commit); +struct ring_buffer_event * +trace_event_buffer_lock_reserve(struct ring_buffer **current_rb, + struct ftrace_event_file *ftrace_file, + int type, unsigned long len, + unsigned long flags, int pc) +{ + *current_rb = ftrace_file->tr->buffer; + return trace_buffer_lock_reserve(*current_rb, + type, len, flags, pc); +} +EXPORT_SYMBOL_GPL(trace_event_buffer_lock_reserve); + struct ring_buffer_event * trace_current_buffer_lock_reserve(struct ring_buffer **current_rb, int type, unsigned long len, -- cgit v1.2.3-70-g09d2 From 15693458c4bc0693fd63a50d60f35b628fcf4e29 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 28 Feb 2013 19:59:17 -0500 Subject: tracing/ring-buffer: Move poll wake ups into ring buffer code Move the logic to wake up on ring buffer data into the ring buffer code itself. This simplifies the tracing code a lot and also has the added benefit that waiters on one of the instance buffers can be woken only when data is added to that instance instead of data added to any instance. Signed-off-by: Steven Rostedt --- include/linux/ring_buffer.h | 6 ++ kernel/trace/ring_buffer.c | 146 ++++++++++++++++++++++++++++++++++++++++++++ kernel/trace/trace.c | 83 ++++--------------------- 3 files changed, 164 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index 1342e69542f..d69cf637a15 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -4,6 +4,7 @@ #include #include #include +#include struct ring_buffer; struct ring_buffer_iter; @@ -96,6 +97,11 @@ __ring_buffer_alloc(unsigned long size, unsigned flags, struct lock_class_key *k __ring_buffer_alloc((size), (flags), &__key); \ }) +void ring_buffer_wait(struct ring_buffer *buffer, int cpu); +int ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu, + struct file *filp, poll_table *poll_table); + + #define RING_BUFFER_ALL_CPUS -1 void ring_buffer_free(struct ring_buffer *buffer); diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 7244acde77b..56b6ea32d2e 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -442,6 +443,12 @@ int ring_buffer_print_page_header(struct trace_seq *s) return ret; } +struct rb_irq_work { + struct irq_work work; + wait_queue_head_t waiters; + bool waiters_pending; +}; + /* * head_page == tail_page && head == tail then buffer is empty. */ @@ -476,6 +483,8 @@ struct ring_buffer_per_cpu { struct list_head new_pages; /* new pages to add */ struct work_struct update_pages_work; struct completion update_done; + + struct rb_irq_work irq_work; }; struct ring_buffer { @@ -495,6 +504,8 @@ struct ring_buffer { struct notifier_block cpu_notify; #endif u64 (*clock)(void); + + struct rb_irq_work irq_work; }; struct ring_buffer_iter { @@ -506,6 +517,118 @@ struct ring_buffer_iter { u64 read_stamp; }; +/* + * rb_wake_up_waiters - wake up tasks waiting for ring buffer input + * + * Schedules a delayed work to wake up any task that is blocked on the + * ring buffer waiters queue. + */ +static void rb_wake_up_waiters(struct irq_work *work) +{ + struct rb_irq_work *rbwork = container_of(work, struct rb_irq_work, work); + + wake_up_all(&rbwork->waiters); +} + +/** + * ring_buffer_wait - wait for input to the ring buffer + * @buffer: buffer to wait on + * @cpu: the cpu buffer to wait on + * + * If @cpu == RING_BUFFER_ALL_CPUS then the task will wake up as soon + * as data is added to any of the @buffer's cpu buffers. Otherwise + * it will wait for data to be added to a specific cpu buffer. + */ +void ring_buffer_wait(struct ring_buffer *buffer, int cpu) +{ + struct ring_buffer_per_cpu *cpu_buffer; + DEFINE_WAIT(wait); + struct rb_irq_work *work; + + /* + * Depending on what the caller is waiting for, either any + * data in any cpu buffer, or a specific buffer, put the + * caller on the appropriate wait queue. + */ + if (cpu == RING_BUFFER_ALL_CPUS) + work = &buffer->irq_work; + else { + cpu_buffer = buffer->buffers[cpu]; + work = &cpu_buffer->irq_work; + } + + + prepare_to_wait(&work->waiters, &wait, TASK_INTERRUPTIBLE); + + /* + * The events can happen in critical sections where + * checking a work queue can cause deadlocks. + * After adding a task to the queue, this flag is set + * only to notify events to try to wake up the queue + * using irq_work. + * + * We don't clear it even if the buffer is no longer + * empty. The flag only causes the next event to run + * irq_work to do the work queue wake up. The worse + * that can happen if we race with !trace_empty() is that + * an event will cause an irq_work to try to wake up + * an empty queue. + * + * There's no reason to protect this flag either, as + * the work queue and irq_work logic will do the necessary + * synchronization for the wake ups. The only thing + * that is necessary is that the wake up happens after + * a task has been queued. It's OK for spurious wake ups. + */ + work->waiters_pending = true; + + if ((cpu == RING_BUFFER_ALL_CPUS && ring_buffer_empty(buffer)) || + (cpu != RING_BUFFER_ALL_CPUS && ring_buffer_empty_cpu(buffer, cpu))) + schedule(); + + finish_wait(&work->waiters, &wait); +} + +/** + * ring_buffer_poll_wait - poll on buffer input + * @buffer: buffer to wait on + * @cpu: the cpu buffer to wait on + * @filp: the file descriptor + * @poll_table: The poll descriptor + * + * If @cpu == RING_BUFFER_ALL_CPUS then the task will wake up as soon + * as data is added to any of the @buffer's cpu buffers. Otherwise + * it will wait for data to be added to a specific cpu buffer. + * + * Returns POLLIN | POLLRDNORM if data exists in the buffers, + * zero otherwise. + */ +int ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu, + struct file *filp, poll_table *poll_table) +{ + struct ring_buffer_per_cpu *cpu_buffer; + struct rb_irq_work *work; + + if ((cpu == RING_BUFFER_ALL_CPUS && !ring_buffer_empty(buffer)) || + (cpu != RING_BUFFER_ALL_CPUS && !ring_buffer_empty_cpu(buffer, cpu))) + return POLLIN | POLLRDNORM; + + if (cpu == RING_BUFFER_ALL_CPUS) + work = &buffer->irq_work; + else { + cpu_buffer = buffer->buffers[cpu]; + work = &cpu_buffer->irq_work; + } + + work->waiters_pending = true; + poll_wait(filp, &work->waiters, poll_table); + + if ((cpu == RING_BUFFER_ALL_CPUS && !ring_buffer_empty(buffer)) || + (cpu != RING_BUFFER_ALL_CPUS && !ring_buffer_empty_cpu(buffer, cpu))) + return POLLIN | POLLRDNORM; + return 0; +} + /* buffer may be either ring_buffer or ring_buffer_per_cpu */ #define RB_WARN_ON(b, cond) \ ({ \ @@ -1061,6 +1184,7 @@ rb_allocate_cpu_buffer(struct ring_buffer *buffer, int nr_pages, int cpu) cpu_buffer->lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; INIT_WORK(&cpu_buffer->update_pages_work, update_pages_handler); init_completion(&cpu_buffer->update_done); + init_irq_work(&cpu_buffer->irq_work.work, rb_wake_up_waiters); bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()), GFP_KERNEL, cpu_to_node(cpu)); @@ -1156,6 +1280,8 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, buffer->clock = trace_clock_local; buffer->reader_lock_key = key; + init_irq_work(&buffer->irq_work.work, rb_wake_up_waiters); + /* need at least two pages */ if (nr_pages < 2) nr_pages = 2; @@ -2610,6 +2736,22 @@ static void rb_commit(struct ring_buffer_per_cpu *cpu_buffer, rb_end_commit(cpu_buffer); } +static __always_inline void +rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer) +{ + if (buffer->irq_work.waiters_pending) { + buffer->irq_work.waiters_pending = false; + /* irq_work_queue() supplies it's own memory barriers */ + irq_work_queue(&buffer->irq_work.work); + } + + if (cpu_buffer->irq_work.waiters_pending) { + cpu_buffer->irq_work.waiters_pending = false; + /* irq_work_queue() supplies it's own memory barriers */ + irq_work_queue(&cpu_buffer->irq_work.work); + } +} + /** * ring_buffer_unlock_commit - commit a reserved * @buffer: The buffer to commit to @@ -2629,6 +2771,8 @@ int ring_buffer_unlock_commit(struct ring_buffer *buffer, rb_commit(cpu_buffer, event); + rb_wakeups(buffer, cpu_buffer); + trace_recursive_unlock(); preempt_enable_notrace(); @@ -2801,6 +2945,8 @@ int ring_buffer_write(struct ring_buffer *buffer, rb_commit(cpu_buffer, event); + rb_wakeups(buffer, cpu_buffer); + ret = 0; out: preempt_enable_notrace(); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 3ec146c96df..b5b25b6575a 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -86,14 +85,6 @@ static int dummy_set_flag(u32 old_flags, u32 bit, int set) */ static DEFINE_PER_CPU(bool, trace_cmdline_save); -/* - * When a reader is waiting for data, then this variable is - * set to true. - */ -static bool trace_wakeup_needed; - -static struct irq_work trace_work_wakeup; - /* * Kill all tracing for good (never come back). * It is initialized to 1 but will turn to zero if the initialization @@ -334,28 +325,12 @@ static inline void trace_access_lock_init(void) #endif -/* trace_wait is a waitqueue for tasks blocked on trace_poll */ -static DECLARE_WAIT_QUEUE_HEAD(trace_wait); - /* trace_flags holds trace_options default values */ unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK | TRACE_ITER_ANNOTATE | TRACE_ITER_CONTEXT_INFO | TRACE_ITER_SLEEP_TIME | TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE | TRACE_ITER_IRQ_INFO | TRACE_ITER_MARKERS; -/** - * trace_wake_up - wake up tasks waiting for trace input - * - * Schedules a delayed work to wake up any task that is blocked on the - * trace_wait queue. These is used with trace_poll for tasks polling the - * trace. - */ -static void trace_wake_up(struct irq_work *work) -{ - wake_up_all(&trace_wait); - -} - /** * tracing_on - enable tracing buffers * @@ -763,36 +738,11 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu) static void default_wait_pipe(struct trace_iterator *iter) { - DEFINE_WAIT(wait); - - prepare_to_wait(&trace_wait, &wait, TASK_INTERRUPTIBLE); - - /* - * The events can happen in critical sections where - * checking a work queue can cause deadlocks. - * After adding a task to the queue, this flag is set - * only to notify events to try to wake up the queue - * using irq_work. - * - * We don't clear it even if the buffer is no longer - * empty. The flag only causes the next event to run - * irq_work to do the work queue wake up. The worse - * that can happen if we race with !trace_empty() is that - * an event will cause an irq_work to try to wake up - * an empty queue. - * - * There's no reason to protect this flag either, as - * the work queue and irq_work logic will do the necessary - * synchronization for the wake ups. The only thing - * that is necessary is that the wake up happens after - * a task has been queued. It's OK for spurious wake ups. - */ - trace_wakeup_needed = true; - - if (trace_empty(iter)) - schedule(); + /* Iterators are static, they should be filled or empty */ + if (trace_buffer_iter(iter, iter->cpu_file)) + return; - finish_wait(&trace_wait, &wait); + ring_buffer_wait(iter->tr->buffer, iter->cpu_file); } /** @@ -1262,11 +1212,6 @@ void __buffer_unlock_commit(struct ring_buffer *buffer, struct ring_buffer_event *event) { __this_cpu_write(trace_cmdline_save, true); - if (trace_wakeup_needed) { - trace_wakeup_needed = false; - /* irq_work_queue() supplies it's own memory barriers */ - irq_work_queue(&trace_work_wakeup); - } ring_buffer_unlock_commit(buffer, event); } @@ -3557,21 +3502,18 @@ static int tracing_release_pipe(struct inode *inode, struct file *file) static unsigned int trace_poll(struct trace_iterator *iter, struct file *filp, poll_table *poll_table) { - if (trace_flags & TRACE_ITER_BLOCK) { + /* Iterators are static, they should be filled or empty */ + if (trace_buffer_iter(iter, iter->cpu_file)) + return POLLIN | POLLRDNORM; + + if (trace_flags & TRACE_ITER_BLOCK) /* * Always select as readable when in blocking mode */ return POLLIN | POLLRDNORM; - } else { - if (!trace_empty(iter)) - return POLLIN | POLLRDNORM; - trace_wakeup_needed = true; - poll_wait(filp, &trace_wait, poll_table); - if (!trace_empty(iter)) - return POLLIN | POLLRDNORM; - - return 0; - } + else + return ring_buffer_poll_wait(iter->tr->buffer, iter->cpu_file, + filp, poll_table); } static unsigned int @@ -5701,7 +5643,6 @@ __init static int tracer_alloc_buffers(void) #endif trace_init_cmdlines(); - init_irq_work(&trace_work_wakeup, trace_wake_up); register_tracer(&nop_trace); -- cgit v1.2.3-70-g09d2 From f71130de5c7fba92faf3901784714e37a234c08f Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Thu, 21 Feb 2013 10:32:38 +0800 Subject: tracing: Add a helper function for event print functions Move duplicate code in event print functions to a helper function. This shrinks the size of the kernel by ~13K. text data bss dec hex filename 6596137 1743966 10138672 18478775 119f6b7 vmlinux.o.old 6583002 1743849 10138672 18465523 119c2f3 vmlinux.o.new Link: http://lkml.kernel.org/r/51258746.2060304@huawei.com Signed-off-by: Li Zefan Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 8 ++++++-- include/trace/ftrace.h | 23 ++++++----------------- kernel/trace/trace_output.c | 26 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index fd28c170c59..4d79d2dc189 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -38,6 +38,12 @@ const char *ftrace_print_symbols_seq_u64(struct trace_seq *p, const char *ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int len); +struct trace_iterator; +struct trace_event; + +int ftrace_raw_output_prep(struct trace_iterator *iter, + struct trace_event *event); + /* * The trace entry - the most basic unit of tracing. This is what * is printed in the end as a single line in the trace output, such as: @@ -95,8 +101,6 @@ enum trace_iter_flags { }; -struct trace_event; - typedef enum print_line_t (*trace_print_func)(struct trace_iterator *iter, int flags, struct trace_event *event); diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index e5d140a91fd..17a77fcac2a 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -227,29 +227,18 @@ static notrace enum print_line_t \ ftrace_raw_output_##call(struct trace_iterator *iter, int flags, \ struct trace_event *trace_event) \ { \ - struct ftrace_event_call *event; \ struct trace_seq *s = &iter->seq; \ + struct trace_seq __maybe_unused *p = &iter->tmp_seq; \ struct ftrace_raw_##call *field; \ - struct trace_entry *entry; \ - struct trace_seq *p = &iter->tmp_seq; \ int ret; \ \ - event = container_of(trace_event, struct ftrace_event_call, \ - event); \ - \ - entry = iter->ent; \ + field = (typeof(field))iter->ent; \ \ - if (entry->type != event->event.type) { \ - WARN_ON_ONCE(1); \ - return TRACE_TYPE_UNHANDLED; \ - } \ - \ - field = (typeof(field))entry; \ - \ - trace_seq_init(p); \ - ret = trace_seq_printf(s, "%s: ", event->name); \ + ret = ftrace_raw_output_prep(iter, trace_event); \ if (ret) \ - ret = trace_seq_printf(s, print); \ + return ret; \ + \ + ret = trace_seq_printf(s, print); \ if (!ret) \ return TRACE_TYPE_PARTIAL_LINE; \ \ diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 194d79602dc..aa92ac322ba 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -397,6 +397,32 @@ ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int buf_len) } EXPORT_SYMBOL(ftrace_print_hex_seq); +int ftrace_raw_output_prep(struct trace_iterator *iter, + struct trace_event *trace_event) +{ + struct ftrace_event_call *event; + struct trace_seq *s = &iter->seq; + struct trace_seq *p = &iter->tmp_seq; + struct trace_entry *entry; + int ret; + + event = container_of(trace_event, struct ftrace_event_call, event); + entry = iter->ent; + + if (entry->type != event->event.type) { + WARN_ON_ONCE(1); + return TRACE_TYPE_UNHANDLED; + } + + trace_seq_init(p); + ret = trace_seq_printf(s, "%s: ", event->name); + if (!ret) + return TRACE_TYPE_PARTIAL_LINE; + + return 0; +} +EXPORT_SYMBOL(ftrace_raw_output_prep); + #ifdef CONFIG_KRETPROBES static inline const char *kretprobed(const char *name) { -- cgit v1.2.3-70-g09d2 From 7e4f44b153e1ec07bb64c1c1671cdf492465bbf3 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Thu, 21 Feb 2013 10:33:33 +0800 Subject: tracing: Annotate event field-defining functions with __init Those functions are called either during kernel boot or module init. Before: $ dmesg | grep 'Freeing unused kernel memory' Freeing unused kernel memory: 1208k freed Freeing unused kernel memory: 1360k freed Freeing unused kernel memory: 1960k freed After: $ dmesg | grep 'Freeing unused kernel memory' Freeing unused kernel memory: 1236k freed Freeing unused kernel memory: 1388k freed Freeing unused kernel memory: 1960k freed Link: http://lkml.kernel.org/r/5125877D.5000201@huawei.com Signed-off-by: Li Zefan Signed-off-by: Steven Rostedt --- include/trace/ftrace.h | 2 +- kernel/trace/trace_export.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 17a77fcac2a..a536f66f84c 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -324,7 +324,7 @@ static struct trace_event_functions ftrace_event_type_funcs_##call = { \ #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, func, print) \ -static int notrace \ +static int notrace __init \ ftrace_define_fields_##call(struct ftrace_event_call *event_call) \ { \ struct ftrace_raw_##call field; \ diff --git a/kernel/trace/trace_export.c b/kernel/trace/trace_export.c index e039906b037..4f6a91c1370 100644 --- a/kernel/trace/trace_export.c +++ b/kernel/trace/trace_export.c @@ -129,7 +129,7 @@ static void __always_unused ____ftrace_check_##name(void) \ #undef FTRACE_ENTRY #define FTRACE_ENTRY(name, struct_name, id, tstruct, print, filter) \ -int \ +static int __init \ ftrace_define_fields_##name(struct ftrace_event_call *event_call) \ { \ struct struct_name field; \ -- cgit v1.2.3-70-g09d2 From 523c81135bb23b2d9a8c21365d90d21b1309c138 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 4 Mar 2013 14:15:59 +0800 Subject: tracing: Fix some section mismatch warnings As we've added __init annotation to field-defining functions, we should add __refdata annotation to event_call variables, which reference those functions. Link: http://lkml.kernel.org/r/51343C1F.2050502@huawei.com Reported-by: Fengguang Wu Signed-off-by: Li Zefan Signed-off-by: Steven Rostedt --- include/trace/ftrace.h | 2 +- kernel/trace/trace_export.c | 2 +- kernel/trace/trace_syscalls.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index a536f66f84c..bbf09c2021b 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -572,7 +572,7 @@ static inline void ftrace_test_probe_##call(void) \ #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ _TRACE_PERF_PROTO(call, PARAMS(proto)); \ static const char print_fmt_##call[] = print; \ -static struct ftrace_event_class __used event_class_##call = { \ +static struct ftrace_event_class __used __refdata event_class_##call = { \ .system = __stringify(TRACE_SYSTEM), \ .define_fields = ftrace_define_fields_##call, \ .fields = LIST_HEAD_INIT(event_class_##call.fields),\ diff --git a/kernel/trace/trace_export.c b/kernel/trace/trace_export.c index 4f6a91c1370..d21a7467008 100644 --- a/kernel/trace/trace_export.c +++ b/kernel/trace/trace_export.c @@ -168,7 +168,7 @@ ftrace_define_fields_##name(struct ftrace_event_call *event_call) \ #define FTRACE_ENTRY_REG(call, struct_name, etype, tstruct, print, filter,\ regfn) \ \ -struct ftrace_event_class event_class_ftrace_##call = { \ +struct ftrace_event_class __refdata event_class_ftrace_##call = { \ .system = __stringify(TRACE_SYSTEM), \ .define_fields = ftrace_define_fields_##call, \ .fields = LIST_HEAD_INIT(event_class_ftrace_##call.fields),\ diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 00b5c3e6fbb..1cd37ffb409 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -479,7 +479,7 @@ struct trace_event_functions exit_syscall_print_funcs = { .trace = print_syscall_exit, }; -struct ftrace_event_class event_class_syscall_enter = { +struct ftrace_event_class __refdata event_class_syscall_enter = { .system = "syscalls", .reg = syscall_enter_register, .define_fields = syscall_enter_define_fields, @@ -487,7 +487,7 @@ struct ftrace_event_class event_class_syscall_enter = { .raw_init = init_syscall_trace, }; -struct ftrace_event_class event_class_syscall_exit = { +struct ftrace_event_class __refdata event_class_syscall_exit = { .system = "syscalls", .reg = syscall_exit_register, .define_fields = syscall_exit_define_fields, -- cgit v1.2.3-70-g09d2 From 2a30c11f6a037e2475f3c651bc57e697e79fa963 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 4 Mar 2013 22:27:04 -0500 Subject: tracing: Add comment for trace event flag IGNORE_ENABLE All the trace event flags have comments but the IGNORE_ENABLE flag which is set for ftrace internal events that should not be enabled via the debugfs "enable" file. That is, if the top level enable file is set, it will enable all events. It use to just check the ftrace event call descriptor "reg" field and skip those whithout it, but now some ftrace internal events have a reg field but still need to be skipped. The flag was created to ignore those events. Now document it. Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 4d79d2dc189..0b0814d9016 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -204,6 +204,7 @@ enum { * FILTERED - The event has a filter attached * CAP_ANY - Any user can enable for perf * NO_SET_FILTER - Set when filter has error and is to be ignored + * IGNORE_ENABLE - For ftrace internal events, do not enable with debugfs file */ enum { TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT), -- cgit v1.2.3-70-g09d2 From 575380da8b46969a2c6a7e14a51056a63b30fe2e Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 4 Mar 2013 23:05:12 -0500 Subject: tracing: Only clear trace buffer on module unload if event was traced Currently, when a module with events is unloaded, the trace buffer is cleared. This is just a safety net in case the module might have some strange callback when its event is outputted. But there's no reason to reset the buffer if the module didn't have any of its events traced. Add a flag to the event "call" structure called WAS_ENABLED and gets set when the event is ever enabled, and this flag never gets cleared. When a module gets unloaded, if any of its events have this flag set, then the trace buffer will get cleared. Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 5 +++++ kernel/trace/trace_events.c | 12 ++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 0b0814d9016..d6964244e56 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -197,6 +197,7 @@ enum { TRACE_EVENT_FL_CAP_ANY_BIT, TRACE_EVENT_FL_NO_SET_FILTER_BIT, TRACE_EVENT_FL_IGNORE_ENABLE_BIT, + TRACE_EVENT_FL_WAS_ENABLED_BIT, }; /* @@ -205,12 +206,16 @@ enum { * CAP_ANY - Any user can enable for perf * NO_SET_FILTER - Set when filter has error and is to be ignored * IGNORE_ENABLE - For ftrace internal events, do not enable with debugfs file + * WAS_ENABLED - Set and stays set when an event was ever enabled + * (used for module unloading, if a module event is enabled, + * it is best to clear the buffers that used it). */ enum { TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT), TRACE_EVENT_FL_CAP_ANY = (1 << TRACE_EVENT_FL_CAP_ANY_BIT), TRACE_EVENT_FL_NO_SET_FILTER = (1 << TRACE_EVENT_FL_NO_SET_FILTER_BIT), TRACE_EVENT_FL_IGNORE_ENABLE = (1 << TRACE_EVENT_FL_IGNORE_ENABLE_BIT), + TRACE_EVENT_FL_WAS_ENABLED = (1 << TRACE_EVENT_FL_WAS_ENABLED_BIT), }; struct ftrace_event_call { diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 0f1307a29fc..9a7dc4bf117 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -245,6 +245,9 @@ static int ftrace_event_enable_disable(struct ftrace_event_file *file, break; } file->flags |= FTRACE_EVENT_FL_ENABLED; + + /* WAS_ENABLED gets set but never cleared. */ + call->flags |= TRACE_EVENT_FL_WAS_ENABLED; } break; } @@ -1626,12 +1629,13 @@ static void trace_module_remove_events(struct module *mod) { struct ftrace_module_file_ops *file_ops; struct ftrace_event_call *call, *p; - bool found = false; + bool clear_trace = false; down_write(&trace_event_mutex); list_for_each_entry_safe(call, p, &ftrace_events, list) { if (call->mod == mod) { - found = true; + if (call->flags & TRACE_EVENT_FL_WAS_ENABLED) + clear_trace = true; __trace_remove_event_call(call); } } @@ -1648,9 +1652,9 @@ static void trace_module_remove_events(struct module *mod) /* * It is safest to reset the ring buffer if the module being unloaded - * registered any events. + * registered any events that were used. */ - if (found) + if (clear_trace) tracing_reset_current_online_cpus(); up_write(&trace_event_mutex); } -- cgit v1.2.3-70-g09d2 From 12883efb670c28dff57dcd7f4f995a1ffe153b2d Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 5 Mar 2013 09:24:35 -0500 Subject: tracing: Consolidate max_tr into main trace_array structure Currently, the way the latency tracers and snapshot feature works is to have a separate trace_array called "max_tr" that holds the snapshot buffer. For latency tracers, this snapshot buffer is used to swap the running buffer with this buffer to save the current max latency. The only items needed for the max_tr is really just a copy of the buffer itself, the per_cpu data pointers, the time_start timestamp that states when the max latency was triggered, and the cpu that the max latency was triggered on. All other fields in trace_array are unused by the max_tr, making the max_tr mostly bloat. This change removes the max_tr completely, and adds a new structure called trace_buffer, that holds the buffer pointer, the per_cpu data pointers, the time_start timestamp, and the cpu where the latency occurred. The trace_array, now has two trace_buffers, one for the normal trace and one for the max trace or snapshot. By doing this, not only do we remove the bloat from the max_trace but the instances of traces can now use their own snapshot feature and not have just the top level global_trace have the snapshot feature and latency tracers for itself. Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 2 + kernel/trace/blktrace.c | 4 +- kernel/trace/trace.c | 486 +++++++++++++++++++---------------- kernel/trace/trace.h | 37 ++- kernel/trace/trace_functions.c | 8 +- kernel/trace/trace_functions_graph.c | 12 +- kernel/trace/trace_irqsoff.c | 10 +- kernel/trace/trace_kdb.c | 8 +- kernel/trace/trace_mmiotrace.c | 12 +- kernel/trace/trace_output.c | 2 +- kernel/trace/trace_sched_switch.c | 8 +- kernel/trace/trace_sched_wakeup.c | 16 +- kernel/trace/trace_selftest.c | 42 +-- kernel/trace/trace_syscalls.c | 4 +- 14 files changed, 365 insertions(+), 286 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index d6964244e56..d84c4a57551 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -8,6 +8,7 @@ #include struct trace_array; +struct trace_buffer; struct tracer; struct dentry; @@ -67,6 +68,7 @@ struct trace_entry { struct trace_iterator { struct trace_array *tr; struct tracer *trace; + struct trace_buffer *trace_buffer; void *private; int cpu_file; struct mutex mutex; diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c index 71259e2b6b6..90a55054744 100644 --- a/kernel/trace/blktrace.c +++ b/kernel/trace/blktrace.c @@ -72,7 +72,7 @@ static void trace_note(struct blk_trace *bt, pid_t pid, int action, bool blk_tracer = blk_tracer_enabled; if (blk_tracer) { - buffer = blk_tr->buffer; + buffer = blk_tr->trace_buffer.buffer; pc = preempt_count(); event = trace_buffer_lock_reserve(buffer, TRACE_BLK, sizeof(*t) + len, @@ -218,7 +218,7 @@ static void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, if (blk_tracer) { tracing_record_cmdline(current); - buffer = blk_tr->buffer; + buffer = blk_tr->trace_buffer.buffer; pc = preempt_count(); event = trace_buffer_lock_reserve(buffer, TRACE_BLK, sizeof(*t) + pdu_len, diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c8a852a55db..a08c127db86 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -195,27 +195,15 @@ cycle_t ftrace_now(int cpu) u64 ts; /* Early boot up does not have a buffer yet */ - if (!global_trace.buffer) + if (!global_trace.trace_buffer.buffer) return trace_clock_local(); - ts = ring_buffer_time_stamp(global_trace.buffer, cpu); - ring_buffer_normalize_time_stamp(global_trace.buffer, cpu, &ts); + ts = ring_buffer_time_stamp(global_trace.trace_buffer.buffer, cpu); + ring_buffer_normalize_time_stamp(global_trace.trace_buffer.buffer, cpu, &ts); return ts; } -/* - * The max_tr is used to snapshot the global_trace when a maximum - * latency is reached. Some tracers will use this to store a maximum - * trace while it continues examining live traces. - * - * The buffers for the max_tr are set up the same as the global_trace. - * When a snapshot is taken, the link list of the max_tr is swapped - * with the link list of the global_trace and the buffers are reset for - * the global_trace so the tracing can continue. - */ -static struct trace_array max_tr; - int tracing_is_enabled(void) { return tracing_is_on(); @@ -339,8 +327,8 @@ unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK | */ void tracing_on(void) { - if (global_trace.buffer) - ring_buffer_record_on(global_trace.buffer); + if (global_trace.trace_buffer.buffer) + ring_buffer_record_on(global_trace.trace_buffer.buffer); /* * This flag is only looked at when buffers haven't been * allocated yet. We don't really care about the race @@ -361,8 +349,8 @@ EXPORT_SYMBOL_GPL(tracing_on); */ void tracing_off(void) { - if (global_trace.buffer) - ring_buffer_record_off(global_trace.buffer); + if (global_trace.trace_buffer.buffer) + ring_buffer_record_off(global_trace.trace_buffer.buffer); /* * This flag is only looked at when buffers haven't been * allocated yet. We don't really care about the race @@ -378,8 +366,8 @@ EXPORT_SYMBOL_GPL(tracing_off); */ int tracing_is_on(void) { - if (global_trace.buffer) - return ring_buffer_record_is_on(global_trace.buffer); + if (global_trace.trace_buffer.buffer) + return ring_buffer_record_is_on(global_trace.trace_buffer.buffer); return !global_trace.buffer_disabled; } EXPORT_SYMBOL_GPL(tracing_is_on); @@ -637,13 +625,14 @@ unsigned long __read_mostly tracing_max_latency; static void __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) { - struct trace_array_cpu *data = per_cpu_ptr(tr->data, cpu); - struct trace_array_cpu *max_data; + struct trace_buffer *trace_buf = &tr->trace_buffer; + struct trace_buffer *max_buf = &tr->max_buffer; + struct trace_array_cpu *data = per_cpu_ptr(trace_buf->data, cpu); + struct trace_array_cpu *max_data = per_cpu_ptr(max_buf->data, cpu); - max_tr.cpu = cpu; - max_tr.time_start = data->preempt_timestamp; + max_buf->cpu = cpu; + max_buf->time_start = data->preempt_timestamp; - max_data = per_cpu_ptr(max_tr.data, cpu); max_data->saved_latency = tracing_max_latency; max_data->critical_start = data->critical_start; max_data->critical_end = data->critical_end; @@ -686,9 +675,9 @@ update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu) arch_spin_lock(&ftrace_max_lock); - buf = tr->buffer; - tr->buffer = max_tr.buffer; - max_tr.buffer = buf; + buf = tr->trace_buffer.buffer; + tr->trace_buffer.buffer = tr->max_buffer.buffer; + tr->max_buffer.buffer = buf; __update_max_tr(tr, tsk, cpu); arch_spin_unlock(&ftrace_max_lock); @@ -716,7 +705,7 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu) arch_spin_lock(&ftrace_max_lock); - ret = ring_buffer_swap_cpu(max_tr.buffer, tr->buffer, cpu); + ret = ring_buffer_swap_cpu(tr->max_buffer.buffer, tr->trace_buffer.buffer, cpu); if (ret == -EBUSY) { /* @@ -725,7 +714,7 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu) * the max trace buffer (no one writes directly to it) * and flag that it failed. */ - trace_array_printk(&max_tr, _THIS_IP_, + trace_array_printk_buf(tr->max_buffer.buffer, _THIS_IP_, "Failed to swap buffers due to commit in progress\n"); } @@ -742,7 +731,7 @@ static void default_wait_pipe(struct trace_iterator *iter) if (trace_buffer_iter(iter, iter->cpu_file)) return; - ring_buffer_wait(iter->tr->buffer, iter->cpu_file); + ring_buffer_wait(iter->trace_buffer->buffer, iter->cpu_file); } /** @@ -803,17 +792,19 @@ int register_tracer(struct tracer *type) * internal tracing to verify that everything is in order. * If we fail, we do not register this tracer. */ - tracing_reset_online_cpus(tr); + tracing_reset_online_cpus(&tr->trace_buffer); tr->current_trace = type; +#ifdef CONFIG_TRACER_MAX_TRACE if (type->use_max_tr) { /* If we expanded the buffers, make sure the max is expanded too */ if (ring_buffer_expanded) - ring_buffer_resize(max_tr.buffer, trace_buf_size, + ring_buffer_resize(tr->max_buffer.buffer, trace_buf_size, RING_BUFFER_ALL_CPUS); type->allocated_snapshot = true; } +#endif /* the test is responsible for initializing and enabling */ pr_info("Testing tracer %s: ", type->name); @@ -827,16 +818,18 @@ int register_tracer(struct tracer *type) goto out; } /* Only reset on passing, to avoid touching corrupted buffers */ - tracing_reset_online_cpus(tr); + tracing_reset_online_cpus(&tr->trace_buffer); +#ifdef CONFIG_TRACER_MAX_TRACE if (type->use_max_tr) { type->allocated_snapshot = false; /* Shrink the max buffer again */ if (ring_buffer_expanded) - ring_buffer_resize(max_tr.buffer, 1, + ring_buffer_resize(tr->max_buffer.buffer, 1, RING_BUFFER_ALL_CPUS); } +#endif printk(KERN_CONT "PASSED\n"); } @@ -870,9 +863,9 @@ int register_tracer(struct tracer *type) return ret; } -void tracing_reset(struct trace_array *tr, int cpu) +void tracing_reset(struct trace_buffer *buf, int cpu) { - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = buf->buffer; if (!buffer) return; @@ -886,9 +879,9 @@ void tracing_reset(struct trace_array *tr, int cpu) ring_buffer_record_enable(buffer); } -void tracing_reset_online_cpus(struct trace_array *tr) +void tracing_reset_online_cpus(struct trace_buffer *buf) { - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = buf->buffer; int cpu; if (!buffer) @@ -899,7 +892,7 @@ void tracing_reset_online_cpus(struct trace_array *tr) /* Make sure all commits have finished */ synchronize_sched(); - tr->time_start = ftrace_now(tr->cpu); + buf->time_start = ftrace_now(buf->cpu); for_each_online_cpu(cpu) ring_buffer_reset_cpu(buffer, cpu); @@ -909,7 +902,7 @@ void tracing_reset_online_cpus(struct trace_array *tr) void tracing_reset_current(int cpu) { - tracing_reset(&global_trace, cpu); + tracing_reset(&global_trace.trace_buffer, cpu); } void tracing_reset_all_online_cpus(void) @@ -918,7 +911,10 @@ void tracing_reset_all_online_cpus(void) mutex_lock(&trace_types_lock); list_for_each_entry(tr, &ftrace_trace_arrays, list) { - tracing_reset_online_cpus(tr); + tracing_reset_online_cpus(&tr->trace_buffer); +#ifdef CONFIG_TRACER_MAX_TRACE + tracing_reset_online_cpus(&tr->max_buffer); +#endif } mutex_unlock(&trace_types_lock); } @@ -988,13 +984,15 @@ void tracing_start(void) /* Prevent the buffers from switching */ arch_spin_lock(&ftrace_max_lock); - buffer = global_trace.buffer; + buffer = global_trace.trace_buffer.buffer; if (buffer) ring_buffer_record_enable(buffer); - buffer = max_tr.buffer; +#ifdef CONFIG_TRACER_MAX_TRACE + buffer = global_trace.max_buffer.buffer; if (buffer) ring_buffer_record_enable(buffer); +#endif arch_spin_unlock(&ftrace_max_lock); @@ -1026,7 +1024,7 @@ static void tracing_start_tr(struct trace_array *tr) goto out; } - buffer = tr->buffer; + buffer = tr->trace_buffer.buffer; if (buffer) ring_buffer_record_enable(buffer); @@ -1053,13 +1051,15 @@ void tracing_stop(void) /* Prevent the buffers from switching */ arch_spin_lock(&ftrace_max_lock); - buffer = global_trace.buffer; + buffer = global_trace.trace_buffer.buffer; if (buffer) ring_buffer_record_disable(buffer); - buffer = max_tr.buffer; +#ifdef CONFIG_TRACER_MAX_TRACE + buffer = global_trace.max_buffer.buffer; if (buffer) ring_buffer_record_disable(buffer); +#endif arch_spin_unlock(&ftrace_max_lock); @@ -1080,7 +1080,7 @@ static void tracing_stop_tr(struct trace_array *tr) if (tr->stop_count++) goto out; - buffer = tr->buffer; + buffer = tr->trace_buffer.buffer; if (buffer) ring_buffer_record_disable(buffer); @@ -1246,7 +1246,7 @@ trace_event_buffer_lock_reserve(struct ring_buffer **current_rb, int type, unsigned long len, unsigned long flags, int pc) { - *current_rb = ftrace_file->tr->buffer; + *current_rb = ftrace_file->tr->trace_buffer.buffer; return trace_buffer_lock_reserve(*current_rb, type, len, flags, pc); } @@ -1257,7 +1257,7 @@ trace_current_buffer_lock_reserve(struct ring_buffer **current_rb, int type, unsigned long len, unsigned long flags, int pc) { - *current_rb = global_trace.buffer; + *current_rb = global_trace.trace_buffer.buffer; return trace_buffer_lock_reserve(*current_rb, type, len, flags, pc); } @@ -1296,7 +1296,7 @@ trace_function(struct trace_array *tr, int pc) { struct ftrace_event_call *call = &event_function; - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = tr->trace_buffer.buffer; struct ring_buffer_event *event; struct ftrace_entry *entry; @@ -1437,7 +1437,7 @@ void ftrace_trace_stack(struct ring_buffer *buffer, unsigned long flags, void __trace_stack(struct trace_array *tr, unsigned long flags, int skip, int pc) { - __ftrace_trace_stack(tr->buffer, flags, skip, pc, NULL); + __ftrace_trace_stack(tr->trace_buffer.buffer, flags, skip, pc, NULL); } /** @@ -1453,7 +1453,8 @@ void trace_dump_stack(void) local_save_flags(flags); /* skipping 3 traces, seems to get us at the caller of this function */ - __ftrace_trace_stack(global_trace.buffer, flags, 3, preempt_count(), NULL); + __ftrace_trace_stack(global_trace.trace_buffer.buffer, flags, 3, + preempt_count(), NULL); } static DEFINE_PER_CPU(int, user_stack_count); @@ -1623,7 +1624,7 @@ void trace_printk_init_buffers(void) * directly here. If the global_trace.buffer is already * allocated here, then this was called by module code. */ - if (global_trace.buffer) + if (global_trace.trace_buffer.buffer) tracing_start_cmdline_record(); } @@ -1683,7 +1684,7 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args) local_save_flags(flags); size = sizeof(*entry) + sizeof(u32) * len; - buffer = tr->buffer; + buffer = tr->trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, TRACE_BPRINT, size, flags, pc); if (!event) @@ -1706,27 +1707,12 @@ out: } EXPORT_SYMBOL_GPL(trace_vbprintk); -int trace_array_printk(struct trace_array *tr, - unsigned long ip, const char *fmt, ...) -{ - int ret; - va_list ap; - - if (!(trace_flags & TRACE_ITER_PRINTK)) - return 0; - - va_start(ap, fmt); - ret = trace_array_vprintk(tr, ip, fmt, ap); - va_end(ap); - return ret; -} - -int trace_array_vprintk(struct trace_array *tr, - unsigned long ip, const char *fmt, va_list args) +static int +__trace_array_vprintk(struct ring_buffer *buffer, + unsigned long ip, const char *fmt, va_list args) { struct ftrace_event_call *call = &event_print; struct ring_buffer_event *event; - struct ring_buffer *buffer; int len = 0, size, pc; struct print_entry *entry; unsigned long flags; @@ -1754,7 +1740,6 @@ int trace_array_vprintk(struct trace_array *tr, local_save_flags(flags); size = sizeof(*entry) + len + 1; - buffer = tr->buffer; event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, size, flags, pc); if (!event) @@ -1775,6 +1760,42 @@ int trace_array_vprintk(struct trace_array *tr, return len; } +int trace_array_vprintk(struct trace_array *tr, + unsigned long ip, const char *fmt, va_list args) +{ + return __trace_array_vprintk(tr->trace_buffer.buffer, ip, fmt, args); +} + +int trace_array_printk(struct trace_array *tr, + unsigned long ip, const char *fmt, ...) +{ + int ret; + va_list ap; + + if (!(trace_flags & TRACE_ITER_PRINTK)) + return 0; + + va_start(ap, fmt); + ret = trace_array_vprintk(tr, ip, fmt, ap); + va_end(ap); + return ret; +} + +int trace_array_printk_buf(struct ring_buffer *buffer, + unsigned long ip, const char *fmt, ...) +{ + int ret; + va_list ap; + + if (!(trace_flags & TRACE_ITER_PRINTK)) + return 0; + + va_start(ap, fmt); + ret = __trace_array_vprintk(buffer, ip, fmt, ap); + va_end(ap); + return ret; +} + int trace_vprintk(unsigned long ip, const char *fmt, va_list args) { return trace_array_vprintk(&global_trace, ip, fmt, args); @@ -1800,7 +1821,7 @@ peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts, if (buf_iter) event = ring_buffer_iter_peek(buf_iter, ts); else - event = ring_buffer_peek(iter->tr->buffer, cpu, ts, + event = ring_buffer_peek(iter->trace_buffer->buffer, cpu, ts, lost_events); if (event) { @@ -1815,7 +1836,7 @@ static struct trace_entry * __find_next_entry(struct trace_iterator *iter, int *ent_cpu, unsigned long *missing_events, u64 *ent_ts) { - struct ring_buffer *buffer = iter->tr->buffer; + struct ring_buffer *buffer = iter->trace_buffer->buffer; struct trace_entry *ent, *next = NULL; unsigned long lost_events = 0, next_lost = 0; int cpu_file = iter->cpu_file; @@ -1892,7 +1913,7 @@ void *trace_find_next_entry_inc(struct trace_iterator *iter) static void trace_consume(struct trace_iterator *iter) { - ring_buffer_consume(iter->tr->buffer, iter->cpu, &iter->ts, + ring_buffer_consume(iter->trace_buffer->buffer, iter->cpu, &iter->ts, &iter->lost_events); } @@ -1925,13 +1946,12 @@ static void *s_next(struct seq_file *m, void *v, loff_t *pos) void tracing_iter_reset(struct trace_iterator *iter, int cpu) { - struct trace_array *tr = iter->tr; struct ring_buffer_event *event; struct ring_buffer_iter *buf_iter; unsigned long entries = 0; u64 ts; - per_cpu_ptr(tr->data, cpu)->skipped_entries = 0; + per_cpu_ptr(iter->trace_buffer->data, cpu)->skipped_entries = 0; buf_iter = trace_buffer_iter(iter, cpu); if (!buf_iter) @@ -1945,13 +1965,13 @@ void tracing_iter_reset(struct trace_iterator *iter, int cpu) * by the timestamp being before the start of the buffer. */ while ((event = ring_buffer_iter_peek(buf_iter, &ts))) { - if (ts >= iter->tr->time_start) + if (ts >= iter->trace_buffer->time_start) break; entries++; ring_buffer_read(buf_iter, NULL); } - per_cpu_ptr(tr->data, cpu)->skipped_entries = entries; + per_cpu_ptr(iter->trace_buffer->data, cpu)->skipped_entries = entries; } /* @@ -1978,8 +1998,10 @@ static void *s_start(struct seq_file *m, loff_t *pos) *iter->trace = *tr->current_trace; mutex_unlock(&trace_types_lock); +#ifdef CONFIG_TRACER_MAX_TRACE if (iter->snapshot && iter->trace->use_max_tr) return ERR_PTR(-EBUSY); +#endif if (!iter->snapshot) atomic_inc(&trace_record_cmdline_disabled); @@ -2021,17 +2043,21 @@ static void s_stop(struct seq_file *m, void *p) { struct trace_iterator *iter = m->private; +#ifdef CONFIG_TRACER_MAX_TRACE if (iter->snapshot && iter->trace->use_max_tr) return; +#endif if (!iter->snapshot) atomic_dec(&trace_record_cmdline_disabled); + trace_access_unlock(iter->cpu_file); trace_event_read_unlock(); } static void -get_total_entries(struct trace_array *tr, unsigned long *total, unsigned long *entries) +get_total_entries(struct trace_buffer *buf, + unsigned long *total, unsigned long *entries) { unsigned long count; int cpu; @@ -2040,19 +2066,19 @@ get_total_entries(struct trace_array *tr, unsigned long *total, unsigned long *e *entries = 0; for_each_tracing_cpu(cpu) { - count = ring_buffer_entries_cpu(tr->buffer, cpu); + count = ring_buffer_entries_cpu(buf->buffer, cpu); /* * If this buffer has skipped entries, then we hold all * entries for the trace and we need to ignore the * ones before the time stamp. */ - if (per_cpu_ptr(tr->data, cpu)->skipped_entries) { - count -= per_cpu_ptr(tr->data, cpu)->skipped_entries; + if (per_cpu_ptr(buf->data, cpu)->skipped_entries) { + count -= per_cpu_ptr(buf->data, cpu)->skipped_entries; /* total is the same as the entries */ *total += count; } else *total += count + - ring_buffer_overrun_cpu(tr->buffer, cpu); + ring_buffer_overrun_cpu(buf->buffer, cpu); *entries += count; } } @@ -2069,27 +2095,27 @@ static void print_lat_help_header(struct seq_file *m) seq_puts(m, "# \\ / ||||| \\ | / \n"); } -static void print_event_info(struct trace_array *tr, struct seq_file *m) +static void print_event_info(struct trace_buffer *buf, struct seq_file *m) { unsigned long total; unsigned long entries; - get_total_entries(tr, &total, &entries); + get_total_entries(buf, &total, &entries); seq_printf(m, "# entries-in-buffer/entries-written: %lu/%lu #P:%d\n", entries, total, num_online_cpus()); seq_puts(m, "#\n"); } -static void print_func_help_header(struct trace_array *tr, struct seq_file *m) +static void print_func_help_header(struct trace_buffer *buf, struct seq_file *m) { - print_event_info(tr, m); + print_event_info(buf, m); seq_puts(m, "# TASK-PID CPU# TIMESTAMP FUNCTION\n"); seq_puts(m, "# | | | | |\n"); } -static void print_func_help_header_irq(struct trace_array *tr, struct seq_file *m) +static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file *m) { - print_event_info(tr, m); + print_event_info(buf, m); seq_puts(m, "# _-----=> irqs-off\n"); seq_puts(m, "# / _----=> need-resched\n"); seq_puts(m, "# | / _---=> hardirq/softirq\n"); @@ -2103,8 +2129,8 @@ void print_trace_header(struct seq_file *m, struct trace_iterator *iter) { unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); - struct trace_array *tr = iter->tr; - struct trace_array_cpu *data = per_cpu_ptr(tr->data, tr->cpu); + struct trace_buffer *buf = iter->trace_buffer; + struct trace_array_cpu *data = per_cpu_ptr(buf->data, buf->cpu); struct tracer *type = iter->trace; unsigned long entries; unsigned long total; @@ -2112,7 +2138,7 @@ print_trace_header(struct seq_file *m, struct trace_iterator *iter) name = type->name; - get_total_entries(tr, &total, &entries); + get_total_entries(buf, &total, &entries); seq_printf(m, "# %s latency trace v1.1.5 on %s\n", name, UTS_RELEASE); @@ -2123,7 +2149,7 @@ print_trace_header(struct seq_file *m, struct trace_iterator *iter) nsecs_to_usecs(data->saved_latency), entries, total, - tr->cpu, + buf->cpu, #if defined(CONFIG_PREEMPT_NONE) "server", #elif defined(CONFIG_PREEMPT_VOLUNTARY) @@ -2174,7 +2200,7 @@ static void test_cpu_buff_start(struct trace_iterator *iter) if (cpumask_test_cpu(iter->cpu, iter->started)) return; - if (per_cpu_ptr(iter->tr->data, iter->cpu)->skipped_entries) + if (per_cpu_ptr(iter->trace_buffer->data, iter->cpu)->skipped_entries) return; cpumask_set_cpu(iter->cpu, iter->started); @@ -2304,7 +2330,7 @@ int trace_empty(struct trace_iterator *iter) if (!ring_buffer_iter_empty(buf_iter)) return 0; } else { - if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu)) + if (!ring_buffer_empty_cpu(iter->trace_buffer->buffer, cpu)) return 0; } return 1; @@ -2316,7 +2342,7 @@ int trace_empty(struct trace_iterator *iter) if (!ring_buffer_iter_empty(buf_iter)) return 0; } else { - if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu)) + if (!ring_buffer_empty_cpu(iter->trace_buffer->buffer, cpu)) return 0; } } @@ -2394,9 +2420,9 @@ void trace_default_header(struct seq_file *m) } else { if (!(trace_flags & TRACE_ITER_VERBOSE)) { if (trace_flags & TRACE_ITER_IRQ_INFO) - print_func_help_header_irq(iter->tr, m); + print_func_help_header_irq(iter->trace_buffer, m); else - print_func_help_header(iter->tr, m); + print_func_help_header(iter->trace_buffer, m); } } } @@ -2515,11 +2541,15 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot) if (!zalloc_cpumask_var(&iter->started, GFP_KERNEL)) goto fail; + iter->tr = tr; + +#ifdef CONFIG_TRACER_MAX_TRACE /* Currently only the top directory has a snapshot */ if (tr->current_trace->print_max || snapshot) - iter->tr = &max_tr; + iter->trace_buffer = &tr->max_buffer; else - iter->tr = tr; +#endif + iter->trace_buffer = &tr->trace_buffer; iter->snapshot = snapshot; iter->pos = -1; mutex_init(&iter->mutex); @@ -2530,7 +2560,7 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot) iter->trace->open(iter); /* Annotate start of buffers if we had overruns */ - if (ring_buffer_overruns(iter->tr->buffer)) + if (ring_buffer_overruns(iter->trace_buffer->buffer)) iter->iter_flags |= TRACE_FILE_ANNOTATE; /* Output in nanoseconds only if we are using a clock in nanoseconds. */ @@ -2544,7 +2574,7 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot) if (iter->cpu_file == RING_BUFFER_ALL_CPUS) { for_each_tracing_cpu(cpu) { iter->buffer_iter[cpu] = - ring_buffer_read_prepare(iter->tr->buffer, cpu); + ring_buffer_read_prepare(iter->trace_buffer->buffer, cpu); } ring_buffer_read_prepare_sync(); for_each_tracing_cpu(cpu) { @@ -2554,7 +2584,7 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot) } else { cpu = iter->cpu_file; iter->buffer_iter[cpu] = - ring_buffer_read_prepare(iter->tr->buffer, cpu); + ring_buffer_read_prepare(iter->trace_buffer->buffer, cpu); ring_buffer_read_prepare_sync(); ring_buffer_read_start(iter->buffer_iter[cpu]); tracing_iter_reset(iter, cpu); @@ -2593,12 +2623,7 @@ static int tracing_release(struct inode *inode, struct file *file) return 0; iter = m->private; - - /* Only the global tracer has a matching max_tr */ - if (iter->tr == &max_tr) - tr = &global_trace; - else - tr = iter->tr; + tr = iter->tr; mutex_lock(&trace_types_lock); for_each_tracing_cpu(cpu) { @@ -2634,9 +2659,9 @@ static int tracing_open(struct inode *inode, struct file *file) struct trace_array *tr = tc->tr; if (tc->cpu == RING_BUFFER_ALL_CPUS) - tracing_reset_online_cpus(tr); + tracing_reset_online_cpus(&tr->trace_buffer); else - tracing_reset(tr, tc->cpu); + tracing_reset(&tr->trace_buffer, tc->cpu); } if (file->f_mode & FMODE_READ) { @@ -2805,13 +2830,13 @@ tracing_cpumask_write(struct file *filp, const char __user *ubuf, */ if (cpumask_test_cpu(cpu, tracing_cpumask) && !cpumask_test_cpu(cpu, tracing_cpumask_new)) { - atomic_inc(&per_cpu_ptr(tr->data, cpu)->disabled); - ring_buffer_record_disable_cpu(tr->buffer, cpu); + atomic_inc(&per_cpu_ptr(tr->trace_buffer.data, cpu)->disabled); + ring_buffer_record_disable_cpu(tr->trace_buffer.buffer, cpu); } if (!cpumask_test_cpu(cpu, tracing_cpumask) && cpumask_test_cpu(cpu, tracing_cpumask_new)) { - atomic_dec(&per_cpu_ptr(tr->data, cpu)->disabled); - ring_buffer_record_enable_cpu(tr->buffer, cpu); + atomic_dec(&per_cpu_ptr(tr->trace_buffer.data, cpu)->disabled); + ring_buffer_record_enable_cpu(tr->trace_buffer.buffer, cpu); } } arch_spin_unlock(&ftrace_max_lock); @@ -2930,9 +2955,9 @@ int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled) trace_event_enable_cmd_record(enabled); if (mask == TRACE_ITER_OVERWRITE) { - ring_buffer_change_overwrite(global_trace.buffer, enabled); + ring_buffer_change_overwrite(tr->trace_buffer.buffer, enabled); #ifdef CONFIG_TRACER_MAX_TRACE - ring_buffer_change_overwrite(max_tr.buffer, enabled); + ring_buffer_change_overwrite(tr->max_buffer.buffer, enabled); #endif } @@ -3116,42 +3141,44 @@ tracing_set_trace_read(struct file *filp, char __user *ubuf, int tracer_init(struct tracer *t, struct trace_array *tr) { - tracing_reset_online_cpus(tr); + tracing_reset_online_cpus(&tr->trace_buffer); return t->init(tr); } -static void set_buffer_entries(struct trace_array *tr, unsigned long val) +static void set_buffer_entries(struct trace_buffer *buf, unsigned long val) { int cpu; for_each_tracing_cpu(cpu) - per_cpu_ptr(tr->data, cpu)->entries = val; + per_cpu_ptr(buf->data, cpu)->entries = val; } +#ifdef CONFIG_TRACER_MAX_TRACE /* resize @tr's buffer to the size of @size_tr's entries */ -static int resize_buffer_duplicate_size(struct trace_array *tr, - struct trace_array *size_tr, int cpu_id) +static int resize_buffer_duplicate_size(struct trace_buffer *trace_buf, + struct trace_buffer *size_buf, int cpu_id) { int cpu, ret = 0; if (cpu_id == RING_BUFFER_ALL_CPUS) { for_each_tracing_cpu(cpu) { - ret = ring_buffer_resize(tr->buffer, - per_cpu_ptr(size_tr->data, cpu)->entries, cpu); + ret = ring_buffer_resize(trace_buf->buffer, + per_cpu_ptr(size_buf->data, cpu)->entries, cpu); if (ret < 0) break; - per_cpu_ptr(tr->data, cpu)->entries = - per_cpu_ptr(size_tr->data, cpu)->entries; + per_cpu_ptr(trace_buf->data, cpu)->entries = + per_cpu_ptr(size_buf->data, cpu)->entries; } } else { - ret = ring_buffer_resize(tr->buffer, - per_cpu_ptr(size_tr->data, cpu_id)->entries, cpu_id); + ret = ring_buffer_resize(trace_buf->buffer, + per_cpu_ptr(size_buf->data, cpu_id)->entries, cpu_id); if (ret == 0) - per_cpu_ptr(tr->data, cpu_id)->entries = - per_cpu_ptr(size_tr->data, cpu_id)->entries; + per_cpu_ptr(trace_buf->data, cpu_id)->entries = + per_cpu_ptr(size_buf->data, cpu_id)->entries; } return ret; } +#endif /* CONFIG_TRACER_MAX_TRACE */ static int __tracing_resize_ring_buffer(struct trace_array *tr, unsigned long size, int cpu) @@ -3166,20 +3193,22 @@ static int __tracing_resize_ring_buffer(struct trace_array *tr, ring_buffer_expanded = 1; /* May be called before buffers are initialized */ - if (!tr->buffer) + if (!tr->trace_buffer.buffer) return 0; - ret = ring_buffer_resize(tr->buffer, size, cpu); + ret = ring_buffer_resize(tr->trace_buffer.buffer, size, cpu); if (ret < 0) return ret; +#ifdef CONFIG_TRACER_MAX_TRACE if (!(tr->flags & TRACE_ARRAY_FL_GLOBAL) || !tr->current_trace->use_max_tr) goto out; - ret = ring_buffer_resize(max_tr.buffer, size, cpu); + ret = ring_buffer_resize(tr->max_buffer.buffer, size, cpu); if (ret < 0) { - int r = resize_buffer_duplicate_size(tr, tr, cpu); + int r = resize_buffer_duplicate_size(&tr->trace_buffer, + &tr->trace_buffer, cpu); if (r < 0) { /* * AARGH! We are left with different @@ -3202,15 +3231,17 @@ static int __tracing_resize_ring_buffer(struct trace_array *tr, } if (cpu == RING_BUFFER_ALL_CPUS) - set_buffer_entries(&max_tr, size); + set_buffer_entries(&tr->max_buffer, size); else - per_cpu_ptr(max_tr.data, cpu)->entries = size; + per_cpu_ptr(tr->max_buffer.data, cpu)->entries = size; out: +#endif /* CONFIG_TRACER_MAX_TRACE */ + if (cpu == RING_BUFFER_ALL_CPUS) - set_buffer_entries(tr, size); + set_buffer_entries(&tr->trace_buffer, size); else - per_cpu_ptr(tr->data, cpu)->entries = size; + per_cpu_ptr(tr->trace_buffer.data, cpu)->entries = size; return ret; } @@ -3277,7 +3308,9 @@ static int tracing_set_tracer(const char *buf) static struct trace_option_dentry *topts; struct trace_array *tr = &global_trace; struct tracer *t; +#ifdef CONFIG_TRACER_MAX_TRACE bool had_max_tr; +#endif int ret = 0; mutex_lock(&trace_types_lock); @@ -3308,7 +3341,10 @@ static int tracing_set_tracer(const char *buf) if (tr->current_trace->reset) tr->current_trace->reset(tr); +#ifdef CONFIG_TRACER_MAX_TRACE had_max_tr = tr->current_trace->allocated_snapshot; + + /* Current trace needs to be nop_trace before synchronize_sched */ tr->current_trace = &nop_trace; if (had_max_tr && !t->use_max_tr) { @@ -3325,22 +3361,28 @@ static int tracing_set_tracer(const char *buf) * The max_tr ring buffer has some state (e.g. ring->clock) and * we want preserve it. */ - ring_buffer_resize(max_tr.buffer, 1, RING_BUFFER_ALL_CPUS); - set_buffer_entries(&max_tr, 1); - tracing_reset_online_cpus(&max_tr); + ring_buffer_resize(tr->max_buffer.buffer, 1, RING_BUFFER_ALL_CPUS); + set_buffer_entries(&tr->max_buffer, 1); + tracing_reset_online_cpus(&tr->max_buffer); tr->current_trace->allocated_snapshot = false; } +#else + tr->current_trace = &nop_trace; +#endif destroy_trace_option_files(topts); topts = create_trace_option_files(tr, t); + +#ifdef CONFIG_TRACER_MAX_TRACE if (t->use_max_tr && !had_max_tr) { /* we need to make per cpu buffer sizes equivalent */ - ret = resize_buffer_duplicate_size(&max_tr, &global_trace, + ret = resize_buffer_duplicate_size(&tr->max_buffer, &tr->trace_buffer, RING_BUFFER_ALL_CPUS); if (ret < 0) goto out; t->allocated_snapshot = true; } +#endif if (t->init) { ret = tracer_init(t, tr); @@ -3468,6 +3510,7 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) iter->cpu_file = tc->cpu; iter->tr = tc->tr; + iter->trace_buffer = &tc->tr->trace_buffer; mutex_init(&iter->mutex); filp->private_data = iter; @@ -3518,7 +3561,7 @@ trace_poll(struct trace_iterator *iter, struct file *filp, poll_table *poll_tabl */ return POLLIN | POLLRDNORM; else - return ring_buffer_poll_wait(iter->tr->buffer, iter->cpu_file, + return ring_buffer_poll_wait(iter->trace_buffer->buffer, iter->cpu_file, filp, poll_table); } @@ -3857,8 +3900,8 @@ tracing_entries_read(struct file *filp, char __user *ubuf, for_each_tracing_cpu(cpu) { /* fill in the size from first enabled cpu */ if (size == 0) - size = per_cpu_ptr(tr->data, cpu)->entries; - if (size != per_cpu_ptr(tr->data, cpu)->entries) { + size = per_cpu_ptr(tr->trace_buffer.data, cpu)->entries; + if (size != per_cpu_ptr(tr->trace_buffer.data, cpu)->entries) { buf_size_same = 0; break; } @@ -3874,7 +3917,7 @@ tracing_entries_read(struct file *filp, char __user *ubuf, } else r = sprintf(buf, "X\n"); } else - r = sprintf(buf, "%lu\n", per_cpu_ptr(tr->data, tc->cpu)->entries >> 10); + r = sprintf(buf, "%lu\n", per_cpu_ptr(tr->trace_buffer.data, tc->cpu)->entries >> 10); mutex_unlock(&trace_types_lock); @@ -3921,7 +3964,7 @@ tracing_total_entries_read(struct file *filp, char __user *ubuf, mutex_lock(&trace_types_lock); for_each_tracing_cpu(cpu) { - size += per_cpu_ptr(tr->data, cpu)->entries >> 10; + size += per_cpu_ptr(tr->trace_buffer.data, cpu)->entries >> 10; if (!ring_buffer_expanded) expanded_size += trace_buf_size >> 10; } @@ -4026,7 +4069,7 @@ tracing_mark_write(struct file *filp, const char __user *ubuf, local_save_flags(irq_flags); size = sizeof(*entry) + cnt + 2; /* possible \n added */ - buffer = global_trace.buffer; + buffer = global_trace.trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, size, irq_flags, preempt_count()); if (!event) { @@ -4111,16 +4154,19 @@ static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf, tr->clock_id = i; - ring_buffer_set_clock(tr->buffer, trace_clocks[i].func); - if (tr->flags & TRACE_ARRAY_FL_GLOBAL && max_tr.buffer) - ring_buffer_set_clock(max_tr.buffer, trace_clocks[i].func); + ring_buffer_set_clock(tr->trace_buffer.buffer, trace_clocks[i].func); /* * New clock may not be consistent with the previous clock. * Reset the buffer so that it doesn't have incomparable timestamps. */ - tracing_reset_online_cpus(&global_trace); - tracing_reset_online_cpus(&max_tr); + tracing_reset_online_cpus(&global_trace.trace_buffer); + +#ifdef CONFIG_TRACER_MAX_TRACE + if (tr->flags & TRACE_ARRAY_FL_GLOBAL && tr->max_buffer.buffer) + ring_buffer_set_clock(tr->max_buffer.buffer, trace_clocks[i].func); + tracing_reset_online_cpus(&global_trace.max_buffer); +#endif mutex_unlock(&trace_types_lock); @@ -4160,6 +4206,7 @@ static int tracing_snapshot_open(struct inode *inode, struct file *file) return -ENOMEM; } iter->tr = tc->tr; + iter->trace_buffer = &tc->tr->max_buffer; m->private = iter; file->private_data = m; } @@ -4196,18 +4243,18 @@ tracing_snapshot_write(struct file *filp, const char __user *ubuf, size_t cnt, case 0: if (tr->current_trace->allocated_snapshot) { /* free spare buffer */ - ring_buffer_resize(max_tr.buffer, 1, + ring_buffer_resize(tr->max_buffer.buffer, 1, RING_BUFFER_ALL_CPUS); - set_buffer_entries(&max_tr, 1); - tracing_reset_online_cpus(&max_tr); + set_buffer_entries(&tr->max_buffer, 1); + tracing_reset_online_cpus(&tr->max_buffer); tr->current_trace->allocated_snapshot = false; } break; case 1: if (!tr->current_trace->allocated_snapshot) { /* allocate spare buffer */ - ret = resize_buffer_duplicate_size(&max_tr, - &global_trace, RING_BUFFER_ALL_CPUS); + ret = resize_buffer_duplicate_size(&tr->max_buffer, + &tr->trace_buffer, RING_BUFFER_ALL_CPUS); if (ret < 0) break; tr->current_trace->allocated_snapshot = true; @@ -4220,7 +4267,7 @@ tracing_snapshot_write(struct file *filp, const char __user *ubuf, size_t cnt, break; default: if (tr->current_trace->allocated_snapshot) - tracing_reset_online_cpus(&max_tr); + tracing_reset_online_cpus(&tr->max_buffer); break; } @@ -4338,6 +4385,7 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp) info->iter.tr = tr; info->iter.cpu_file = tc->cpu; info->iter.trace = tr->current_trace; + info->iter.trace_buffer = &tr->trace_buffer; info->spare = NULL; /* Force reading ring buffer for first read */ info->read = (unsigned int)-1; @@ -4369,7 +4417,8 @@ tracing_buffers_read(struct file *filp, char __user *ubuf, return 0; if (!info->spare) - info->spare = ring_buffer_alloc_read_page(iter->tr->buffer, iter->cpu_file); + info->spare = ring_buffer_alloc_read_page(iter->trace_buffer->buffer, + iter->cpu_file); if (!info->spare) return -ENOMEM; @@ -4379,7 +4428,7 @@ tracing_buffers_read(struct file *filp, char __user *ubuf, again: trace_access_lock(iter->cpu_file); - ret = ring_buffer_read_page(iter->tr->buffer, + ret = ring_buffer_read_page(iter->trace_buffer->buffer, &info->spare, count, iter->cpu_file, 0); @@ -4421,7 +4470,7 @@ static int tracing_buffers_release(struct inode *inode, struct file *file) struct trace_iterator *iter = &info->iter; if (info->spare) - ring_buffer_free_read_page(iter->tr->buffer, info->spare); + ring_buffer_free_read_page(iter->trace_buffer->buffer, info->spare); kfree(info); return 0; @@ -4521,7 +4570,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, again: trace_access_lock(iter->cpu_file); - entries = ring_buffer_entries_cpu(iter->tr->buffer, iter->cpu_file); + entries = ring_buffer_entries_cpu(iter->trace_buffer->buffer, iter->cpu_file); for (i = 0; i < pipe->buffers && len && entries; i++, len -= PAGE_SIZE) { struct page *page; @@ -4532,7 +4581,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, break; ref->ref = 1; - ref->buffer = iter->tr->buffer; + ref->buffer = iter->trace_buffer->buffer; ref->page = ring_buffer_alloc_read_page(ref->buffer, iter->cpu_file); if (!ref->page) { kfree(ref); @@ -4564,7 +4613,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos, spd.nr_pages++; *ppos += PAGE_SIZE; - entries = ring_buffer_entries_cpu(iter->tr->buffer, iter->cpu_file); + entries = ring_buffer_entries_cpu(iter->trace_buffer->buffer, iter->cpu_file); } trace_access_unlock(iter->cpu_file); @@ -4605,6 +4654,7 @@ tracing_stats_read(struct file *filp, char __user *ubuf, { struct trace_cpu *tc = filp->private_data; struct trace_array *tr = tc->tr; + struct trace_buffer *trace_buf = &tr->trace_buffer; struct trace_seq *s; unsigned long cnt; unsigned long long t; @@ -4617,41 +4667,41 @@ tracing_stats_read(struct file *filp, char __user *ubuf, trace_seq_init(s); - cnt = ring_buffer_entries_cpu(tr->buffer, cpu); + cnt = ring_buffer_entries_cpu(trace_buf->buffer, cpu); trace_seq_printf(s, "entries: %ld\n", cnt); - cnt = ring_buffer_overrun_cpu(tr->buffer, cpu); + cnt = ring_buffer_overrun_cpu(trace_buf->buffer, cpu); trace_seq_printf(s, "overrun: %ld\n", cnt); - cnt = ring_buffer_commit_overrun_cpu(tr->buffer, cpu); + cnt = ring_buffer_commit_overrun_cpu(trace_buf->buffer, cpu); trace_seq_printf(s, "commit overrun: %ld\n", cnt); - cnt = ring_buffer_bytes_cpu(tr->buffer, cpu); + cnt = ring_buffer_bytes_cpu(trace_buf->buffer, cpu); trace_seq_printf(s, "bytes: %ld\n", cnt); if (trace_clocks[trace_clock_id].in_ns) { /* local or global for trace_clock */ - t = ns2usecs(ring_buffer_oldest_event_ts(tr->buffer, cpu)); + t = ns2usecs(ring_buffer_oldest_event_ts(trace_buf->buffer, cpu)); usec_rem = do_div(t, USEC_PER_SEC); trace_seq_printf(s, "oldest event ts: %5llu.%06lu\n", t, usec_rem); - t = ns2usecs(ring_buffer_time_stamp(tr->buffer, cpu)); + t = ns2usecs(ring_buffer_time_stamp(trace_buf->buffer, cpu)); usec_rem = do_div(t, USEC_PER_SEC); trace_seq_printf(s, "now ts: %5llu.%06lu\n", t, usec_rem); } else { /* counter or tsc mode for trace_clock */ trace_seq_printf(s, "oldest event ts: %llu\n", - ring_buffer_oldest_event_ts(tr->buffer, cpu)); + ring_buffer_oldest_event_ts(trace_buf->buffer, cpu)); trace_seq_printf(s, "now ts: %llu\n", - ring_buffer_time_stamp(tr->buffer, cpu)); + ring_buffer_time_stamp(trace_buf->buffer, cpu)); } - cnt = ring_buffer_dropped_events_cpu(tr->buffer, cpu); + cnt = ring_buffer_dropped_events_cpu(trace_buf->buffer, cpu); trace_seq_printf(s, "dropped events: %ld\n", cnt); - cnt = ring_buffer_read_events_cpu(tr->buffer, cpu); + cnt = ring_buffer_read_events_cpu(trace_buf->buffer, cpu); trace_seq_printf(s, "read events: %ld\n", cnt); count = simple_read_from_buffer(ubuf, count, ppos, s->buffer, s->len); @@ -4754,7 +4804,7 @@ static struct dentry *tracing_dentry_percpu(struct trace_array *tr, int cpu) static void tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) { - struct trace_array_cpu *data = per_cpu_ptr(tr->data, cpu); + struct trace_array_cpu *data = per_cpu_ptr(tr->trace_buffer.data, cpu); struct dentry *d_percpu = tracing_dentry_percpu(tr, cpu); struct dentry *d_cpu; char cpu_dir[30]; /* 30 characters should be more than enough */ @@ -5038,7 +5088,7 @@ rb_simple_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { struct trace_array *tr = filp->private_data; - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = tr->trace_buffer.buffer; char buf[64]; int r; @@ -5057,7 +5107,7 @@ rb_simple_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { struct trace_array *tr = filp->private_data; - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = tr->trace_buffer.buffer; unsigned long val; int ret; @@ -5129,18 +5179,18 @@ static int new_instance_create(const char *name) rb_flags = trace_flags & TRACE_ITER_OVERWRITE ? RB_FL_OVERWRITE : 0; - tr->buffer = ring_buffer_alloc(trace_buf_size, rb_flags); - if (!tr->buffer) + tr->trace_buffer.buffer = ring_buffer_alloc(trace_buf_size, rb_flags); + if (!tr->trace_buffer.buffer) goto out_free_tr; - tr->data = alloc_percpu(struct trace_array_cpu); - if (!tr->data) + tr->trace_buffer.data = alloc_percpu(struct trace_array_cpu); + if (!tr->trace_buffer.data) goto out_free_tr; for_each_tracing_cpu(i) { - memset(per_cpu_ptr(tr->data, i), 0, sizeof(struct trace_array_cpu)); - per_cpu_ptr(tr->data, i)->trace_cpu.cpu = i; - per_cpu_ptr(tr->data, i)->trace_cpu.tr = tr; + memset(per_cpu_ptr(tr->trace_buffer.data, i), 0, sizeof(struct trace_array_cpu)); + per_cpu_ptr(tr->trace_buffer.data, i)->trace_cpu.cpu = i; + per_cpu_ptr(tr->trace_buffer.data, i)->trace_cpu.tr = tr; } /* Holder for file callbacks */ @@ -5164,8 +5214,8 @@ static int new_instance_create(const char *name) return 0; out_free_tr: - if (tr->buffer) - ring_buffer_free(tr->buffer); + if (tr->trace_buffer.buffer) + ring_buffer_free(tr->trace_buffer.buffer); kfree(tr->name); kfree(tr); @@ -5198,8 +5248,8 @@ static int instance_delete(const char *name) event_trace_del_tracer(tr); debugfs_remove_recursive(tr->dir); - free_percpu(tr->data); - ring_buffer_free(tr->buffer); + free_percpu(tr->trace_buffer.data); + ring_buffer_free(tr->trace_buffer.buffer); kfree(tr->name); kfree(tr); @@ -5439,6 +5489,7 @@ void trace_init_global_iter(struct trace_iterator *iter) iter->tr = &global_trace; iter->trace = iter->tr->current_trace; iter->cpu_file = RING_BUFFER_ALL_CPUS; + iter->trace_buffer = &global_trace.trace_buffer; } static void @@ -5476,7 +5527,7 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode) trace_init_global_iter(&iter); for_each_tracing_cpu(cpu) { - atomic_inc(&per_cpu_ptr(iter.tr->data, cpu)->disabled); + atomic_inc(&per_cpu_ptr(iter.tr->trace_buffer.data, cpu)->disabled); } old_userobj = trace_flags & TRACE_ITER_SYM_USEROBJ; @@ -5544,7 +5595,7 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode) trace_flags |= old_userobj; for_each_tracing_cpu(cpu) { - atomic_dec(&per_cpu_ptr(iter.tr->data, cpu)->disabled); + atomic_dec(&per_cpu_ptr(iter.trace_buffer->data, cpu)->disabled); } tracing_on(); } @@ -5594,58 +5645,59 @@ __init static int tracer_alloc_buffers(void) raw_spin_lock_init(&global_trace.start_lock); /* TODO: make the number of buffers hot pluggable with CPUS */ - global_trace.buffer = ring_buffer_alloc(ring_buf_size, rb_flags); - if (!global_trace.buffer) { + global_trace.trace_buffer.buffer = ring_buffer_alloc(ring_buf_size, rb_flags); + if (!global_trace.trace_buffer.buffer) { printk(KERN_ERR "tracer: failed to allocate ring buffer!\n"); WARN_ON(1); goto out_free_cpumask; } - global_trace.data = alloc_percpu(struct trace_array_cpu); + global_trace.trace_buffer.data = alloc_percpu(struct trace_array_cpu); - if (!global_trace.data) { + if (!global_trace.trace_buffer.data) { printk(KERN_ERR "tracer: failed to allocate percpu memory!\n"); WARN_ON(1); goto out_free_cpumask; } for_each_tracing_cpu(i) { - memset(per_cpu_ptr(global_trace.data, i), 0, sizeof(struct trace_array_cpu)); - per_cpu_ptr(global_trace.data, i)->trace_cpu.cpu = i; - per_cpu_ptr(global_trace.data, i)->trace_cpu.tr = &global_trace; + memset(per_cpu_ptr(global_trace.trace_buffer.data, i), 0, + sizeof(struct trace_array_cpu)); + per_cpu_ptr(global_trace.trace_buffer.data, i)->trace_cpu.cpu = i; + per_cpu_ptr(global_trace.trace_buffer.data, i)->trace_cpu.tr = &global_trace; } if (global_trace.buffer_disabled) tracing_off(); #ifdef CONFIG_TRACER_MAX_TRACE - max_tr.data = alloc_percpu(struct trace_array_cpu); - if (!max_tr.data) { + global_trace.max_buffer.data = alloc_percpu(struct trace_array_cpu); + if (!global_trace.max_buffer.data) { printk(KERN_ERR "tracer: failed to allocate percpu memory!\n"); WARN_ON(1); goto out_free_cpumask; } - max_tr.buffer = ring_buffer_alloc(1, rb_flags); - raw_spin_lock_init(&max_tr.start_lock); - if (!max_tr.buffer) { + global_trace.max_buffer.buffer = ring_buffer_alloc(1, rb_flags); + if (!global_trace.max_buffer.buffer) { printk(KERN_ERR "tracer: failed to allocate max ring buffer!\n"); WARN_ON(1); - ring_buffer_free(global_trace.buffer); + ring_buffer_free(global_trace.trace_buffer.buffer); goto out_free_cpumask; } for_each_tracing_cpu(i) { - memset(per_cpu_ptr(max_tr.data, i), 0, sizeof(struct trace_array_cpu)); - per_cpu_ptr(max_tr.data, i)->trace_cpu.cpu = i; - per_cpu_ptr(max_tr.data, i)->trace_cpu.tr = &max_tr; + memset(per_cpu_ptr(global_trace.max_buffer.data, i), 0, + sizeof(struct trace_array_cpu)); + per_cpu_ptr(global_trace.max_buffer.data, i)->trace_cpu.cpu = i; + per_cpu_ptr(global_trace.max_buffer.data, i)->trace_cpu.tr = &global_trace; } #endif /* Allocate the first page for all buffers */ - set_buffer_entries(&global_trace, - ring_buffer_size(global_trace.buffer, 0)); + set_buffer_entries(&global_trace.trace_buffer, + ring_buffer_size(global_trace.trace_buffer.buffer, 0)); #ifdef CONFIG_TRACER_MAX_TRACE - set_buffer_entries(&max_tr, 1); + set_buffer_entries(&global_trace.max_buffer, 1); #endif trace_init_cmdlines(); @@ -5682,8 +5734,10 @@ __init static int tracer_alloc_buffers(void) return 0; out_free_cpumask: - free_percpu(global_trace.data); - free_percpu(max_tr.data); + free_percpu(global_trace.trace_buffer.data); +#ifdef CONFIG_TRACER_MAX_TRACE + free_percpu(global_trace.max_buffer.data); +#endif free_cpumask_var(tracing_cpumask); out_free_buffer_mask: free_cpumask_var(tracing_buffer_mask); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index fa60b297752..986834f1f4d 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -167,16 +167,37 @@ struct trace_array_cpu { struct tracer; +struct trace_buffer { + struct trace_array *tr; + struct ring_buffer *buffer; + struct trace_array_cpu __percpu *data; + cycle_t time_start; + int cpu; +}; + /* * The trace array - an array of per-CPU trace arrays. This is the * highest level data structure that individual tracers deal with. * They have on/off state as well: */ struct trace_array { - struct ring_buffer *buffer; struct list_head list; char *name; - int cpu; + struct trace_buffer trace_buffer; +#ifdef CONFIG_TRACER_MAX_TRACE + /* + * The max_buffer is used to snapshot the trace when a maximum + * latency is reached, or when the user initiates a snapshot. + * Some tracers will use this to store a maximum trace while + * it continues examining live traces. + * + * The buffers for the max_buffer are set up the same as the trace_buffer + * When a snapshot is taken, the buffer of the max_buffer is swapped + * with the buffer of the trace_buffer and the buffers are reset for + * the trace_buffer so the tracing can continue. + */ + struct trace_buffer max_buffer; +#endif int buffer_disabled; struct trace_cpu trace_cpu; /* place holder */ #ifdef CONFIG_FTRACE_SYSCALLS @@ -189,7 +210,6 @@ struct trace_array { int clock_id; struct tracer *current_trace; unsigned int flags; - cycle_t time_start; raw_spinlock_t start_lock; struct dentry *dir; struct dentry *options; @@ -198,7 +218,6 @@ struct trace_array { struct list_head systems; struct list_head events; struct task_struct *waiter; - struct trace_array_cpu __percpu *data; }; enum { @@ -345,9 +364,11 @@ struct tracer { struct tracer *next; struct tracer_flags *flags; bool print_max; + bool enabled; +#ifdef CONFIG_TRACER_MAX_TRACE bool use_max_tr; bool allocated_snapshot; - bool enabled; +#endif }; @@ -493,8 +514,8 @@ trace_buffer_iter(struct trace_iterator *iter, int cpu) int tracer_init(struct tracer *t, struct trace_array *tr); int tracing_is_enabled(void); -void tracing_reset(struct trace_array *tr, int cpu); -void tracing_reset_online_cpus(struct trace_array *tr); +void tracing_reset(struct trace_buffer *buf, int cpu); +void tracing_reset_online_cpus(struct trace_buffer *buf); void tracing_reset_current(int cpu); void tracing_reset_all_online_cpus(void); int tracing_open_generic(struct inode *inode, struct file *filp); @@ -674,6 +695,8 @@ trace_array_vprintk(struct trace_array *tr, unsigned long ip, const char *fmt, va_list args); int trace_array_printk(struct trace_array *tr, unsigned long ip, const char *fmt, ...); +int trace_array_printk_buf(struct ring_buffer *buffer, + unsigned long ip, const char *fmt, ...); void trace_printk_seq(struct trace_seq *s); enum print_line_t print_trace_line(struct trace_iterator *iter); diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index 9d73861efc6..e467c0c7bdd 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -28,7 +28,7 @@ static void tracing_stop_function_trace(void); static int function_trace_init(struct trace_array *tr) { func_trace = tr; - tr->cpu = get_cpu(); + tr->trace_buffer.cpu = get_cpu(); put_cpu(); tracing_start_cmdline_record(); @@ -44,7 +44,7 @@ static void function_trace_reset(struct trace_array *tr) static void function_trace_start(struct trace_array *tr) { - tracing_reset_online_cpus(tr); + tracing_reset_online_cpus(&tr->trace_buffer); } /* Our option */ @@ -76,7 +76,7 @@ function_trace_call(unsigned long ip, unsigned long parent_ip, goto out; cpu = smp_processor_id(); - data = per_cpu_ptr(tr->data, cpu); + data = per_cpu_ptr(tr->trace_buffer.data, cpu); if (!atomic_read(&data->disabled)) { local_save_flags(flags); trace_function(tr, ip, parent_ip, flags, pc); @@ -107,7 +107,7 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip, */ local_irq_save(flags); cpu = raw_smp_processor_id(); - data = per_cpu_ptr(tr->data, cpu); + data = per_cpu_ptr(tr->trace_buffer.data, cpu); disabled = atomic_inc_return(&data->disabled); if (likely(disabled == 1)) { diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index ca986d61a28..8388bc99f2e 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -218,7 +218,7 @@ int __trace_graph_entry(struct trace_array *tr, { struct ftrace_event_call *call = &event_funcgraph_entry; struct ring_buffer_event *event; - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = tr->trace_buffer.buffer; struct ftrace_graph_ent_entry *entry; if (unlikely(__this_cpu_read(ftrace_cpu_disabled))) @@ -265,7 +265,7 @@ int trace_graph_entry(struct ftrace_graph_ent *trace) local_irq_save(flags); cpu = raw_smp_processor_id(); - data = per_cpu_ptr(tr->data, cpu); + data = per_cpu_ptr(tr->trace_buffer.data, cpu); disabled = atomic_inc_return(&data->disabled); if (likely(disabled == 1)) { pc = preempt_count(); @@ -323,7 +323,7 @@ void __trace_graph_return(struct trace_array *tr, { struct ftrace_event_call *call = &event_funcgraph_exit; struct ring_buffer_event *event; - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = tr->trace_buffer.buffer; struct ftrace_graph_ret_entry *entry; if (unlikely(__this_cpu_read(ftrace_cpu_disabled))) @@ -350,7 +350,7 @@ void trace_graph_return(struct ftrace_graph_ret *trace) local_irq_save(flags); cpu = raw_smp_processor_id(); - data = per_cpu_ptr(tr->data, cpu); + data = per_cpu_ptr(tr->trace_buffer.data, cpu); disabled = atomic_inc_return(&data->disabled); if (likely(disabled == 1)) { pc = preempt_count(); @@ -560,9 +560,9 @@ get_return_for_leaf(struct trace_iterator *iter, * We need to consume the current entry to see * the next one. */ - ring_buffer_consume(iter->tr->buffer, iter->cpu, + ring_buffer_consume(iter->trace_buffer->buffer, iter->cpu, NULL, NULL); - event = ring_buffer_peek(iter->tr->buffer, iter->cpu, + event = ring_buffer_peek(iter->trace_buffer->buffer, iter->cpu, NULL, NULL); } diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 9b52f9cf7a0..5aa40ab72b5 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -121,7 +121,7 @@ static int func_prolog_dec(struct trace_array *tr, if (!irqs_disabled_flags(*flags)) return 0; - *data = per_cpu_ptr(tr->data, cpu); + *data = per_cpu_ptr(tr->trace_buffer.data, cpu); disabled = atomic_inc_return(&(*data)->disabled); if (likely(disabled == 1)) @@ -175,7 +175,7 @@ static int irqsoff_set_flag(u32 old_flags, u32 bit, int set) per_cpu(tracing_cpu, cpu) = 0; tracing_max_latency = 0; - tracing_reset_online_cpus(irqsoff_trace); + tracing_reset_online_cpus(&irqsoff_trace->trace_buffer); return start_irqsoff_tracer(irqsoff_trace, set); } @@ -380,7 +380,7 @@ start_critical_timing(unsigned long ip, unsigned long parent_ip) if (per_cpu(tracing_cpu, cpu)) return; - data = per_cpu_ptr(tr->data, cpu); + data = per_cpu_ptr(tr->trace_buffer.data, cpu); if (unlikely(!data) || atomic_read(&data->disabled)) return; @@ -418,7 +418,7 @@ stop_critical_timing(unsigned long ip, unsigned long parent_ip) if (!tracer_enabled) return; - data = per_cpu_ptr(tr->data, cpu); + data = per_cpu_ptr(tr->trace_buffer.data, cpu); if (unlikely(!data) || !data->critical_start || atomic_read(&data->disabled)) @@ -568,7 +568,7 @@ static void __irqsoff_tracer_init(struct trace_array *tr) irqsoff_trace = tr; /* make sure that the tracer is visible */ smp_wmb(); - tracing_reset_online_cpus(tr); + tracing_reset_online_cpus(&tr->trace_buffer); if (start_irqsoff_tracer(tr, is_graph())) printk(KERN_ERR "failed to start irqsoff tracer\n"); diff --git a/kernel/trace/trace_kdb.c b/kernel/trace/trace_kdb.c index 349f6941e8f..bd90e1b0608 100644 --- a/kernel/trace/trace_kdb.c +++ b/kernel/trace/trace_kdb.c @@ -26,7 +26,7 @@ static void ftrace_dump_buf(int skip_lines, long cpu_file) trace_init_global_iter(&iter); for_each_tracing_cpu(cpu) { - atomic_inc(&per_cpu_ptr(iter.tr->data, cpu)->disabled); + atomic_inc(&per_cpu_ptr(iter.trace_buffer->data, cpu)->disabled); } old_userobj = trace_flags; @@ -46,14 +46,14 @@ static void ftrace_dump_buf(int skip_lines, long cpu_file) if (cpu_file == RING_BUFFER_ALL_CPUS) { for_each_tracing_cpu(cpu) { iter.buffer_iter[cpu] = - ring_buffer_read_prepare(iter.tr->buffer, cpu); + ring_buffer_read_prepare(iter.trace_buffer->buffer, cpu); ring_buffer_read_start(iter.buffer_iter[cpu]); tracing_iter_reset(&iter, cpu); } } else { iter.cpu_file = cpu_file; iter.buffer_iter[cpu_file] = - ring_buffer_read_prepare(iter.tr->buffer, cpu_file); + ring_buffer_read_prepare(iter.trace_buffer->buffer, cpu_file); ring_buffer_read_start(iter.buffer_iter[cpu_file]); tracing_iter_reset(&iter, cpu_file); } @@ -83,7 +83,7 @@ out: trace_flags = old_userobj; for_each_tracing_cpu(cpu) { - atomic_dec(&per_cpu_ptr(iter.tr->data, cpu)->disabled); + atomic_dec(&per_cpu_ptr(iter.trace_buffer->data, cpu)->disabled); } for_each_tracing_cpu(cpu) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index 2472f6f76b5..a5e8f4878bf 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -31,7 +31,7 @@ static void mmio_reset_data(struct trace_array *tr) overrun_detected = false; prev_overruns = 0; - tracing_reset_online_cpus(tr); + tracing_reset_online_cpus(&tr->trace_buffer); } static int mmio_trace_init(struct trace_array *tr) @@ -128,7 +128,7 @@ static void mmio_close(struct trace_iterator *iter) static unsigned long count_overruns(struct trace_iterator *iter) { unsigned long cnt = atomic_xchg(&dropped_count, 0); - unsigned long over = ring_buffer_overruns(iter->tr->buffer); + unsigned long over = ring_buffer_overruns(iter->trace_buffer->buffer); if (over > prev_overruns) cnt += over - prev_overruns; @@ -309,7 +309,7 @@ static void __trace_mmiotrace_rw(struct trace_array *tr, struct mmiotrace_rw *rw) { struct ftrace_event_call *call = &event_mmiotrace_rw; - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = tr->trace_buffer.buffer; struct ring_buffer_event *event; struct trace_mmiotrace_rw *entry; int pc = preempt_count(); @@ -330,7 +330,7 @@ static void __trace_mmiotrace_rw(struct trace_array *tr, void mmio_trace_rw(struct mmiotrace_rw *rw) { struct trace_array *tr = mmio_trace_array; - struct trace_array_cpu *data = per_cpu_ptr(tr->data, smp_processor_id()); + struct trace_array_cpu *data = per_cpu_ptr(tr->trace_buffer.data, smp_processor_id()); __trace_mmiotrace_rw(tr, data, rw); } @@ -339,7 +339,7 @@ static void __trace_mmiotrace_map(struct trace_array *tr, struct mmiotrace_map *map) { struct ftrace_event_call *call = &event_mmiotrace_map; - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = tr->trace_buffer.buffer; struct ring_buffer_event *event; struct trace_mmiotrace_map *entry; int pc = preempt_count(); @@ -363,7 +363,7 @@ void mmio_trace_mapping(struct mmiotrace_map *map) struct trace_array_cpu *data; preempt_disable(); - data = per_cpu_ptr(tr->data, smp_processor_id()); + data = per_cpu_ptr(tr->trace_buffer.data, smp_processor_id()); __trace_mmiotrace_map(tr, data, map); preempt_enable(); } diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index aa92ac322ba..2edc7220d01 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -643,7 +643,7 @@ lat_print_timestamp(struct trace_iterator *iter, u64 next_ts) { unsigned long verbose = trace_flags & TRACE_ITER_VERBOSE; unsigned long in_ns = iter->iter_flags & TRACE_FILE_TIME_IN_NS; - unsigned long long abs_ts = iter->ts - iter->tr->time_start; + unsigned long long abs_ts = iter->ts - iter->trace_buffer->time_start; unsigned long long rel_ts = next_ts - iter->ts; struct trace_seq *s = &iter->seq; diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c index 1ffe39abd6f..4e98e3b257a 100644 --- a/kernel/trace/trace_sched_switch.c +++ b/kernel/trace/trace_sched_switch.c @@ -28,7 +28,7 @@ tracing_sched_switch_trace(struct trace_array *tr, unsigned long flags, int pc) { struct ftrace_event_call *call = &event_context_switch; - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = tr->trace_buffer.buffer; struct ring_buffer_event *event; struct ctx_switch_entry *entry; @@ -69,7 +69,7 @@ probe_sched_switch(void *ignore, struct task_struct *prev, struct task_struct *n pc = preempt_count(); local_irq_save(flags); cpu = raw_smp_processor_id(); - data = per_cpu_ptr(ctx_trace->data, cpu); + data = per_cpu_ptr(ctx_trace->trace_buffer.data, cpu); if (likely(!atomic_read(&data->disabled))) tracing_sched_switch_trace(ctx_trace, prev, next, flags, pc); @@ -86,7 +86,7 @@ tracing_sched_wakeup_trace(struct trace_array *tr, struct ftrace_event_call *call = &event_wakeup; struct ring_buffer_event *event; struct ctx_switch_entry *entry; - struct ring_buffer *buffer = tr->buffer; + struct ring_buffer *buffer = tr->trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, TRACE_WAKE, sizeof(*entry), flags, pc); @@ -123,7 +123,7 @@ probe_sched_wakeup(void *ignore, struct task_struct *wakee, int success) pc = preempt_count(); local_irq_save(flags); cpu = raw_smp_processor_id(); - data = per_cpu_ptr(ctx_trace->data, cpu); + data = per_cpu_ptr(ctx_trace->trace_buffer.data, cpu); if (likely(!atomic_read(&data->disabled))) tracing_sched_wakeup_trace(ctx_trace, wakee, current, diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index f9ceb75a95b..c16f8cd63c3 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c @@ -89,7 +89,7 @@ func_prolog_preempt_disable(struct trace_array *tr, if (cpu != wakeup_current_cpu) goto out_enable; - *data = per_cpu_ptr(tr->data, cpu); + *data = per_cpu_ptr(tr->trace_buffer.data, cpu); disabled = atomic_inc_return(&(*data)->disabled); if (unlikely(disabled != 1)) goto out; @@ -353,7 +353,7 @@ probe_wakeup_sched_switch(void *ignore, /* disable local data, not wakeup_cpu data */ cpu = raw_smp_processor_id(); - disabled = atomic_inc_return(&per_cpu_ptr(wakeup_trace->data, cpu)->disabled); + disabled = atomic_inc_return(&per_cpu_ptr(wakeup_trace->trace_buffer.data, cpu)->disabled); if (likely(disabled != 1)) goto out; @@ -365,7 +365,7 @@ probe_wakeup_sched_switch(void *ignore, goto out_unlock; /* The task we are waiting for is waking up */ - data = per_cpu_ptr(wakeup_trace->data, wakeup_cpu); + data = per_cpu_ptr(wakeup_trace->trace_buffer.data, wakeup_cpu); __trace_function(wakeup_trace, CALLER_ADDR0, CALLER_ADDR1, flags, pc); tracing_sched_switch_trace(wakeup_trace, prev, next, flags, pc); @@ -387,7 +387,7 @@ out_unlock: arch_spin_unlock(&wakeup_lock); local_irq_restore(flags); out: - atomic_dec(&per_cpu_ptr(wakeup_trace->data, cpu)->disabled); + atomic_dec(&per_cpu_ptr(wakeup_trace->trace_buffer.data, cpu)->disabled); } static void __wakeup_reset(struct trace_array *tr) @@ -405,7 +405,7 @@ static void wakeup_reset(struct trace_array *tr) { unsigned long flags; - tracing_reset_online_cpus(tr); + tracing_reset_online_cpus(&tr->trace_buffer); local_irq_save(flags); arch_spin_lock(&wakeup_lock); @@ -435,7 +435,7 @@ probe_wakeup(void *ignore, struct task_struct *p, int success) return; pc = preempt_count(); - disabled = atomic_inc_return(&per_cpu_ptr(wakeup_trace->data, cpu)->disabled); + disabled = atomic_inc_return(&per_cpu_ptr(wakeup_trace->trace_buffer.data, cpu)->disabled); if (unlikely(disabled != 1)) goto out; @@ -458,7 +458,7 @@ probe_wakeup(void *ignore, struct task_struct *p, int success) local_save_flags(flags); - data = per_cpu_ptr(wakeup_trace->data, wakeup_cpu); + data = per_cpu_ptr(wakeup_trace->trace_buffer.data, wakeup_cpu); data->preempt_timestamp = ftrace_now(cpu); tracing_sched_wakeup_trace(wakeup_trace, p, current, flags, pc); @@ -472,7 +472,7 @@ probe_wakeup(void *ignore, struct task_struct *p, int success) out_locked: arch_spin_unlock(&wakeup_lock); out: - atomic_dec(&per_cpu_ptr(wakeup_trace->data, cpu)->disabled); + atomic_dec(&per_cpu_ptr(wakeup_trace->trace_buffer.data, cpu)->disabled); } static void start_wakeup_tracer(struct trace_array *tr) diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 51c819c12c2..8672c40cb15 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -21,13 +21,13 @@ static inline int trace_valid_entry(struct trace_entry *entry) return 0; } -static int trace_test_buffer_cpu(struct trace_array *tr, int cpu) +static int trace_test_buffer_cpu(struct trace_buffer *buf, int cpu) { struct ring_buffer_event *event; struct trace_entry *entry; unsigned int loops = 0; - while ((event = ring_buffer_consume(tr->buffer, cpu, NULL, NULL))) { + while ((event = ring_buffer_consume(buf->buffer, cpu, NULL, NULL))) { entry = ring_buffer_event_data(event); /* @@ -58,7 +58,7 @@ static int trace_test_buffer_cpu(struct trace_array *tr, int cpu) * Test the trace buffer to see if all the elements * are still sane. */ -static int trace_test_buffer(struct trace_array *tr, unsigned long *count) +static int trace_test_buffer(struct trace_buffer *buf, unsigned long *count) { unsigned long flags, cnt = 0; int cpu, ret = 0; @@ -67,7 +67,7 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count) local_irq_save(flags); arch_spin_lock(&ftrace_max_lock); - cnt = ring_buffer_entries(tr->buffer); + cnt = ring_buffer_entries(buf->buffer); /* * The trace_test_buffer_cpu runs a while loop to consume all data. @@ -78,7 +78,7 @@ static int trace_test_buffer(struct trace_array *tr, unsigned long *count) */ tracing_off(); for_each_possible_cpu(cpu) { - ret = trace_test_buffer_cpu(tr, cpu); + ret = trace_test_buffer_cpu(buf, cpu); if (ret) break; } @@ -355,7 +355,7 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace, msleep(100); /* we should have nothing in the buffer */ - ret = trace_test_buffer(tr, &count); + ret = trace_test_buffer(&tr->trace_buffer, &count); if (ret) goto out; @@ -376,7 +376,7 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace, ftrace_enabled = 0; /* check the trace buffer */ - ret = trace_test_buffer(tr, &count); + ret = trace_test_buffer(&tr->trace_buffer, &count); tracing_start(); /* we should only have one item */ @@ -666,7 +666,7 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) ftrace_enabled = 0; /* check the trace buffer */ - ret = trace_test_buffer(tr, &count); + ret = trace_test_buffer(&tr->trace_buffer, &count); trace->reset(tr); tracing_start(); @@ -737,7 +737,7 @@ trace_selftest_startup_function_graph(struct tracer *trace, * Simulate the init() callback but we attach a watchdog callback * to detect and recover from possible hangs */ - tracing_reset_online_cpus(tr); + tracing_reset_online_cpus(&tr->trace_buffer); set_graph_array(tr); ret = register_ftrace_graph(&trace_graph_return, &trace_graph_entry_watchdog); @@ -760,7 +760,7 @@ trace_selftest_startup_function_graph(struct tracer *trace, tracing_stop(); /* check the trace buffer */ - ret = trace_test_buffer(tr, &count); + ret = trace_test_buffer(&tr->trace_buffer, &count); trace->reset(tr); tracing_start(); @@ -815,9 +815,9 @@ trace_selftest_startup_irqsoff(struct tracer *trace, struct trace_array *tr) /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ - ret = trace_test_buffer(tr, NULL); + ret = trace_test_buffer(&tr->trace_buffer, NULL); if (!ret) - ret = trace_test_buffer(&max_tr, &count); + ret = trace_test_buffer(&tr->max_buffer, &count); trace->reset(tr); tracing_start(); @@ -877,9 +877,9 @@ trace_selftest_startup_preemptoff(struct tracer *trace, struct trace_array *tr) /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ - ret = trace_test_buffer(tr, NULL); + ret = trace_test_buffer(&tr->trace_buffer, NULL); if (!ret) - ret = trace_test_buffer(&max_tr, &count); + ret = trace_test_buffer(&tr->max_buffer, &count); trace->reset(tr); tracing_start(); @@ -943,11 +943,11 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array * /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ - ret = trace_test_buffer(tr, NULL); + ret = trace_test_buffer(&tr->trace_buffer, NULL); if (ret) goto out; - ret = trace_test_buffer(&max_tr, &count); + ret = trace_test_buffer(&tr->max_buffer, &count); if (ret) goto out; @@ -973,11 +973,11 @@ trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array * /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ - ret = trace_test_buffer(tr, NULL); + ret = trace_test_buffer(&tr->trace_buffer, NULL); if (ret) goto out; - ret = trace_test_buffer(&max_tr, &count); + ret = trace_test_buffer(&tr->max_buffer, &count); if (!ret && !count) { printk(KERN_CONT ".. no entries found .."); @@ -1084,10 +1084,10 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr) /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ - ret = trace_test_buffer(tr, NULL); + ret = trace_test_buffer(&tr->trace_buffer, NULL); printk("ret = %d\n", ret); if (!ret) - ret = trace_test_buffer(&max_tr, &count); + ret = trace_test_buffer(&tr->max_buffer, &count); trace->reset(tr); @@ -1126,7 +1126,7 @@ trace_selftest_startup_sched_switch(struct tracer *trace, struct trace_array *tr /* stop the tracing. */ tracing_stop(); /* check the trace buffer */ - ret = trace_test_buffer(tr, &count); + ret = trace_test_buffer(&tr->trace_buffer, &count); trace->reset(tr); tracing_start(); diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 1cd37ffb409..68f3f344be6 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -321,7 +321,7 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id) size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; - buffer = tr->buffer; + buffer = tr->trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, sys_data->enter_event->event.type, size, 0, 0); if (!event) @@ -355,7 +355,7 @@ static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret) if (!sys_data) return; - buffer = tr->buffer; + buffer = tr->trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, sys_data->exit_event->event.type, sizeof(*entry), 0, 0); if (!event) -- cgit v1.2.3-70-g09d2 From ad909e21bbe69f1d39055d346540abd827190eca Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 6 Mar 2013 21:45:37 -0500 Subject: tracing: Add internal tracing_snapshot() functions The new snapshot feature is quite handy. It's a way for the user to take advantage of the spare buffer that, until then, only the latency tracers used to "snapshot" the buffer when it hit a max latency. Now users can trigger a "snapshot" manually when some condition is hit in a program. But a snapshot currently can not be triggered by a condition inside the kernel. With the addition of tracing_snapshot() and tracing_snapshot_alloc(), snapshots can now be taking when a condition is hit, and the developer wants to snapshot the case without stopping the trace. Note, any snapshot will overwrite the old one, so take care in how this is done. These new functions are to be used like tracing_on(), tracing_off() and trace_printk() are. That is, they should never be called in the mainline Linux kernel. They are solely for the purpose of debugging. The tracing_snapshot() will not allocate a buffer, but it is safe to be called from any context (except NMIs). But if a snapshot buffer isn't allocated when it is called, it will write to the live buffer, complaining about the lack of a snapshot buffer, and then stop tracing (giving you the "permanent snapshot"). tracing_snapshot_alloc() will allocate the snapshot buffer if it was not already allocated and then take the snapshot. This routine *may sleep*, and must be called from context that can sleep. The allocation is done with GFP_KERNEL and not atomic. If you need a snapshot in an atomic context, say in early boot, then it is best to call the tracing_snapshot_alloc() before then, where it will allocate the buffer, and then you can use the tracing_snapshot() anywhere you want and still get snapshots. Cc: Hiraku Toyooka Cc: Thomas Gleixner Cc: Peter Zijlstra Signed-off-by: Steven Rostedt --- include/linux/kernel.h | 4 +++ kernel/trace/trace.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index c566927efcb..bc5392a326a 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -483,6 +483,8 @@ enum ftrace_dump_mode { void tracing_on(void); void tracing_off(void); int tracing_is_on(void); +void tracing_snapshot(void); +void tracing_snapshot_alloc(void); extern void tracing_start(void); extern void tracing_stop(void); @@ -570,6 +572,8 @@ static inline void trace_dump_stack(void) { } static inline void tracing_on(void) { } static inline void tracing_off(void) { } static inline int tracing_is_on(void) { return 0; } +static inline void tracing_snapshot(void) { } +static inline void tracing_snapshot_alloc(void) { } static inline __printf(1, 2) int trace_printk(const char *fmt, ...) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 3a89496dc99..307524d784e 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -339,6 +339,90 @@ void tracing_on(void) } EXPORT_SYMBOL_GPL(tracing_on); +#ifdef CONFIG_TRACER_SNAPSHOT +/** + * trace_snapshot - take a snapshot of the current buffer. + * + * This causes a swap between the snapshot buffer and the current live + * tracing buffer. You can use this to take snapshots of the live + * trace when some condition is triggered, but continue to trace. + * + * Note, make sure to allocate the snapshot with either + * a tracing_snapshot_alloc(), or by doing it manually + * with: echo 1 > /sys/kernel/debug/tracing/snapshot + * + * If the snapshot buffer is not allocated, it will stop tracing. + * Basically making a permanent snapshot. + */ +void tracing_snapshot(void) +{ + struct trace_array *tr = &global_trace; + struct tracer *tracer = tr->current_trace; + unsigned long flags; + + if (!tr->allocated_snapshot) { + trace_printk("*** SNAPSHOT NOT ALLOCATED ***\n"); + trace_printk("*** stopping trace here! ***\n"); + tracing_off(); + return; + } + + /* Note, snapshot can not be used when the tracer uses it */ + if (tracer->use_max_tr) { + trace_printk("*** LATENCY TRACER ACTIVE ***\n"); + trace_printk("*** Can not use snapshot (sorry) ***\n"); + return; + } + + local_irq_save(flags); + update_max_tr(tr, current, smp_processor_id()); + local_irq_restore(flags); +} + +static int resize_buffer_duplicate_size(struct trace_buffer *trace_buf, + struct trace_buffer *size_buf, int cpu_id); + +/** + * trace_snapshot_alloc - allocate and take a snapshot of the current buffer. + * + * This is similar to trace_snapshot(), but it will allocate the + * snapshot buffer if it isn't already allocated. Use this only + * where it is safe to sleep, as the allocation may sleep. + * + * This causes a swap between the snapshot buffer and the current live + * tracing buffer. You can use this to take snapshots of the live + * trace when some condition is triggered, but continue to trace. + */ +void tracing_snapshot_alloc(void) +{ + struct trace_array *tr = &global_trace; + int ret; + + if (!tr->allocated_snapshot) { + + /* allocate spare buffer */ + ret = resize_buffer_duplicate_size(&tr->max_buffer, + &tr->trace_buffer, RING_BUFFER_ALL_CPUS); + if (WARN_ON(ret < 0)) + return; + + tr->allocated_snapshot = true; + } + + tracing_snapshot(); +} +#else +void tracing_snapshot(void) +{ + WARN_ONCE(1, "Snapshot feature not enabled, but internal snapshot used"); +} +void tracing_snapshot_alloc(void) +{ + /* Give warning */ + tracing_snapshot(); +} +#endif /* CONFIG_TRACER_SNAPSHOT */ + /** * tracing_off - turn off tracing buffers * -- cgit v1.2.3-70-g09d2 From 09ae72348eccb60e304cf8ce94653f4a78fcd407 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 8 Mar 2013 21:02:34 -0500 Subject: tracing: Add trace_puts() for even faster trace_printk() tracing The trace_printk() is extremely fast and is very handy as it can be used in any context (including NMIs!). But it still requires scanning the fmt string for parsing the args. Even the trace_bprintk() requires a scan to know what args will be saved, although it doesn't copy the format string itself. Several times trace_printk() has no args, and wastes cpu cycles scanning the fmt string. Adding trace_puts() allows the developer to use an even faster tracing method that only saves the pointer to the string in the ring buffer without doing any format parsing at all. This will help remove even more of the "Heisenbug" effect, when debugging. Also fixed up the F_printk()s for the ftrace internal bprint and print events. Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Frederic Weisbecker Signed-off-by: Steven Rostedt --- include/linux/kernel.h | 41 +++++++++++++++++++++++- kernel/trace/trace.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ kernel/trace/trace.h | 2 ++ kernel/trace/trace_entries.h | 23 +++++++++++--- kernel/trace/trace_output.c | 75 +++++++++++++++++++++++++++++++++++++++++++ kernel/trace/trace_output.h | 2 ++ 6 files changed, 214 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index bc5392a326a..a3a5574a61f 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -514,7 +514,8 @@ do { \ * * This is intended as a debugging tool for the developer only. * Please refrain from leaving trace_printks scattered around in - * your code. + * your code. (Extra memory is used for special buffers that are + * allocated when trace_printk() is used) */ #define trace_printk(fmt, args...) \ @@ -537,6 +538,44 @@ int __trace_bprintk(unsigned long ip, const char *fmt, ...); extern __printf(2, 3) int __trace_printk(unsigned long ip, const char *fmt, ...); +/** + * trace_puts - write a string into the ftrace buffer + * @str: the string to record + * + * Note: __trace_bputs is an internal function for trace_puts and + * the @ip is passed in via the trace_puts macro. + * + * This is similar to trace_printk() but is made for those really fast + * paths that a developer wants the least amount of "Heisenbug" affects, + * where the processing of the print format is still too much. + * + * This function allows a kernel developer to debug fast path sections + * that printk is not appropriate for. By scattering in various + * printk like tracing in the code, a developer can quickly see + * where problems are occurring. + * + * This is intended as a debugging tool for the developer only. + * Please refrain from leaving trace_puts scattered around in + * your code. (Extra memory is used for special buffers that are + * allocated when trace_puts() is used) + * + * Returns: 0 if nothing was written, positive # if string was. + * (1 when __trace_bputs is used, strlen(str) when __trace_puts is used) + */ + +extern int __trace_bputs(unsigned long ip, const char *str); +extern int __trace_puts(unsigned long ip, const char *str, int size); +#define trace_puts(str) ({ \ + static const char *trace_printk_fmt \ + __attribute__((section("__trace_printk_fmt"))) = \ + __builtin_constant_p(str) ? str : NULL; \ + \ + if (__builtin_constant_p(str)) \ + __trace_bputs(_THIS_IP_, trace_printk_fmt); \ + else \ + __trace_puts(_THIS_IP_, str, strlen(str)); \ +}) + extern void trace_dump_stack(void); /* diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 4021a5e6641..5043a0c4dde 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -350,6 +350,77 @@ void tracing_on(void) } EXPORT_SYMBOL_GPL(tracing_on); +/** + * __trace_puts - write a constant string into the trace buffer. + * @ip: The address of the caller + * @str: The constant string to write + * @size: The size of the string. + */ +int __trace_puts(unsigned long ip, const char *str, int size) +{ + struct ring_buffer_event *event; + struct ring_buffer *buffer; + struct print_entry *entry; + unsigned long irq_flags; + int alloc; + + alloc = sizeof(*entry) + size + 2; /* possible \n added */ + + local_save_flags(irq_flags); + buffer = global_trace.trace_buffer.buffer; + event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, alloc, + irq_flags, preempt_count()); + if (!event) + return 0; + + entry = ring_buffer_event_data(event); + entry->ip = ip; + + memcpy(&entry->buf, str, size); + + /* Add a newline if necessary */ + if (entry->buf[size - 1] != '\n') { + entry->buf[size] = '\n'; + entry->buf[size + 1] = '\0'; + } else + entry->buf[size] = '\0'; + + __buffer_unlock_commit(buffer, event); + + return size; +} +EXPORT_SYMBOL_GPL(__trace_puts); + +/** + * __trace_bputs - write the pointer to a constant string into trace buffer + * @ip: The address of the caller + * @str: The constant string to write to the buffer to + */ +int __trace_bputs(unsigned long ip, const char *str) +{ + struct ring_buffer_event *event; + struct ring_buffer *buffer; + struct bputs_entry *entry; + unsigned long irq_flags; + int size = sizeof(struct bputs_entry); + + local_save_flags(irq_flags); + buffer = global_trace.trace_buffer.buffer; + event = trace_buffer_lock_reserve(buffer, TRACE_BPUTS, size, + irq_flags, preempt_count()); + if (!event) + return 0; + + entry = ring_buffer_event_data(event); + entry->ip = ip; + entry->str = str; + + __buffer_unlock_commit(buffer, event); + + return 1; +} +EXPORT_SYMBOL_GPL(__trace_bputs); + #ifdef CONFIG_TRACER_SNAPSHOT /** * trace_snapshot - take a snapshot of the current buffer. @@ -2475,6 +2546,11 @@ enum print_line_t print_trace_line(struct trace_iterator *iter) return ret; } + if (iter->ent->type == TRACE_BPUTS && + trace_flags & TRACE_ITER_PRINTK && + trace_flags & TRACE_ITER_PRINTK_MSGONLY) + return trace_print_bputs_msg_only(iter); + if (iter->ent->type == TRACE_BPRINT && trace_flags & TRACE_ITER_PRINTK && trace_flags & TRACE_ITER_PRINTK_MSGONLY) diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 26bc7183404..d5764a8532e 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -34,6 +34,7 @@ enum trace_type { TRACE_GRAPH_ENT, TRACE_USER_STACK, TRACE_BLK, + TRACE_BPUTS, __TRACE_LAST_TYPE, }; @@ -277,6 +278,7 @@ extern void __ftrace_bad_type(void); IF_ASSIGN(var, ent, struct userstack_entry, TRACE_USER_STACK);\ IF_ASSIGN(var, ent, struct print_entry, TRACE_PRINT); \ IF_ASSIGN(var, ent, struct bprint_entry, TRACE_BPRINT); \ + IF_ASSIGN(var, ent, struct bputs_entry, TRACE_BPUTS); \ IF_ASSIGN(var, ent, struct trace_mmiotrace_rw, \ TRACE_MMIO_RW); \ IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \ diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index 4108e1250ca..e2d027ac66a 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h @@ -223,8 +223,8 @@ FTRACE_ENTRY(bprint, bprint_entry, __dynamic_array( u32, buf ) ), - F_printk("%08lx fmt:%p", - __entry->ip, __entry->fmt), + F_printk("%pf: %s", + (void *)__entry->ip, __entry->fmt), FILTER_OTHER ); @@ -238,8 +238,23 @@ FTRACE_ENTRY(print, print_entry, __dynamic_array( char, buf ) ), - F_printk("%08lx %s", - __entry->ip, __entry->buf), + F_printk("%pf: %s", + (void *)__entry->ip, __entry->buf), + + FILTER_OTHER +); + +FTRACE_ENTRY(bputs, bputs_entry, + + TRACE_BPUTS, + + F_STRUCT( + __field( unsigned long, ip ) + __field( const char *, str ) + ), + + F_printk("%pf: %s", + (void *)__entry->ip, __entry->str), FILTER_OTHER ); diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 2edc7220d01..19f48e7edc3 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -37,6 +37,22 @@ int trace_print_seq(struct seq_file *m, struct trace_seq *s) return ret; } +enum print_line_t trace_print_bputs_msg_only(struct trace_iterator *iter) +{ + struct trace_seq *s = &iter->seq; + struct trace_entry *entry = iter->ent; + struct bputs_entry *field; + int ret; + + trace_assign_type(field, entry); + + ret = trace_seq_puts(s, field->str); + if (!ret) + return TRACE_TYPE_PARTIAL_LINE; + + return TRACE_TYPE_HANDLED; +} + enum print_line_t trace_print_bprintk_msg_only(struct trace_iterator *iter) { struct trace_seq *s = &iter->seq; @@ -1244,6 +1260,64 @@ static struct trace_event trace_user_stack_event = { .funcs = &trace_user_stack_funcs, }; +/* TRACE_BPUTS */ +static enum print_line_t +trace_bputs_print(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct trace_entry *entry = iter->ent; + struct trace_seq *s = &iter->seq; + struct bputs_entry *field; + + trace_assign_type(field, entry); + + if (!seq_print_ip_sym(s, field->ip, flags)) + goto partial; + + if (!trace_seq_puts(s, ": ")) + goto partial; + + if (!trace_seq_puts(s, field->str)) + goto partial; + + return TRACE_TYPE_HANDLED; + + partial: + return TRACE_TYPE_PARTIAL_LINE; +} + + +static enum print_line_t +trace_bputs_raw(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct bputs_entry *field; + struct trace_seq *s = &iter->seq; + + trace_assign_type(field, iter->ent); + + if (!trace_seq_printf(s, ": %lx : ", field->ip)) + goto partial; + + if (!trace_seq_puts(s, field->str)) + goto partial; + + return TRACE_TYPE_HANDLED; + + partial: + return TRACE_TYPE_PARTIAL_LINE; +} + +static struct trace_event_functions trace_bputs_funcs = { + .trace = trace_bputs_print, + .raw = trace_bputs_raw, +}; + +static struct trace_event trace_bputs_event = { + .type = TRACE_BPUTS, + .funcs = &trace_bputs_funcs, +}; + /* TRACE_BPRINT */ static enum print_line_t trace_bprint_print(struct trace_iterator *iter, int flags, @@ -1356,6 +1430,7 @@ static struct trace_event *events[] __initdata = { &trace_wake_event, &trace_stack_event, &trace_user_stack_event, + &trace_bputs_event, &trace_bprint_event, &trace_print_event, NULL diff --git a/kernel/trace/trace_output.h b/kernel/trace/trace_output.h index c038eba0492..af77870de27 100644 --- a/kernel/trace/trace_output.h +++ b/kernel/trace/trace_output.h @@ -4,6 +4,8 @@ #include #include "trace.h" +extern enum print_line_t +trace_print_bputs_msg_only(struct trace_iterator *iter); extern enum print_line_t trace_print_bprintk_msg_only(struct trace_iterator *iter); extern enum print_line_t -- cgit v1.2.3-70-g09d2 From 9d3c752c062e3266f1051ba0825276ea1e2777da Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 8 Mar 2013 22:11:57 -0500 Subject: tracing: Optimize trace_printk() with one arg to use trace_puts() Although trace_printk() is extremely fast, especially when it uses trace_bprintk() (writes args straight to buffer instead of inserting into string), it still has the overhead of calling one of the printf sprintf() functions, that need to scan the fmt string to determine what, if any args it has. This is a waste of precious CPU cycles if the printk format has no args but a single constant string. It is better to use trace_puts() which does not have the overhead of the fmt scanning. But wouldn't it be nice if the developer didn't have to think about such things, and the compile would just do it for them? trace_printk("this string has no args\n"); [...] trace_printk("this sting does %p %d\n", foo, bar); As tracing is critical to have the least amount of overhead, especially when dealing with race conditions, and you want to eliminate any "Heisenbugs", you want the trace_printk() to use the fastest possible means of tracing. Currently the macro magic determines if it will use trace_bprintk() or if the fmt is a dynamic string (a variable), it will fall back to the slow trace_printk() method that does a full snprintf() before copying it into the buffer, where as trace_bprintk() only copys the pointer to the fmt and the args into the buffer. Well, now there's a way to spend some more Hogwarts cash and come up with new fancy macro magic. #define trace_printk(fmt, ...) \ do { \ char _______STR[] = __stringify((__VA_ARGS__)); \ if (sizeof(_______STR) > 3) \ do_trace_printk(fmt, ##__VA_ARGS__); \ else \ trace_puts(fmt); \ } while (0) The above needs a bit of explaining (both here and in the comments). By stringifying the __VA_ARGS__, we can, at compile time, determine the number of args that are being passed to trace_printk(). The extra parenthesis are required, otherwise the compiler complains about too many parameters for __stringify if there is more than one arg. When there are no args, the __stringify((__VA_ARGS__)) converts into "()\0", a string of 3 characters. Anything else, will be a string containing more than 3 characters. Now we assign that string to a dynamic char array, and then take the sizeof() of that array. If it is greater than 3 characters, we know trace_printk() has args and we need to do the full "do_trace_printk()" on them, otherwise it was only passed a single arg and we can optimize to use trace_puts(). Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Frederic Weisbecker Signed-off-by: Steven "The King of Nasty Macros!" Rostedt --- include/linux/kernel.h | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index a3a5574a61f..d0a16fe03fe 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -516,9 +516,30 @@ do { \ * Please refrain from leaving trace_printks scattered around in * your code. (Extra memory is used for special buffers that are * allocated when trace_printk() is used) + * + * A little optization trick is done here. If there's only one + * argument, there's no need to scan the string for printf formats. + * The trace_puts() will suffice. But how can we take advantage of + * using trace_puts() when trace_printk() has only one argument? + * By stringifying the args and checking the size we can tell + * whether or not there are args. __stringify((__VA_ARGS__)) will + * turn into "()\0" with a size of 3 when there are no args, anything + * else will be bigger. All we need to do is define a string to this, + * and then take its size and compare to 3. If it's bigger, use + * do_trace_printk() otherwise, optimize it to trace_puts(). Then just + * let gcc optimize the rest. */ -#define trace_printk(fmt, args...) \ +#define trace_printk(fmt, ...) \ +do { \ + char _______STR[] = __stringify((__VA_ARGS__)); \ + if (sizeof(_______STR) > 3) \ + do_trace_printk(fmt, ##__VA_ARGS__); \ + else \ + trace_puts(fmt); \ +} while (0) + +#define do_trace_printk(fmt, args...) \ do { \ static const char *trace_printk_fmt \ __attribute__((section("__trace_printk_fmt"))) = \ -- cgit v1.2.3-70-g09d2 From 57d01ad09721fb7719c4c8c72b434398186f35a0 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 12 Mar 2013 12:38:06 -0400 Subject: tracing: Fix comments for ftrace_event_file/call flags Most of the flags for the struct ftrace_event_file were moved over to the flags of the struct ftrace_event_call, but the comments were never updated. Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index d84c4a57551..4cb6cd8338a 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -230,6 +230,13 @@ struct ftrace_event_call { struct list_head *files; void *mod; void *data; + /* + * bit 0: filter_active + * bit 1: allow trace by non root (cap any) + * bit 2: failed to apply filter + * bit 3: ftrace internal event (do not enable) + * bit 4: Event was enabled by module + */ int flags; /* static flags of different events */ #ifdef CONFIG_PERF_EVENTS @@ -248,7 +255,7 @@ enum { /* * Ftrace event file flags: - * ENABELD - The event is enabled + * ENABLED - The event is enabled * RECORDED_CMD - The comms should be recorded at sched_switch */ enum { @@ -265,12 +272,8 @@ struct ftrace_event_file { /* * 32 bit flags: - * bit 1: enabled - * bit 2: filter_active - * bit 3: enabled cmd record - * bit 4: allow trace by non root (cap any) - * bit 5: failed to apply filter - * bit 6: ftrace internal event (do not enable) + * bit 0: enabled + * bit 1: enabled cmd record * * Changes to flags must hold the event_mutex. * -- cgit v1.2.3-70-g09d2 From e67efb93f0e9130174293ffaa5975f87b301b531 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 12 Mar 2013 15:07:59 -0400 Subject: ftrace: Clean up function probe methods When a function probe is created, each function that the probe is attached to, a "callback" method is called. On release of the probe, each function entry calls the "free" method. First, "callback" is a confusing name and does not really match what it does. Callback sounds like it will be called when the probe triggers. But that's not the case. This is really an "init" function, so lets rename it as such. Secondly, both "init" and "free" do not pass enough information back to the handlers. Pass back the ops, ip and data for each time the method is called. We have the information, might as well use it. Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 6 ++++-- kernel/trace/ftrace.c | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index e5ca8ef50e9..832422d706f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -259,8 +259,10 @@ struct ftrace_probe_ops { void (*func)(unsigned long ip, unsigned long parent_ip, void **data); - int (*callback)(unsigned long ip, void **data); - void (*free)(void **data); + int (*init)(struct ftrace_probe_ops *ops, + unsigned long ip, void **data); + void (*free)(struct ftrace_probe_ops *ops, + unsigned long ip, void **data); int (*print)(struct seq_file *m, unsigned long ip, struct ftrace_probe_ops *ops, diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index dab031fec85..ff0ef41c6d9 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -2984,7 +2984,7 @@ static void ftrace_free_entry_rcu(struct rcu_head *rhp) container_of(rhp, struct ftrace_func_probe, rcu); if (entry->ops->free) - entry->ops->free(&entry->data); + entry->ops->free(entry->ops, entry->ip, &entry->data); kfree(entry); } @@ -3045,8 +3045,8 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, * for each function we find. We call the callback * to give the caller an opportunity to do so. */ - if (ops->callback) { - if (ops->callback(rec->ip, &entry->data) < 0) { + if (ops->init) { + if (ops->init(ops, rec->ip, &entry->data) < 0) { /* caller does not like this func */ kfree(entry); continue; -- cgit v1.2.3-70-g09d2 From 417944c4c7a0f657158d0515f3b8e8c043fd788f Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 12 Mar 2013 13:26:18 -0400 Subject: tracing: Add a way to soft disable trace events In order to let triggers enable or disable events, we need a 'soft' method for doing so. For example, if a function probe is added that lets a user enable or disable events when a function is called, that change must be done without taking locks or a mutex, and definitely it can't sleep. But the full enabling of a tracepoint is expensive. By adding a 'SOFT_DISABLE' flag, and converting the flags to be updated without the protection of a mutex (using set/clear_bit()), this soft disable flag can be used to allow critical sections to enable or disable events from being traced (after the event has been placed into "SOFT_MODE"). Some caveats though: The comm recorder (to map pids with a comm) can not be soft disabled (yet). If you disable an event with with a "soft" disable and wait a while before reading the trace, the comm cache may be replaced and you'll get a bunch of <...> for comms in the trace. Reading the "enable" file for an event that is disabled will now give you "0*" where the '*' denotes that the tracepoint is still active but the event itself is "disabled". [ fixed _BIT used in & operation : thanks to Dan Carpenter and smatch ] Cc: Dan Carpenter Cc: Tom Zanussi Signed-off-by: Steven Rostedt --- include/linux/ftrace_event.h | 20 ++++++++---- include/trace/ftrace.h | 8 +++++ kernel/trace/trace_events.c | 75 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 84 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 4cb6cd8338a..4e28b011e63 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -251,16 +251,23 @@ struct ftrace_subsystem_dir; enum { FTRACE_EVENT_FL_ENABLED_BIT, FTRACE_EVENT_FL_RECORDED_CMD_BIT, + FTRACE_EVENT_FL_SOFT_MODE_BIT, + FTRACE_EVENT_FL_SOFT_DISABLED_BIT, }; /* * Ftrace event file flags: * ENABLED - The event is enabled * RECORDED_CMD - The comms should be recorded at sched_switch + * SOFT_MODE - The event is enabled/disabled by SOFT_DISABLED + * SOFT_DISABLED - When set, do not trace the event (even though its + * tracepoint may be enabled) */ enum { FTRACE_EVENT_FL_ENABLED = (1 << FTRACE_EVENT_FL_ENABLED_BIT), FTRACE_EVENT_FL_RECORDED_CMD = (1 << FTRACE_EVENT_FL_RECORDED_CMD_BIT), + FTRACE_EVENT_FL_SOFT_MODE = (1 << FTRACE_EVENT_FL_SOFT_MODE_BIT), + FTRACE_EVENT_FL_SOFT_DISABLED = (1 << FTRACE_EVENT_FL_SOFT_DISABLED_BIT), }; struct ftrace_event_file { @@ -274,17 +281,18 @@ struct ftrace_event_file { * 32 bit flags: * bit 0: enabled * bit 1: enabled cmd record + * bit 2: enable/disable with the soft disable bit + * bit 3: soft disabled * - * Changes to flags must hold the event_mutex. - * - * Note: Reads of flags do not hold the event_mutex since - * they occur in critical sections. But the way flags + * Note: The bits must be set atomically to prevent races + * from other writers. Reads of flags do not need to be in + * sync as they occur in critical sections. But the way flags * is currently used, these changes do not affect the code * except that when a change is made, it may have a slight * delay in propagating the changes to other CPUs due to - * caching and such. + * caching and such. Which is mostly OK ;-) */ - unsigned int flags; + unsigned long flags; }; #define __TRACE_EVENT_FLAGS(name, value) \ diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index bbf09c2021b..4bda044e6c7 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -413,6 +413,10 @@ static inline notrace int ftrace_get_offsets_##call( \ * int __data_size; * int pc; * + * if (test_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, + * &ftrace_file->flags)) + * return; + * * local_save_flags(irq_flags); * pc = preempt_count(); * @@ -518,6 +522,10 @@ ftrace_raw_event_##call(void *__data, proto) \ int __data_size; \ int pc; \ \ + if (test_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, \ + &ftrace_file->flags)) \ + return; \ + \ local_save_flags(irq_flags); \ pc = preempt_count(); \ \ diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 38b54c5edeb..106640b0df4 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -205,37 +205,77 @@ void trace_event_enable_cmd_record(bool enable) if (enable) { tracing_start_cmdline_record(); - file->flags |= FTRACE_EVENT_FL_RECORDED_CMD; + set_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags); } else { tracing_stop_cmdline_record(); - file->flags &= ~FTRACE_EVENT_FL_RECORDED_CMD; + clear_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags); } } while_for_each_event_file(); mutex_unlock(&event_mutex); } -static int ftrace_event_enable_disable(struct ftrace_event_file *file, - int enable) +static int __ftrace_event_enable_disable(struct ftrace_event_file *file, + int enable, int soft_disable) { struct ftrace_event_call *call = file->event_call; int ret = 0; + int disable; switch (enable) { case 0: - if (file->flags & FTRACE_EVENT_FL_ENABLED) { - file->flags &= ~FTRACE_EVENT_FL_ENABLED; + /* + * When soft_disable is set and enable is cleared, we want + * to clear the SOFT_DISABLED flag but leave the event in the + * state that it was. That is, if the event was enabled and + * SOFT_DISABLED isn't set, then do nothing. But if SOFT_DISABLED + * is set we do not want the event to be enabled before we + * clear the bit. + * + * When soft_disable is not set but the SOFT_MODE flag is, + * we do nothing. Do not disable the tracepoint, otherwise + * "soft enable"s (clearing the SOFT_DISABLED bit) wont work. + */ + if (soft_disable) { + disable = file->flags & FTRACE_EVENT_FL_SOFT_DISABLED; + clear_bit(FTRACE_EVENT_FL_SOFT_MODE_BIT, &file->flags); + } else + disable = !(file->flags & FTRACE_EVENT_FL_SOFT_MODE); + + if (disable && (file->flags & FTRACE_EVENT_FL_ENABLED)) { + clear_bit(FTRACE_EVENT_FL_ENABLED_BIT, &file->flags); if (file->flags & FTRACE_EVENT_FL_RECORDED_CMD) { tracing_stop_cmdline_record(); - file->flags &= ~FTRACE_EVENT_FL_RECORDED_CMD; + clear_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags); } call->class->reg(call, TRACE_REG_UNREGISTER, file); } + /* If in SOFT_MODE, just set the SOFT_DISABLE_BIT */ + if (file->flags & FTRACE_EVENT_FL_SOFT_MODE) + set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags); break; case 1: + /* + * When soft_disable is set and enable is set, we want to + * register the tracepoint for the event, but leave the event + * as is. That means, if the event was already enabled, we do + * nothing (but set SOFT_MODE). If the event is disabled, we + * set SOFT_DISABLED before enabling the event tracepoint, so + * it still seems to be disabled. + */ + if (!soft_disable) + clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags); + else + set_bit(FTRACE_EVENT_FL_SOFT_MODE_BIT, &file->flags); + if (!(file->flags & FTRACE_EVENT_FL_ENABLED)) { + + /* Keep the event disabled, when going to SOFT_MODE. */ + if (soft_disable) + set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags); + if (trace_flags & TRACE_ITER_RECORD_CMD) { tracing_start_cmdline_record(); - file->flags |= FTRACE_EVENT_FL_RECORDED_CMD; + set_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags); } ret = call->class->reg(call, TRACE_REG_REGISTER, file); if (ret) { @@ -244,7 +284,7 @@ static int ftrace_event_enable_disable(struct ftrace_event_file *file, "%s\n", call->name); break; } - file->flags |= FTRACE_EVENT_FL_ENABLED; + set_bit(FTRACE_EVENT_FL_ENABLED_BIT, &file->flags); /* WAS_ENABLED gets set but never cleared. */ call->flags |= TRACE_EVENT_FL_WAS_ENABLED; @@ -255,6 +295,12 @@ static int ftrace_event_enable_disable(struct ftrace_event_file *file, return ret; } +static int ftrace_event_enable_disable(struct ftrace_event_file *file, + int enable) +{ + return __ftrace_event_enable_disable(file, enable, 0); +} + static void ftrace_clear_events(struct trace_array *tr) { struct ftrace_event_file *file; @@ -547,12 +593,15 @@ event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, struct ftrace_event_file *file = filp->private_data; char *buf; - if (file->flags & FTRACE_EVENT_FL_ENABLED) - buf = "1\n"; - else + if (file->flags & FTRACE_EVENT_FL_ENABLED) { + if (file->flags & FTRACE_EVENT_FL_SOFT_DISABLED) + buf = "0*\n"; + else + buf = "1\n"; + } else buf = "0\n"; - return simple_read_from_buffer(ubuf, cnt, ppos, buf, 2); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, strlen(buf)); } static ssize_t -- cgit v1.2.3-70-g09d2 From c142be8ebe0b7bf73c8a0063925623f3e4b980c0 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 13 Mar 2013 09:55:57 -0400 Subject: tracing: Add skip argument to trace_dump_stack() Altough the trace_dump_stack() already skips three functions in the call to stack trace, which gets the stack trace to start at the caller of the function, the caller may want to skip some more too (as it may have helper functions). Add a skip argument to the trace_dump_stack() that lets the caller skip back tracing functions that it doesn't care about. Signed-off-by: Steven Rostedt --- include/linux/kernel.h | 2 +- kernel/trace/trace.c | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index d0a16fe03fe..239dbb9627c 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -597,7 +597,7 @@ extern int __trace_puts(unsigned long ip, const char *str, int size); __trace_puts(_THIS_IP_, str, strlen(str)); \ }) -extern void trace_dump_stack(void); +extern void trace_dump_stack(int skip); /* * The double __builtin_constant_p is because gcc will give us an error diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c5b84462156..8aa53213201 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1657,8 +1657,9 @@ void __trace_stack(struct trace_array *tr, unsigned long flags, int skip, /** * trace_dump_stack - record a stack back trace in the trace buffer + * @skip: Number of functions to skip (helper handlers) */ -void trace_dump_stack(void) +void trace_dump_stack(int skip) { unsigned long flags; @@ -1667,9 +1668,13 @@ void trace_dump_stack(void) local_save_flags(flags); - /* skipping 3 traces, seems to get us at the caller of this function */ - __ftrace_trace_stack(global_trace.trace_buffer.buffer, flags, 3, - preempt_count(), NULL); + /* + * Skip 3 more, seems to get us at the caller of + * this function. + */ + skip += 3; + __ftrace_trace_stack(global_trace.trace_buffer.buffer, + flags, skip, preempt_count(), NULL); } static DEFINE_PER_CPU(int, user_stack_count); -- cgit v1.2.3-70-g09d2 From 8aacf017b065a805d27467843490c976835eb4a5 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 14 Mar 2013 13:13:45 -0400 Subject: tracing: Add "uptime" trace clock that uses jiffies Add a simple trace clock called "uptime" for those that are interested in the uptime of the trace. It uses jiffies as that's the safest method, as other uptime clocks grab seq locks, which could cause a deadlock if taken from an event or function tracer. Requested-by: Mauro Carvalho Chehab Cc: Thomas Gleixner Cc: Frederic Weisbecker Signed-off-by: Steven Rostedt --- include/linux/trace_clock.h | 1 + kernel/trace/trace.c | 1 + kernel/trace/trace_clock.c | 10 ++++++++++ 3 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/linux/trace_clock.h b/include/linux/trace_clock.h index d563f37e1a1..1d7ca273927 100644 --- a/include/linux/trace_clock.h +++ b/include/linux/trace_clock.h @@ -16,6 +16,7 @@ extern u64 notrace trace_clock_local(void); extern u64 notrace trace_clock(void); +extern u64 notrace trace_clock_jiffies(void); extern u64 notrace trace_clock_global(void); extern u64 notrace trace_clock_counter(void); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index f90ca16afcf..8eabfbb8003 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -647,6 +647,7 @@ static struct { { trace_clock_local, "local", 1 }, { trace_clock_global, "global", 1 }, { trace_clock_counter, "counter", 0 }, + { trace_clock_jiffies, "uptime", 1 }, ARCH_TRACE_CLOCKS }; diff --git a/kernel/trace/trace_clock.c b/kernel/trace/trace_clock.c index aa8f5f48dae..26dc348332b 100644 --- a/kernel/trace/trace_clock.c +++ b/kernel/trace/trace_clock.c @@ -57,6 +57,16 @@ u64 notrace trace_clock(void) return local_clock(); } +/* + * trace_jiffy_clock(): Simply use jiffies as a clock counter. + */ +u64 notrace trace_clock_jiffies(void) +{ + u64 jiffy = jiffies - INITIAL_JIFFIES; + + /* Return nsecs */ + return (u64)jiffies_to_usecs(jiffy) * 1000ULL; +} /* * trace_clock_global(): special globally coherent trace clock -- cgit v1.2.3-70-g09d2 From b92021b09df70c1609e3547f3d6128dd560be97f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 15 Mar 2013 15:04:17 +1030 Subject: CONFIG_SYMBOL_PREFIX: cleanup. We have CONFIG_SYMBOL_PREFIX, which three archs define to the string "_". But Al Viro broke this in "consolidate cond_syscall and SYSCALL_ALIAS declarations" (in linux-next), and he's not the first to do so. Using CONFIG_SYMBOL_PREFIX is awkward, since we usually just want to prefix it so something. So various places define helpers which are defined to nothing if CONFIG_SYMBOL_PREFIX isn't set: 1) include/asm-generic/unistd.h defines __SYMBOL_PREFIX. 2) include/asm-generic/vmlinux.lds.h defines VMLINUX_SYMBOL(sym) 3) include/linux/export.h defines MODULE_SYMBOL_PREFIX. 4) include/linux/kernel.h defines SYMBOL_PREFIX (which differs from #7) 5) kernel/modsign_certificate.S defines ASM_SYMBOL(sym) 6) scripts/modpost.c defines MODULE_SYMBOL_PREFIX 7) scripts/Makefile.lib defines SYMBOL_PREFIX on the commandline if CONFIG_SYMBOL_PREFIX is set, so that we have a non-string version for pasting. (arch/h8300/include/asm/linkage.h defines SYMBOL_NAME(), too). Let's solve this properly: 1) No more generic prefix, just CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX. 2) Make linux/export.h usable from asm. 3) Define VMLINUX_SYMBOL() and VMLINUX_SYMBOL_STR(). 4) Make everyone use them. Signed-off-by: Rusty Russell Reviewed-by: James Hogan Tested-by: James Hogan (metag) --- Makefile | 2 +- arch/Kconfig | 6 ++++++ arch/blackfin/Kconfig | 5 +---- arch/h8300/Kconfig | 5 +---- arch/metag/Kconfig | 5 +---- drivers/mtd/chips/gen_probe.c | 8 +++++--- include/asm-generic/unistd.h | 12 ++++-------- include/asm-generic/vmlinux.lds.h | 8 +------- include/linux/export.h | 20 ++++++++++++++------ include/linux/kernel.h | 7 ------- include/linux/module.h | 4 ++-- kernel/modsign_certificate.S | 13 +++---------- kernel/module.c | 2 +- scripts/Makefile.lib | 7 ------- scripts/link-vmlinux.sh | 5 ++--- scripts/mod/modpost.c | 36 +++++++++++++++--------------------- 16 files changed, 57 insertions(+), 88 deletions(-) (limited to 'include') diff --git a/Makefile b/Makefile index a05ea42c5f1..0b09ba5e492 100644 --- a/Makefile +++ b/Makefile @@ -1398,7 +1398,7 @@ quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files)) # Run depmod only if we have System.map and depmod is executable quiet_cmd_depmod = DEPMOD $(KERNELRELEASE) cmd_depmod = $(CONFIG_SHELL) $(srctree)/scripts/depmod.sh $(DEPMOD) \ - $(KERNELRELEASE) "$(patsubst "%",%,$(CONFIG_SYMBOL_PREFIX))" + $(KERNELRELEASE) "$(patsubst y,_,$(CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX))" # Create temporary dir for module support files # clean it up only when building all modules diff --git a/arch/Kconfig b/arch/Kconfig index 1455579791e..7b433a4bcc2 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -384,6 +384,12 @@ config MODULES_USE_ELF_REL Modules only use ELF REL relocations. Modules with ELF RELA relocations will give an error. +config HAVE_UNDERSCORE_SYMBOL_PREFIX + bool + help + Some architectures generate an _ in front of C symbols; things like + module loading and assembly files need to know about this. + # # ABI hall of shame # diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index c3f2e0bc644..453ebe46b06 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -1,7 +1,3 @@ -config SYMBOL_PREFIX - string - default "_" - config MMU def_bool n @@ -33,6 +29,7 @@ config BLACKFIN select ARCH_HAVE_CUSTOM_GPIO_H select ARCH_WANT_OPTIONAL_GPIOLIB select HAVE_UID16 + select HAVE_UNDERSCORE_SYMBOL_PREFIX select VIRT_TO_BUS select ARCH_WANT_IPC_PARSE_VERSION select HAVE_GENERIC_HARDIRQS diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 79250de1b12..303e4f9a79d 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -12,10 +12,7 @@ config H8300 select MODULES_USE_ELF_RELA select OLD_SIGSUSPEND3 select OLD_SIGACTION - -config SYMBOL_PREFIX - string - default "_" + select HAVE_UNDERSCORE_SYMBOL_PREFIX config MMU bool diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig index afc8973d148..2099617e3ec 100644 --- a/arch/metag/Kconfig +++ b/arch/metag/Kconfig @@ -1,7 +1,3 @@ -config SYMBOL_PREFIX - string - default "_" - config METAG def_bool y select EMBEDDED @@ -27,6 +23,7 @@ config METAG select HAVE_MOD_ARCH_SPECIFIC select HAVE_PERF_EVENTS select HAVE_SYSCALL_TRACEPOINTS + select HAVE_UNDERSCORE_SYMBOL_PREFIX select IRQ_DOMAIN select MODULES_USE_ELF_RELA select OF diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index 3b9a2843c5f..74dbb6bcf48 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -204,14 +204,16 @@ static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, struct cfi_private *cfi = map->fldrv_priv; __u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID; #ifdef CONFIG_MODULES - char probename[16+sizeof(MODULE_SYMBOL_PREFIX)]; + char probename[sizeof(VMLINUX_SYMBOL_STR(cfi_cmdset_%4.4X))]; cfi_cmdset_fn_t *probe_function; - sprintf(probename, MODULE_SYMBOL_PREFIX "cfi_cmdset_%4.4X", type); + sprintf(probename, VMLINUX_SYMBOL_STR(cfi_cmdset_%4.4X), type); probe_function = __symbol_get(probename); if (!probe_function) { - request_module(probename + sizeof(MODULE_SYMBOL_PREFIX) - 1); + char modname[sizeof("cfi_cmdset_%4.4X")]; + sprintf(modname, "cfi_cmdset_%4.4X", type); + request_module(modname); probe_function = __symbol_get(probename); } diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h index 4077b5d9ff8..15c0598e110 100644 --- a/include/asm-generic/unistd.h +++ b/include/asm-generic/unistd.h @@ -1,4 +1,5 @@ #include +#include /* * These are required system calls, we should @@ -17,12 +18,7 @@ * but it doesn't work on all toolchains, so we just do it by hand */ #ifndef cond_syscall -#ifdef CONFIG_SYMBOL_PREFIX -#define __SYMBOL_PREFIX CONFIG_SYMBOL_PREFIX -#else -#define __SYMBOL_PREFIX -#endif -#define cond_syscall(x) asm(".weak\t" __SYMBOL_PREFIX #x "\n\t" \ - ".set\t" __SYMBOL_PREFIX #x "," \ - __SYMBOL_PREFIX "sys_ni_syscall") +#define cond_syscall(x) asm(".weak\t" VMLINUX_SYMBOL_STR(x) "\n\t" \ + ".set\t" VMLINUX_SYMBOL_STR(x) "," \ + VMLINUX_SYMBOL_STR(sys_ni_syscall)) #endif diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index afa12c7a025..eb58d2d7d97 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -52,13 +52,7 @@ #define LOAD_OFFSET 0 #endif -#ifndef SYMBOL_PREFIX -#define VMLINUX_SYMBOL(sym) sym -#else -#define PASTE2(x,y) x##y -#define PASTE(x,y) PASTE2(x,y) -#define VMLINUX_SYMBOL(sym) PASTE(SYMBOL_PREFIX, sym) -#endif +#include /* Align . to a 8 byte boundary equals to maximum function alignment. */ #define ALIGN_FUNCTION() . = ALIGN(8) diff --git a/include/linux/export.h b/include/linux/export.h index 696c0f48afc..412cd509eff 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -5,17 +5,24 @@ * to reduce the amount of pointless cruft we feed to gcc when only * exporting a simple symbol or two. * - * If you feel the need to add #include to this file - * then you are doing something wrong and should go away silently. + * Try not to add #includes here. It slows compilation and makes kernel + * hackers place grumpy comments in header files. */ /* Some toolchains use a `_' prefix for all user symbols. */ -#ifdef CONFIG_SYMBOL_PREFIX -#define MODULE_SYMBOL_PREFIX CONFIG_SYMBOL_PREFIX +#ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX +#define __VMLINUX_SYMBOL(x) _##x +#define __VMLINUX_SYMBOL_STR(x) "_" #x #else -#define MODULE_SYMBOL_PREFIX "" +#define __VMLINUX_SYMBOL(x) x +#define __VMLINUX_SYMBOL_STR(x) #x #endif +/* Indirect, so macros are expanded before pasting. */ +#define VMLINUX_SYMBOL(x) __VMLINUX_SYMBOL(x) +#define VMLINUX_SYMBOL_STR(x) __VMLINUX_SYMBOL_STR(x) + +#ifndef __ASSEMBLY__ struct kernel_symbol { unsigned long value; @@ -51,7 +58,7 @@ extern struct module __this_module; __CRC_SYMBOL(sym, sec) \ static const char __kstrtab_##sym[] \ __attribute__((section("__ksymtab_strings"), aligned(1))) \ - = MODULE_SYMBOL_PREFIX #sym; \ + = VMLINUX_SYMBOL_STR(sym); \ static const struct kernel_symbol __ksymtab_##sym \ __used \ __attribute__((section("___ksymtab" sec "+" #sym), unused)) \ @@ -85,5 +92,6 @@ extern struct module __this_module; #define EXPORT_UNUSED_SYMBOL_GPL(sym) #endif /* CONFIG_MODULES */ +#endif /* !__ASSEMBLY__ */ #endif /* _LINUX_EXPORT_H */ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 80d36874689..e13e992eae8 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -723,13 +723,6 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } /* Trap pasters of __FUNCTION__ at compile-time */ #define __FUNCTION__ (__func__) -/* This helps us to avoid #ifdef CONFIG_SYMBOL_PREFIX */ -#ifdef CONFIG_SYMBOL_PREFIX -#define SYMBOL_PREFIX CONFIG_SYMBOL_PREFIX -#else -#define SYMBOL_PREFIX "" -#endif - /* Rebuild everything on CONFIG_FTRACE_MCOUNT_RECORD */ #ifdef CONFIG_FTRACE_MCOUNT_RECORD # define REBUILD_DUE_TO_FTRACE_MCOUNT_RECORD diff --git a/include/linux/module.h b/include/linux/module.h index ead1b5719a1..46f1ea01e6f 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -190,7 +190,7 @@ extern int modules_disabled; /* for sysctl */ /* Get/put a kernel symbol (calls must be symmetric) */ void *__symbol_get(const char *symbol); void *__symbol_get_gpl(const char *symbol); -#define symbol_get(x) ((typeof(&x))(__symbol_get(MODULE_SYMBOL_PREFIX #x))) +#define symbol_get(x) ((typeof(&x))(__symbol_get(VMLINUX_SYMBOL_STR(x)))) /* modules using other modules: kdb wants to see this. */ struct module_use { @@ -453,7 +453,7 @@ extern void __module_put_and_exit(struct module *mod, long code) #ifdef CONFIG_MODULE_UNLOAD unsigned long module_refcount(struct module *mod); void __symbol_put(const char *symbol); -#define symbol_put(x) __symbol_put(MODULE_SYMBOL_PREFIX #x) +#define symbol_put(x) __symbol_put(VMLINUX_SYMBOL_STR(x)) void symbol_put_addr(void *addr); /* Sometimes we know we already have a refcount, and it's easier not diff --git a/kernel/modsign_certificate.S b/kernel/modsign_certificate.S index 246b4c6e613..4a9a86d12c8 100644 --- a/kernel/modsign_certificate.S +++ b/kernel/modsign_certificate.S @@ -1,15 +1,8 @@ -/* SYMBOL_PREFIX defined on commandline from CONFIG_SYMBOL_PREFIX */ -#ifndef SYMBOL_PREFIX -#define ASM_SYMBOL(sym) sym -#else -#define PASTE2(x,y) x##y -#define PASTE(x,y) PASTE2(x,y) -#define ASM_SYMBOL(sym) PASTE(SYMBOL_PREFIX, sym) -#endif +#include #define GLOBAL(name) \ - .globl ASM_SYMBOL(name); \ - ASM_SYMBOL(name): + .globl VMLINUX_SYMBOL(name); \ + VMLINUX_SYMBOL(name): .section ".init.data","aw" diff --git a/kernel/module.c b/kernel/module.c index 0925c9a7197..cfd4a3f68d7 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1209,7 +1209,7 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs, /* Since this should be found in kernel (which can't be removed), * no locking is necessary. */ - if (!find_symbol(MODULE_SYMBOL_PREFIX "module_layout", NULL, + if (!find_symbol(VMLINUX_SYMBOL_STR(module_layout), NULL, &crc, true, false)) BUG(); return check_version(sechdrs, versindex, "module_layout", mod, crc, diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 07125e697d7..a373a1f6602 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -119,13 +119,6 @@ _c_flags += $(if $(patsubst n%,, \ $(CFLAGS_GCOV)) endif -ifdef CONFIG_SYMBOL_PREFIX -_sym_flags = -DSYMBOL_PREFIX=$(patsubst "%",%,$(CONFIG_SYMBOL_PREFIX)) -_cpp_flags += $(_sym_flags) -_a_flags += $(_sym_flags) -endif - - # If building the kernel in a separate objtree expand all occurrences # of -Idir to -I$(srctree)/dir except for absolute paths (starting with '/'). diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 3d569d6022c..014994936b1 100644 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -74,9 +74,8 @@ kallsyms() info KSYM ${2} local kallsymopt; - if [ -n "${CONFIG_SYMBOL_PREFIX}" ]; then - kallsymopt="${kallsymopt} \ - --symbol-prefix=${CONFIG_SYMBOL_PREFIX}" + if [ -n "${CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX}" ]; then + kallsymopt="${kallsymopt} --symbol-prefix=_" fi if [ -n "${CONFIG_KALLSYMS_ALL}" ]; then diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 78b30c1548e..282decfa29a 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -18,14 +18,7 @@ #include "modpost.h" #include "../../include/generated/autoconf.h" #include "../../include/linux/license.h" - -/* Some toolchains use a `_' prefix for all user symbols. */ -#ifdef CONFIG_SYMBOL_PREFIX -#define MODULE_SYMBOL_PREFIX CONFIG_SYMBOL_PREFIX -#else -#define MODULE_SYMBOL_PREFIX "" -#endif - +#include "../../include/linux/export.h" /* Are we using CONFIG_MODVERSIONS? */ int modversions = 0; @@ -562,7 +555,7 @@ static void parse_elf_finish(struct elf_info *info) static int ignore_undef_symbol(struct elf_info *info, const char *symname) { /* ignore __this_module, it will be resolved shortly */ - if (strcmp(symname, MODULE_SYMBOL_PREFIX "__this_module") == 0) + if (strcmp(symname, VMLINUX_SYMBOL_STR(__this_module)) == 0) return 1; /* ignore global offset table */ if (strcmp(symname, "_GLOBAL_OFFSET_TABLE_") == 0) @@ -583,8 +576,8 @@ static int ignore_undef_symbol(struct elf_info *info, const char *symname) return 0; } -#define CRC_PFX MODULE_SYMBOL_PREFIX "__crc_" -#define KSYMTAB_PFX MODULE_SYMBOL_PREFIX "__ksymtab_" +#define CRC_PFX VMLINUX_SYMBOL_STR(__crc_) +#define KSYMTAB_PFX VMLINUX_SYMBOL_STR(__ksymtab_) static void handle_modversions(struct module *mod, struct elf_info *info, Elf_Sym *sym, const char *symname) @@ -637,14 +630,15 @@ static void handle_modversions(struct module *mod, struct elf_info *info, } #endif - if (memcmp(symname, MODULE_SYMBOL_PREFIX, - strlen(MODULE_SYMBOL_PREFIX)) == 0) { - mod->unres = - alloc_symbol(symname + - strlen(MODULE_SYMBOL_PREFIX), - ELF_ST_BIND(sym->st_info) == STB_WEAK, - mod->unres); - } +#ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX + if (symname[0] != '_') + break; + else + symname++; +#endif + mod->unres = alloc_symbol(symname, + ELF_ST_BIND(sym->st_info) == STB_WEAK, + mod->unres); break; default: /* All exported symbols */ @@ -652,9 +646,9 @@ static void handle_modversions(struct module *mod, struct elf_info *info, sym_add_exported(symname + strlen(KSYMTAB_PFX), mod, export); } - if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0) + if (strcmp(symname, VMLINUX_SYMBOL_STR(init_module)) == 0) mod->has_init = 1; - if (strcmp(symname, MODULE_SYMBOL_PREFIX "cleanup_module") == 0) + if (strcmp(symname, VMLINUX_SYMBOL_STR(cleanup_module)) == 0) mod->has_cleanup = 1; break; } -- cgit v1.2.3-70-g09d2 From 8a7fbfab4be39b8690543f3d29b26860d2f6c576 Mon Sep 17 00:00:00 2001 From: "nikolay@redhat.com" Date: Tue, 12 Mar 2013 02:49:01 +0000 Subject: netxen: write IP address to firmware when using bonding This patch allows LRO aggregation on bonded devices that contain an NX3031 device. It also adds a for_each_netdev_in_bond_rcu(bond, slave) macro which executes for each slave that has bond as master. V3: After testing and discussing this with Rajesh, I decided to keep the vlan ip cache and just rename it to ip_cache since it will store bond ip addresses too. A new master flag has been added to the ip cache to denote that the address has been added because of a master device. I've taken care of the enslave/release cases by checking for various combinations of events and flags (e.g. netxen has a master, it's a bond master and it's not marked as a slave means it is being enslaved and is dev_open()ed in bond_enslave). I've changed netxen_free_ip_list() to have a "master" parameter which causes all IP addresses marked as master to be deleted (used when a netxen is being released). I've made the patch use the new upper device API as well. The following cases were tested: - bond -> netxen - vlan -> netxen - vlan -> bond -> netxen V2: Remove local ip caching, retrieve addresses dynamically and restore them if necessary. Note: Tested with NX3031 adapter. Tested-by: Rajesh Borundia Signed-off-by: Andy Gospodarek Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/netxen/netxen_nic.h | 5 +- .../net/ethernet/qlogic/netxen/netxen_nic_main.c | 220 ++++++++++++++------- include/linux/netdevice.h | 8 + 3 files changed, 155 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h index eb3dfdbb642..322a36b7672 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h @@ -955,9 +955,10 @@ typedef struct nx_mac_list_s { uint8_t mac_addr[ETH_ALEN+2]; } nx_mac_list_t; -struct nx_vlan_ip_list { +struct nx_ip_list { struct list_head list; __be32 ip_addr; + bool master; }; /* @@ -1605,7 +1606,7 @@ struct netxen_adapter { struct net_device *netdev; struct pci_dev *pdev; struct list_head mac_list; - struct list_head vlan_ip_list; + struct list_head ip_list; spinlock_t tx_clean_lock; diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 501f49207da..7867aebc05f 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -90,7 +90,7 @@ static irqreturn_t netxen_intr(int irq, void *data); static irqreturn_t netxen_msi_intr(int irq, void *data); static irqreturn_t netxen_msix_intr(int irq, void *data); -static void netxen_free_vlan_ip_list(struct netxen_adapter *); +static void netxen_free_ip_list(struct netxen_adapter *, bool); static void netxen_restore_indev_addr(struct net_device *dev, unsigned long); static struct rtnl_link_stats64 *netxen_nic_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats); @@ -1450,7 +1450,7 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) spin_lock_init(&adapter->tx_clean_lock); INIT_LIST_HEAD(&adapter->mac_list); - INIT_LIST_HEAD(&adapter->vlan_ip_list); + INIT_LIST_HEAD(&adapter->ip_list); err = netxen_setup_pci_map(adapter); if (err) @@ -1585,7 +1585,7 @@ static void netxen_nic_remove(struct pci_dev *pdev) cancel_work_sync(&adapter->tx_timeout_task); - netxen_free_vlan_ip_list(adapter); + netxen_free_ip_list(adapter, false); netxen_nic_detach(adapter); nx_decr_dev_ref_cnt(adapter); @@ -3137,62 +3137,77 @@ netxen_destip_supported(struct netxen_adapter *adapter) } static void -netxen_free_vlan_ip_list(struct netxen_adapter *adapter) +netxen_free_ip_list(struct netxen_adapter *adapter, bool master) { - struct nx_vlan_ip_list *cur; - struct list_head *head = &adapter->vlan_ip_list; + struct nx_ip_list *cur, *tmp_cur; - while (!list_empty(head)) { - cur = list_entry(head->next, struct nx_vlan_ip_list, list); - netxen_config_ipaddr(adapter, cur->ip_addr, NX_IP_DOWN); - list_del(&cur->list); - kfree(cur); + list_for_each_entry_safe(cur, tmp_cur, &adapter->ip_list, list) { + if (master) { + if (cur->master) { + netxen_config_ipaddr(adapter, cur->ip_addr, + NX_IP_DOWN); + list_del(&cur->list); + kfree(cur); + } + } else { + netxen_config_ipaddr(adapter, cur->ip_addr, NX_IP_DOWN); + list_del(&cur->list); + kfree(cur); + } } - } -static void -netxen_list_config_vlan_ip(struct netxen_adapter *adapter, + +static bool +netxen_list_config_ip(struct netxen_adapter *adapter, struct in_ifaddr *ifa, unsigned long event) { struct net_device *dev; - struct nx_vlan_ip_list *cur, *tmp_cur; + struct nx_ip_list *cur, *tmp_cur; struct list_head *head; + bool ret = false; dev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL; if (dev == NULL) - return; - - if (!is_vlan_dev(dev)) - return; + goto out; switch (event) { case NX_IP_UP: - list_for_each(head, &adapter->vlan_ip_list) { - cur = list_entry(head, struct nx_vlan_ip_list, list); + list_for_each(head, &adapter->ip_list) { + cur = list_entry(head, struct nx_ip_list, list); if (cur->ip_addr == ifa->ifa_address) - return; + goto out; } - cur = kzalloc(sizeof(struct nx_vlan_ip_list), GFP_ATOMIC); + cur = kzalloc(sizeof(struct nx_ip_list), GFP_ATOMIC); if (cur == NULL) - return; - + goto out; + if (dev->priv_flags & IFF_802_1Q_VLAN) + dev = vlan_dev_real_dev(dev); + cur->master = !!netif_is_bond_master(dev); cur->ip_addr = ifa->ifa_address; - list_add_tail(&cur->list, &adapter->vlan_ip_list); + list_add_tail(&cur->list, &adapter->ip_list); + netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_UP); + ret = true; break; case NX_IP_DOWN: list_for_each_entry_safe(cur, tmp_cur, - &adapter->vlan_ip_list, list) { + &adapter->ip_list, list) { if (cur->ip_addr == ifa->ifa_address) { list_del(&cur->list); kfree(cur); + netxen_config_ipaddr(adapter, ifa->ifa_address, + NX_IP_DOWN); + ret = true; break; } } } +out: + return ret; } + static void netxen_config_indev_addr(struct netxen_adapter *adapter, struct net_device *dev, unsigned long event) @@ -3209,14 +3224,10 @@ netxen_config_indev_addr(struct netxen_adapter *adapter, for_ifa(indev) { switch (event) { case NETDEV_UP: - netxen_config_ipaddr(adapter, - ifa->ifa_address, NX_IP_UP); - netxen_list_config_vlan_ip(adapter, ifa, NX_IP_UP); + netxen_list_config_ip(adapter, ifa, NX_IP_UP); break; case NETDEV_DOWN: - netxen_config_ipaddr(adapter, - ifa->ifa_address, NX_IP_DOWN); - netxen_list_config_vlan_ip(adapter, ifa, NX_IP_DOWN); + netxen_list_config_ip(adapter, ifa, NX_IP_DOWN); break; default: break; @@ -3231,23 +3242,78 @@ netxen_restore_indev_addr(struct net_device *netdev, unsigned long event) { struct netxen_adapter *adapter = netdev_priv(netdev); - struct nx_vlan_ip_list *pos, *tmp_pos; + struct nx_ip_list *pos, *tmp_pos; unsigned long ip_event; ip_event = (event == NETDEV_UP) ? NX_IP_UP : NX_IP_DOWN; netxen_config_indev_addr(adapter, netdev, event); - list_for_each_entry_safe(pos, tmp_pos, &adapter->vlan_ip_list, list) { + list_for_each_entry_safe(pos, tmp_pos, &adapter->ip_list, list) { netxen_config_ipaddr(adapter, pos->ip_addr, ip_event); } } +static inline bool +netxen_config_checkdev(struct net_device *dev) +{ + struct netxen_adapter *adapter; + + if (!is_netxen_netdev(dev)) + return false; + adapter = netdev_priv(dev); + if (!adapter) + return false; + if (!netxen_destip_supported(adapter)) + return false; + if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) + return false; + + return true; +} + +/** + * netxen_config_master - configure addresses based on master + * @dev: netxen device + * @event: netdev event + */ +static void netxen_config_master(struct net_device *dev, unsigned long event) +{ + struct net_device *master, *slave; + struct netxen_adapter *adapter = netdev_priv(dev); + + rcu_read_lock(); + master = netdev_master_upper_dev_get_rcu(dev); + /* + * This is the case where the netxen nic is being + * enslaved and is dev_open()ed in bond_enslave() + * Now we should program the bond's (and its vlans') + * addresses in the netxen NIC. + */ + if (master && netif_is_bond_master(master) && + !netif_is_bond_slave(dev)) { + netxen_config_indev_addr(adapter, master, event); + for_each_netdev_rcu(&init_net, slave) + if (slave->priv_flags & IFF_802_1Q_VLAN && + vlan_dev_real_dev(slave) == master) + netxen_config_indev_addr(adapter, slave, event); + } + rcu_read_unlock(); + /* + * This is the case where the netxen nic is being + * released and is dev_close()ed in bond_release() + * just before IFF_BONDING is stripped. + */ + if (!master && dev->priv_flags & IFF_BONDING) + netxen_free_ip_list(adapter, true); +} + static int netxen_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct netxen_adapter *adapter; struct net_device *dev = (struct net_device *)ptr; struct net_device *orig_dev = dev; + struct net_device *slave; recheck: if (dev == NULL) @@ -3257,19 +3323,28 @@ recheck: dev = vlan_dev_real_dev(dev); goto recheck; } - - if (!is_netxen_netdev(dev)) - goto done; - - adapter = netdev_priv(dev); - - if (!adapter) - goto done; - - if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) - goto done; - - netxen_config_indev_addr(adapter, orig_dev, event); + if (event == NETDEV_UP || event == NETDEV_DOWN) { + /* If this is a bonding device, look for netxen-based slaves*/ + if (netif_is_bond_master(dev)) { + rcu_read_lock(); + for_each_netdev_in_bond_rcu(dev, slave) { + if (!netxen_config_checkdev(slave)) + continue; + adapter = netdev_priv(slave); + netxen_config_indev_addr(adapter, + orig_dev, event); + } + rcu_read_unlock(); + } else { + if (!netxen_config_checkdev(dev)) + goto done; + adapter = netdev_priv(dev); + /* Act only if the actual netxen is the target */ + if (orig_dev == dev) + netxen_config_master(dev, event); + netxen_config_indev_addr(adapter, orig_dev, event); + } + } done: return NOTIFY_DONE; } @@ -3279,12 +3354,12 @@ netxen_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr) { struct netxen_adapter *adapter; - struct net_device *dev; - + struct net_device *dev, *slave; struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; + unsigned long ip_event; dev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL; - + ip_event = (event == NETDEV_UP) ? NX_IP_UP : NX_IP_DOWN; recheck: if (dev == NULL) goto done; @@ -3293,31 +3368,24 @@ recheck: dev = vlan_dev_real_dev(dev); goto recheck; } - - if (!is_netxen_netdev(dev)) - goto done; - - adapter = netdev_priv(dev); - - if (!adapter || !netxen_destip_supported(adapter)) - goto done; - - if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) - goto done; - - switch (event) { - case NETDEV_UP: - netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_UP); - netxen_list_config_vlan_ip(adapter, ifa, NX_IP_UP); - break; - case NETDEV_DOWN: - netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_DOWN); - netxen_list_config_vlan_ip(adapter, ifa, NX_IP_DOWN); - break; - default: - break; + if (event == NETDEV_UP || event == NETDEV_DOWN) { + /* If this is a bonding device, look for netxen-based slaves*/ + if (netif_is_bond_master(dev)) { + rcu_read_lock(); + for_each_netdev_in_bond_rcu(dev, slave) { + if (!netxen_config_checkdev(slave)) + continue; + adapter = netdev_priv(slave); + netxen_list_config_ip(adapter, ifa, ip_event); + } + rcu_read_unlock(); + } else { + if (!netxen_config_checkdev(dev)) + goto done; + adapter = netdev_priv(dev); + netxen_list_config_ip(adapter, ifa, ip_event); + } } - done: return NOTIFY_DONE; } @@ -3334,7 +3402,7 @@ static void netxen_restore_indev_addr(struct net_device *dev, unsigned long event) { } static void -netxen_free_vlan_ip_list(struct netxen_adapter *adapter) +netxen_free_ip_list(struct netxen_adapter *adapter, bool master) { } #endif diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e1ebeffa6b3..9fc1ab0c891 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1617,6 +1617,9 @@ extern seqcount_t devnet_rename_seq; /* Device rename seq */ list_for_each_entry_continue(d, &(net)->dev_base_head, dev_list) #define for_each_netdev_continue_rcu(net, d) \ list_for_each_entry_continue_rcu(d, &(net)->dev_base_head, dev_list) +#define for_each_netdev_in_bond_rcu(bond, slave) \ + for_each_netdev_rcu(&init_net, slave) \ + if (netdev_master_upper_dev_get_rcu(slave) == bond) #define net_device_entry(lh) list_entry(lh, struct net_device, dev_list) static inline struct net_device *next_net_device(struct net_device *dev) @@ -2774,6 +2777,11 @@ static inline void netif_set_gso_max_size(struct net_device *dev, dev->gso_max_size = size; } +static inline bool netif_is_bond_master(struct net_device *dev) +{ + return dev->flags & IFF_MASTER && dev->priv_flags & IFF_BONDING; +} + static inline bool netif_is_bond_slave(struct net_device *dev) { return dev->flags & IFF_SLAVE && dev->priv_flags & IFF_BONDING; -- cgit v1.2.3-70-g09d2 From 764444f5a324ad5a272773f078192819084388ce Mon Sep 17 00:00:00 2001 From: Fernando Luis Vazquez Cao Date: Wed, 13 Mar 2013 16:57:25 +0000 Subject: net: clean leftover of COMPAT_NET_DEV_OPS removal COMPAT_NET_DEV_OPS was removed a while back and with it the definition of netdev_resync_ops() went away. Let's finish the clean-up. Signed-off-by: Fernando Luis Vazquez Cao Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9fc1ab0c891..56e3e066527 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1692,7 +1692,6 @@ extern int netdev_refcnt_read(const struct net_device *dev); extern void free_netdev(struct net_device *dev); extern void synchronize_net(void); extern int init_dummy_netdev(struct net_device *dev); -extern void netdev_resync_ops(struct net_device *dev); extern struct net_device *dev_get_by_index(struct net *net, int ifindex); extern struct net_device *__dev_get_by_index(struct net *net, int ifindex); -- cgit v1.2.3-70-g09d2 From dad3cab3e063110b3ae3dc82a00e7aacd09b91ec Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Mon, 4 Mar 2013 16:52:49 -0600 Subject: USB: fix trivial usb_device kernel-doc errors Fix trivial kernel-doc warnings: Warning(include/linux/usb.h:574): No description found for parameter 'usb3_lpm_enabled' Warning(include/linux/usb.h:574): Excess struct/union/enum/typedef member 'usb_classdev' description in 'usb_device' Warning(include/linux/usb.h:574): Excess struct/union/enum/typedef member 'usbfs_dentry' description in 'usb_device' Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: Jiri Kosina Cc: linux-usb@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Nishanth Menon Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/usb.h b/include/linux/usb.h index 4d22d0f6167..52464fb2389 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -469,14 +469,12 @@ struct usb3_lpm_parameters { * @lpm_capable: device supports LPM * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled + * @usb3_lpm_enabled: USB3 hardware LPM enabled * @string_langid: language ID for strings * @product: iProduct string, if present (static) * @manufacturer: iManufacturer string, if present (static) * @serial: iSerialNumber string, if present (static) * @filelist: usbfs files that are open to this device - * @usb_classdev: USB class device that was created for usbfs device - * access from userspace - * @usbfs_dentry: usbfs dentry entry for the device * @maxchild: number of ports if hub * @quirks: quirks of the whole device * @urbnum: number of URBs submitted for the whole device -- cgit v1.2.3-70-g09d2 From 96dd86fa588169b745a71aedf2070e80f4943623 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 15 Mar 2013 12:30:06 -0700 Subject: Drivers: hv: Add a new driver to support host initiated backup This driver supports host initiated backup of the guest. On Windows guests, the host can generate application consistent backups using the Windows VSS framework. On Linux, we ensure that the backup will be file system consistent. This driver allows the host to initiate a "Freeze" operation on all the mounted file systems in the guest. Once the mounted file systems in the guest are frozen, the host snapshots the guest's file systems. Once this is done, the guest's file systems are "thawed". This driver has a user-level component (daemon) that invokes the appropriate operation on all the mounted file systems in response to the requests from the host. The duration for which the guest is frozen is very short - a few seconds. During this interval, the diff disk is comitted. In this version of the patch I have addressed the feedback from Olaf Herring. Also, some of the connector related issues have been fixed. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Cc: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/hv/Makefile | 2 +- drivers/hv/hv_snapshot.c | 287 +++++++++++++++++++++++++++++++++++++++++ drivers/hv/hv_util.c | 10 ++ include/linux/hyperv.h | 69 ++++++++++ include/uapi/linux/connector.h | 5 +- tools/hv/hv_vss_daemon.c | 220 +++++++++++++++++++++++++++++++ 6 files changed, 591 insertions(+), 2 deletions(-) create mode 100644 drivers/hv/hv_snapshot.c create mode 100644 tools/hv/hv_vss_daemon.c (limited to 'include') diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index e6abfa02d8b..0a74b566118 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o hv_vmbus-y := vmbus_drv.o \ hv.o connection.o channel.o \ channel_mgmt.o ring_buffer.o -hv_utils-y := hv_util.o hv_kvp.o +hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c new file mode 100644 index 00000000000..8ad5653ce44 --- /dev/null +++ b/drivers/hv/hv_snapshot.c @@ -0,0 +1,287 @@ +/* + * An implementation of host initiated guest snapshot. + * + * + * Copyright (C) 2013, Microsoft, Inc. + * Author : K. Y. Srinivasan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + + + +/* + * Global state maintained for transaction that is being processed. + * Note that only one transaction can be active at any point in time. + * + * This state is set when we receive a request from the host; we + * cleanup this state when the transaction is completed - when we respond + * to the host with the key value. + */ + +static struct { + bool active; /* transaction status - active or not */ + int recv_len; /* number of bytes received. */ + struct vmbus_channel *recv_channel; /* chn we got the request */ + u64 recv_req_id; /* request ID. */ + struct hv_vss_msg *msg; /* current message */ +} vss_transaction; + + +static void vss_respond_to_host(int error); + +static struct cb_id vss_id = { CN_VSS_IDX, CN_VSS_VAL }; +static const char vss_name[] = "vss_kernel_module"; +static __u8 *recv_buffer; + +static void vss_send_op(struct work_struct *dummy); +static DECLARE_WORK(vss_send_op_work, vss_send_op); + +/* + * Callback when data is received from user mode. + */ + +static void +vss_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) +{ + struct hv_vss_msg *vss_msg; + + vss_msg = (struct hv_vss_msg *)msg->data; + + if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER) { + pr_info("VSS daemon registered\n"); + vss_transaction.active = false; + if (vss_transaction.recv_channel != NULL) + hv_vss_onchannelcallback(vss_transaction.recv_channel); + return; + + } + vss_respond_to_host(vss_msg->error); +} + + +static void vss_send_op(struct work_struct *dummy) +{ + int op = vss_transaction.msg->vss_hdr.operation; + struct cn_msg *msg; + struct hv_vss_msg *vss_msg; + + msg = kzalloc(sizeof(*msg) + sizeof(*vss_msg), GFP_ATOMIC); + if (!msg) + return; + + vss_msg = (struct hv_vss_msg *)msg->data; + + msg->id.idx = CN_VSS_IDX; + msg->id.val = CN_VSS_VAL; + + vss_msg->vss_hdr.operation = op; + msg->len = sizeof(struct hv_vss_msg); + + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + + return; +} + +/* + * Send a response back to the host. + */ + +static void +vss_respond_to_host(int error) +{ + struct icmsg_hdr *icmsghdrp; + u32 buf_len; + struct vmbus_channel *channel; + u64 req_id; + + /* + * If a transaction is not active; log and return. + */ + + if (!vss_transaction.active) { + /* + * This is a spurious call! + */ + pr_warn("VSS: Transaction not active\n"); + return; + } + /* + * Copy the global state for completing the transaction. Note that + * only one transaction can be active at a time. + */ + + buf_len = vss_transaction.recv_len; + channel = vss_transaction.recv_channel; + req_id = vss_transaction.recv_req_id; + vss_transaction.active = false; + + icmsghdrp = (struct icmsg_hdr *) + &recv_buffer[sizeof(struct vmbuspipe_hdr)]; + + if (channel->onchannel_callback == NULL) + /* + * We have raced with util driver being unloaded; + * silently return. + */ + return; + + icmsghdrp->status = error; + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, + VM_PKT_DATA_INBAND, 0); + +} + +/* + * This callback is invoked when we get a VSS message from the host. + * The host ensures that only one VSS transaction can be active at a time. + */ + +void hv_vss_onchannelcallback(void *context) +{ + struct vmbus_channel *channel = context; + u32 recvlen; + u64 requestid; + struct hv_vss_msg *vss_msg; + + + struct icmsg_hdr *icmsghdrp; + struct icmsg_negotiate *negop = NULL; + + if (vss_transaction.active) { + /* + * We will defer processing this callback once + * the current transaction is complete. + */ + vss_transaction.recv_channel = channel; + return; + } + + vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, + &requestid); + + if (recvlen > 0) { + icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + vmbus_prep_negotiate_resp(icmsghdrp, negop, + recv_buffer, MAX_SRV_VER, MAX_SRV_VER); + /* + * We currently negotiate the highest number the + * host has presented. If this version is not + * atleast 5.0, reject. + */ + negop = (struct icmsg_negotiate *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + if (negop->icversion_data[1].major < 5) + negop->icframe_vercnt = 0; + } else { + vss_msg = (struct hv_vss_msg *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + /* + * Stash away this global state for completing the + * transaction; note transactions are serialized. + */ + + vss_transaction.recv_len = recvlen; + vss_transaction.recv_channel = channel; + vss_transaction.recv_req_id = requestid; + vss_transaction.active = true; + vss_transaction.msg = (struct hv_vss_msg *)vss_msg; + + switch (vss_msg->vss_hdr.operation) { + /* + * Initiate a "freeze/thaw" + * operation in the guest. + * We respond to the host once + * the operation is complete. + * + * We send the message to the + * user space daemon and the + * operation is performed in + * the daemon. + */ + case VSS_OP_FREEZE: + case VSS_OP_THAW: + schedule_work(&vss_send_op_work); + return; + + case VSS_OP_HOT_BACKUP: + vss_msg->vss_cf.flags = + VSS_HBU_NO_AUTO_RECOVERY; + vss_respond_to_host(0); + return; + + case VSS_OP_GET_DM_INFO: + vss_msg->dm_info.flags = 0; + vss_respond_to_host(0); + return; + + default: + vss_respond_to_host(0); + return; + + } + + } + + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, recv_buffer, + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); + } + +} + +int +hv_vss_init(struct hv_util_service *srv) +{ + int err; + + err = cn_add_callback(&vss_id, vss_name, vss_cn_callback); + if (err) + return err; + recv_buffer = srv->recv_buffer; + + /* + * When this driver loads, the user level daemon that + * processes the host requests may not yet be running. + * Defer processing channel callbacks until the daemon + * has registered. + */ + vss_transaction.active = true; + return 0; +} + +void hv_vss_deinit(void) +{ + cn_del_callback(&vss_id); + cancel_work_sync(&vss_send_op_work); +} diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 1d4cbd8e826..2f561c5dfe2 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -49,6 +49,12 @@ static struct hv_util_service util_kvp = { .util_deinit = hv_kvp_deinit, }; +static struct hv_util_service util_vss = { + .util_cb = hv_vss_onchannelcallback, + .util_init = hv_vss_init, + .util_deinit = hv_vss_deinit, +}; + static void perform_shutdown(struct work_struct *dummy) { orderly_poweroff(true); @@ -339,6 +345,10 @@ static const struct hv_vmbus_device_id id_table[] = { { HV_KVP_GUID, .driver_data = (unsigned long)&util_kvp }, + /* VSS GUID */ + { HV_VSS_GUID, + .driver_data = (unsigned long)&util_vss + }, { }, }; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index df77ba9a816..95d0850584d 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -27,6 +27,63 @@ #include + +/* + * Implementation of host controlled snapshot of the guest. + */ + +#define VSS_OP_REGISTER 128 + +enum hv_vss_op { + VSS_OP_CREATE = 0, + VSS_OP_DELETE, + VSS_OP_HOT_BACKUP, + VSS_OP_GET_DM_INFO, + VSS_OP_BU_COMPLETE, + /* + * Following operations are only supported with IC version >= 5.0 + */ + VSS_OP_FREEZE, /* Freeze the file systems in the VM */ + VSS_OP_THAW, /* Unfreeze the file systems */ + VSS_OP_AUTO_RECOVER, + VSS_OP_COUNT /* Number of operations, must be last */ +}; + + +/* + * Header for all VSS messages. + */ +struct hv_vss_hdr { + __u8 operation; + __u8 reserved[7]; +} __attribute__((packed)); + + +/* + * Flag values for the hv_vss_check_feature. Linux supports only + * one value. + */ +#define VSS_HBU_NO_AUTO_RECOVERY 0x00000005 + +struct hv_vss_check_feature { + __u32 flags; +} __attribute__((packed)); + +struct hv_vss_check_dm_info { + __u32 flags; +} __attribute__((packed)); + +struct hv_vss_msg { + union { + struct hv_vss_hdr vss_hdr; + int error; + }; + union { + struct hv_vss_check_feature vss_cf; + struct hv_vss_check_dm_info dm_info; + }; +} __attribute__((packed)); + /* * An implementation of HyperV key value pair (KVP) functionality for Linux. * @@ -1252,6 +1309,14 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); 0xb9, 0x8b, 0x8b, 0xa1, 0xa1, 0xf3, 0xf9, 0x5a \ } +/* + * VSS (Backup/Restore) GUID + */ +#define HV_VSS_GUID \ + .guid = { \ + 0x29, 0x2e, 0xfa, 0x35, 0x23, 0xea, 0x36, 0x42, \ + 0x96, 0xae, 0x3a, 0x6e, 0xba, 0xcb, 0xa4, 0x40 \ + } /* * Common header for Hyper-V ICs */ @@ -1356,6 +1421,10 @@ int hv_kvp_init(struct hv_util_service *); void hv_kvp_deinit(void); void hv_kvp_onchannelcallback(void *); +int hv_vss_init(struct hv_util_service *); +void hv_vss_deinit(void); +void hv_vss_onchannelcallback(void *); + /* * Negotiated version with the Host. */ diff --git a/include/uapi/linux/connector.h b/include/uapi/linux/connector.h index 8761a0349c7..4cb283505e4 100644 --- a/include/uapi/linux/connector.h +++ b/include/uapi/linux/connector.h @@ -44,8 +44,11 @@ #define CN_VAL_DRBD 0x1 #define CN_KVP_IDX 0x9 /* HyperV KVP */ #define CN_KVP_VAL 0x1 /* queries from the kernel */ +#define CN_VSS_IDX 0xA /* HyperV VSS */ +#define CN_VSS_VAL 0x1 /* queries from the kernel */ -#define CN_NETLINK_USERS 10 /* Highest index + 1 */ + +#define CN_NETLINK_USERS 11 /* Highest index + 1 */ /* * Maximum connector's message size. diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c new file mode 100644 index 00000000000..95269952aa9 --- /dev/null +++ b/tools/hv/hv_vss_daemon.c @@ -0,0 +1,220 @@ +/* + * An implementation of the host initiated guest snapshot for Hyper-V. + * + * + * Copyright (C) 2013, Microsoft, Inc. + * Author : K. Y. Srinivasan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char vss_recv_buffer[4096]; +static char vss_send_buffer[4096]; +static struct sockaddr_nl addr; + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + + +static int vss_operate(int operation) +{ + char *fs_op; + char cmd[512]; + char buf[512]; + FILE *file; + char *p; + char *x; + int error; + + switch (operation) { + case VSS_OP_FREEZE: + fs_op = "-f "; + break; + case VSS_OP_THAW: + fs_op = "-u "; + break; + } + + file = popen("mount | awk '/^\/dev\// { print $3}'", "r"); + if (file == NULL) + return; + + while ((p = fgets(buf, sizeof(buf), file)) != NULL) { + x = strchr(p, '\n'); + *x = '\0'; + if (!strncmp(p, "/", sizeof("/"))) + continue; + + sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, p); + syslog(LOG_INFO, "VSS cmd is %s\n", cmd); + error = system(cmd); + } + pclose(file); + + sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, "/"); + syslog(LOG_INFO, "VSS cmd is %s\n", cmd); + error = system(cmd); + + return error; +} + +static int netlink_send(int fd, struct cn_msg *msg) +{ + struct nlmsghdr *nlh; + unsigned int size; + struct msghdr message; + char buffer[64]; + struct iovec iov[2]; + + size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); + + nlh = (struct nlmsghdr *)buffer; + nlh->nlmsg_seq = 0; + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_type = NLMSG_DONE; + nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); + nlh->nlmsg_flags = 0; + + iov[0].iov_base = nlh; + iov[0].iov_len = sizeof(*nlh); + + iov[1].iov_base = msg; + iov[1].iov_len = size; + + memset(&message, 0, sizeof(message)); + message.msg_name = &addr; + message.msg_namelen = sizeof(addr); + message.msg_iov = iov; + message.msg_iovlen = 2; + + return sendmsg(fd, &message, 0); +} + +int main(void) +{ + int fd, len, nl_group; + int error; + struct cn_msg *message; + struct pollfd pfd; + struct nlmsghdr *incoming_msg; + struct cn_msg *incoming_cn_msg; + int op; + struct hv_vss_msg *vss_msg; + + daemon(1, 0); + openlog("Hyper-V VSS", 0, LOG_USER); + syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (fd < 0) { + syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); + exit(EXIT_FAILURE); + } + addr.nl_family = AF_NETLINK; + addr.nl_pad = 0; + addr.nl_pid = 0; + addr.nl_groups = 0; + + + error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (error < 0) { + syslog(LOG_ERR, "bind failed; error:%d", error); + close(fd); + exit(EXIT_FAILURE); + } + nl_group = CN_VSS_IDX; + setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)); + /* + * Register ourselves with the kernel. + */ + message = (struct cn_msg *)vss_send_buffer; + message->id.idx = CN_VSS_IDX; + message->id.val = CN_VSS_VAL; + message->ack = 0; + vss_msg = (struct hv_vss_msg *)message->data; + vss_msg->vss_hdr.operation = VSS_OP_REGISTER; + + message->len = sizeof(struct hv_vss_msg); + + len = netlink_send(fd, message); + if (len < 0) { + syslog(LOG_ERR, "netlink_send failed; error:%d", len); + close(fd); + exit(EXIT_FAILURE); + } + + pfd.fd = fd; + + while (1) { + struct sockaddr *addr_p = (struct sockaddr *) &addr; + socklen_t addr_l = sizeof(addr); + pfd.events = POLLIN; + pfd.revents = 0; + poll(&pfd, 1, -1); + + len = recvfrom(fd, vss_recv_buffer, sizeof(vss_recv_buffer), 0, + addr_p, &addr_l); + + if (len < 0 || addr.nl_pid) { + syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", + addr.nl_pid, errno, strerror(errno)); + close(fd); + return -1; + } + + incoming_msg = (struct nlmsghdr *)vss_recv_buffer; + + if (incoming_msg->nlmsg_type != NLMSG_DONE) + continue; + + incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); + vss_msg = (struct hv_vss_msg *)incoming_cn_msg->data; + op = vss_msg->vss_hdr.operation; + error = HV_S_OK; + + switch (op) { + case VSS_OP_FREEZE: + case VSS_OP_THAW: + error = vss_operate(op); + if (error) + error = HV_E_FAIL; + break; + default: + syslog(LOG_ERR, "Illegal op:%d\n", op); + } + vss_msg->error = error; + len = netlink_send(fd, incoming_cn_msg); + if (len < 0) { + syslog(LOG_ERR, "net_link send failed; error:%d", len); + exit(EXIT_FAILURE); + } + } + +} -- cgit v1.2.3-70-g09d2 From fa882867ae5f8543eb304a1667563f1c99514475 Mon Sep 17 00:00:00 2001 From: Samuel Iglesias Gonsalvez Date: Fri, 8 Mar 2013 09:21:46 +0100 Subject: ipack: add ipack_get_device() ipack_put_device() Prepare everything for later use. Signed-off-by: Samuel Iglesias Gonsalvez Signed-off-by: Greg Kroah-Hartman --- drivers/ipack/ipack.c | 12 ++++++++++++ include/linux/ipack.h | 3 +++ 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/drivers/ipack/ipack.c b/drivers/ipack/ipack.c index 7ec6b208b1c..4f913aa8897 100644 --- a/drivers/ipack/ipack.c +++ b/drivers/ipack/ipack.c @@ -461,6 +461,18 @@ void ipack_device_unregister(struct ipack_device *dev) } EXPORT_SYMBOL_GPL(ipack_device_unregister); +void ipack_get_device(struct ipack_device *dev) +{ + get_device(&dev->dev); +} +EXPORT_SYMBOL_GPL(ipack_get_device); + +void ipack_put_device(struct ipack_device *dev) +{ + put_device(&dev->dev); +} +EXPORT_SYMBOL_GPL(ipack_put_device); + static int __init ipack_init(void) { ida_init(&ipack_ida); diff --git a/include/linux/ipack.h b/include/linux/ipack.h index fea12cbb2ae..def91fd996f 100644 --- a/include/linux/ipack.h +++ b/include/linux/ipack.h @@ -221,6 +221,9 @@ void ipack_driver_unregister(struct ipack_driver *edrv); int ipack_device_register(struct ipack_device *dev); void ipack_device_unregister(struct ipack_device *dev); +void ipack_get_device(struct ipack_device *dev); +void ipack_put_device(struct ipack_device *dev); + /** * DEFINE_IPACK_DEVICE_TABLE - macro used to describe a IndustryPack table * @_table: device table name -- cgit v1.2.3-70-g09d2 From e926301b39a07f587ff8c66354a2e2ee4c29162c Mon Sep 17 00:00:00 2001 From: Samuel Iglesias Gonsalvez Date: Fri, 8 Mar 2013 09:21:47 +0100 Subject: ipack: split ipack_device_register() in several functions One function is ipack_device_init(). If it fails, the caller should execute ipack_put_device(). The second function is ipack_device_add that only adds the device. If it fails, the caller should execute ipack_put_device(). Then the device is removed with refcount = 0, as device_register() kernel documentation says. ipack_device_del() is added to remove the device. Signed-off-by: Samuel Iglesias Gonsalvez Signed-off-by: Greg Kroah-Hartman --- drivers/ipack/carriers/tpci200.c | 14 +++++++++++++- drivers/ipack/ipack.c | 24 ++++++++++++++---------- include/linux/ipack.h | 39 +++++++++++++++++++++++++++++---------- 3 files changed, 56 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/ipack/carriers/tpci200.c b/drivers/ipack/carriers/tpci200.c index 0246b1fddff..c276fde318e 100644 --- a/drivers/ipack/carriers/tpci200.c +++ b/drivers/ipack/carriers/tpci200.c @@ -480,6 +480,7 @@ static void tpci200_release_device(struct ipack_device *dev) static int tpci200_create_device(struct tpci200_board *tpci200, int i) { + int ret; enum ipack_space space; struct ipack_device *dev = kzalloc(sizeof(struct ipack_device), GFP_KERNEL); @@ -495,7 +496,18 @@ static int tpci200_create_device(struct tpci200_board *tpci200, int i) + tpci200_space_interval[space] * i; dev->region[space].size = tpci200_space_size[space]; } - return ipack_device_register(dev); + + ret = ipack_device_init(dev); + if (ret < 0) { + ipack_put_device(dev); + return ret; + } + + ret = ipack_device_add(dev); + if (ret < 0) + ipack_put_device(dev); + + return ret; } static int tpci200_pci_probe(struct pci_dev *pdev, diff --git a/drivers/ipack/ipack.c b/drivers/ipack/ipack.c index 4f913aa8897..6e066c53acc 100644 --- a/drivers/ipack/ipack.c +++ b/drivers/ipack/ipack.c @@ -227,7 +227,7 @@ static int ipack_unregister_bus_member(struct device *dev, void *data) struct ipack_bus_device *bus = data; if (idev->bus == bus) - ipack_device_unregister(idev); + ipack_device_del(idev); return 1; } @@ -419,7 +419,7 @@ out: return ret; } -int ipack_device_register(struct ipack_device *dev) +int ipack_device_init(struct ipack_device *dev) { int ret; @@ -428,6 +428,7 @@ int ipack_device_register(struct ipack_device *dev) dev->dev.parent = dev->bus->parent; dev_set_name(&dev->dev, "ipack-dev.%u.%u", dev->bus->bus_nr, dev->slot); + device_initialize(&dev->dev); if (dev->bus->ops->set_clockrate(dev, 8)) dev_warn(&dev->dev, "failed to switch to 8 MHz operation for reading of device ID.\n"); @@ -447,19 +448,22 @@ int ipack_device_register(struct ipack_device *dev) dev_err(&dev->dev, "failed to switch to 32 MHz operation.\n"); } - ret = device_register(&dev->dev); - if (ret < 0) - kfree(dev->id); + return 0; +} +EXPORT_SYMBOL_GPL(ipack_device_init); - return ret; +int ipack_device_add(struct ipack_device *dev) +{ + return device_add(&dev->dev); } -EXPORT_SYMBOL_GPL(ipack_device_register); +EXPORT_SYMBOL_GPL(ipack_device_add); -void ipack_device_unregister(struct ipack_device *dev) +void ipack_device_del(struct ipack_device *dev) { - device_unregister(&dev->dev); + device_del(&dev->dev); + ipack_put_device(dev); } -EXPORT_SYMBOL_GPL(ipack_device_unregister); +EXPORT_SYMBOL_GPL(ipack_device_del); void ipack_get_device(struct ipack_device *dev) { diff --git a/include/linux/ipack.h b/include/linux/ipack.h index def91fd996f..1888e06ddf6 100644 --- a/include/linux/ipack.h +++ b/include/linux/ipack.h @@ -207,19 +207,38 @@ int ipack_driver_register(struct ipack_driver *edrv, struct module *owner, void ipack_driver_unregister(struct ipack_driver *edrv); /** - * ipack_device_register -- register an IPack device with the kernel - * @dev: the new device to register. + * ipack_device_init -- initialize an IPack device + * @dev: the new device to initialize. * - * Register a new IPack device ("module" in IndustryPack jargon). The call - * is done by the carrier driver. The carrier should populate the fields - * bus and slot as well as the region array of @dev prior to calling this - * function. The rest of the fields will be allocated and populated - * during registration. + * Initialize a new IPack device ("module" in IndustryPack jargon). The call + * is done by the carrier driver. The carrier should populate the fields + * bus and slot as well as the region array of @dev prior to calling this + * function. The rest of the fields will be allocated and populated + * during initalization. * - * Return zero on success or error code on failure. + * Return zero on success or error code on failure. + * + * NOTE: _Never_ directly free @dev after calling this function, even + * if it returned an error! Always use ipack_put_device() to give up the + * reference initialized in this function instead. + */ +int ipack_device_init(struct ipack_device *dev); + +/** + * ipack_device_add -- Add an IPack device + * @dev: the new device to add. + * + * Add a new IPack device. The call is done by the carrier driver + * after calling ipack_device_init(). + * + * Return zero on success or error code on failure. + * + * NOTE: _Never_ directly free @dev after calling this function, even + * if it returned an error! Always use ipack_put_device() to give up the + * reference initialized in this function instead. */ -int ipack_device_register(struct ipack_device *dev); -void ipack_device_unregister(struct ipack_device *dev); +int ipack_device_add(struct ipack_device *dev); +void ipack_device_del(struct ipack_device *dev); void ipack_get_device(struct ipack_device *dev); void ipack_put_device(struct ipack_device *dev); -- cgit v1.2.3-70-g09d2 From 6ed7ffddcf61f668114edb676417e5fb33773b59 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 6 Mar 2013 11:24:44 -0700 Subject: pcmcia/ds.h: introduce helper for pcmcia_driver module boilerplate Introduce the module_pcmcia_driver() macro which is a convenience macro for pcmcia driver modules. It is intended to be used by pcmcia drivers with init/exit sections that do nothing but register/unregister the pcmcia driver. Signed-off-by: H Hartley Sweeten Cc: linux-pcmcia@lists.infradead.org Signed-off-by: Greg Kroah-Hartman --- include/pcmcia/ds.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index 3bbbd78e143..2d56e428506 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -65,6 +65,18 @@ struct pcmcia_driver { int pcmcia_register_driver(struct pcmcia_driver *driver); void pcmcia_unregister_driver(struct pcmcia_driver *driver); +/** + * module_pcmcia_driver() - Helper macro for registering a pcmcia driver + * @__pcmcia_driver: pcmcia_driver struct + * + * Helper macro for pcmcia drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only use + * this macro once, and calling it replaces module_init() and module_exit(). + */ +#define module_pcmcia_driver(__pcmcia_driver) \ + module_driver(__pcmcia_driver, pcmcia_register_driver, \ + pcmcia_unregister_driver) + /* for struct resource * array embedded in struct pcmcia_device */ enum { PCMCIA_IOPORT_0, -- cgit v1.2.3-70-g09d2 From 5caf4636259ae3af0efbb9bfc4cd97874b547c7d Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Tue, 12 Mar 2013 11:56:46 +0800 Subject: clocksource: Add new feature flag CLOCK_SOURCE_SUSPEND_NONSTOP Some x86 processors have a TSC clocksource, which continues to run even when system is suspended. Also most OMAP platforms have a 32 KHz timer which has similar capability. Add a feature flag so that it could be utilized. Signed-off-by: Feng Tang Signed-off-by: John Stultz --- include/linux/clocksource.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 27cfda427dd..aa7032c7238 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -206,6 +206,7 @@ struct clocksource { #define CLOCK_SOURCE_WATCHDOG 0x10 #define CLOCK_SOURCE_VALID_FOR_HRES 0x20 #define CLOCK_SOURCE_UNSTABLE 0x40 +#define CLOCK_SOURCE_SUSPEND_NONSTOP 0x80 /* simplify initialization of mask field */ #define CLOCKSOURCE_MASK(bits) (cycle_t)((bits) < 64 ? ((1ULL<<(bits))-1) : -1) -- cgit v1.2.3-70-g09d2 From 7a875903389f3492d4cb06faa1d55a1630e77c11 Mon Sep 17 00:00:00 2001 From: Erwan Yvin Date: Mon, 11 Mar 2013 03:13:03 +0000 Subject: caif: remove caif_shm caif_shm is an old implementation caif_shm will be replaced by caif_virtio [ As explained by Linus Walleij: "U5500 used this, but was cancelled and the silicon did not reach anyone outside ST-Ericsson. Then for the next platforms, we have gone for the leaner & cleaner approach of using virtio, rpmesg and rproc." ] Signed-off-by: Erwan Yvin Acked-by: Linus Walleij Acked-by: Sjur Brendeland Signed-off-by: David S. Miller --- drivers/net/caif/Kconfig | 7 - drivers/net/caif/Makefile | 4 - drivers/net/caif/caif_shm_u5500.c | 128 ------- drivers/net/caif/caif_shmcore.c | 744 -------------------------------------- include/net/caif/caif_shm.h | 26 -- 5 files changed, 909 deletions(-) delete mode 100644 drivers/net/caif/caif_shm_u5500.c delete mode 100644 drivers/net/caif/caif_shmcore.c delete mode 100644 include/net/caif/caif_shm.h (limited to 'include') diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig index 60c2142373c..a966128c2a7 100644 --- a/drivers/net/caif/Kconfig +++ b/drivers/net/caif/Kconfig @@ -32,13 +32,6 @@ config CAIF_SPI_SYNC help to synchronize to the next transfer in case of over or under-runs. This option also needs to be enabled on the modem. -config CAIF_SHM - tristate "CAIF shared memory protocol driver" - depends on CAIF && U5500_MBOX - default n - ---help--- - The CAIF shared memory protocol driver for the STE UX5500 platform. - config CAIF_HSI tristate "CAIF HSI transport driver" depends on CAIF diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile index 91dff861560..15a9d2fc753 100644 --- a/drivers/net/caif/Makefile +++ b/drivers/net/caif/Makefile @@ -7,9 +7,5 @@ obj-$(CONFIG_CAIF_TTY) += caif_serial.o cfspi_slave-objs := caif_spi.o caif_spi_slave.o obj-$(CONFIG_CAIF_SPI_SLAVE) += cfspi_slave.o -# Shared memory -caif_shm-objs := caif_shmcore.o caif_shm_u5500.o -obj-$(CONFIG_CAIF_SHM) += caif_shm.o - # HSI interface obj-$(CONFIG_CAIF_HSI) += caif_hsi.o diff --git a/drivers/net/caif/caif_shm_u5500.c b/drivers/net/caif/caif_shm_u5500.c deleted file mode 100644 index 89d76b7b325..00000000000 --- a/drivers/net/caif/caif_shm_u5500.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com - * Author: Amarnath Revanna / amarnath.bangalore.revanna@stericsson.com - * License terms: GNU General Public License (GPL) version 2 - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt - -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("CAIF Shared Memory protocol driver"); - -#define MAX_SHM_INSTANCES 1 - -enum { - MBX_ACC0, - MBX_ACC1, - MBX_DSP -}; - -static struct shmdev_layer shmdev_lyr[MAX_SHM_INSTANCES]; - -static unsigned int shm_start; -static unsigned int shm_size; - -module_param(shm_size, uint , 0440); -MODULE_PARM_DESC(shm_total_size, "Start of SHM shared memory"); - -module_param(shm_start, uint , 0440); -MODULE_PARM_DESC(shm_total_start, "Total Size of SHM shared memory"); - -static int shmdev_send_msg(u32 dev_id, u32 mbx_msg) -{ - /* Always block until msg is written successfully */ - mbox_send(shmdev_lyr[dev_id].hmbx, mbx_msg, true); - return 0; -} - -static int shmdev_mbx_setup(void *pshmdrv_cb, struct shmdev_layer *pshm_dev, - void *pshm_drv) -{ - /* - * For UX5500, we have only 1 SHM instance which uses MBX0 - * for communication with the peer modem - */ - pshm_dev->hmbx = mbox_setup(MBX_ACC0, pshmdrv_cb, pshm_drv); - - if (!pshm_dev->hmbx) - return -ENODEV; - else - return 0; -} - -static int __init caif_shmdev_init(void) -{ - int i, result; - - /* Loop is currently overkill, there is only one instance */ - for (i = 0; i < MAX_SHM_INSTANCES; i++) { - - shmdev_lyr[i].shm_base_addr = shm_start; - shmdev_lyr[i].shm_total_sz = shm_size; - - if (((char *)shmdev_lyr[i].shm_base_addr == NULL) - || (shmdev_lyr[i].shm_total_sz <= 0)) { - pr_warn("ERROR," - "Shared memory Address and/or Size incorrect" - ", Bailing out ...\n"); - result = -EINVAL; - goto clean; - } - - pr_info("SHM AREA (instance %d) STARTS" - " AT %p\n", i, (char *)shmdev_lyr[i].shm_base_addr); - - shmdev_lyr[i].shm_id = i; - shmdev_lyr[i].pshmdev_mbxsend = shmdev_send_msg; - shmdev_lyr[i].pshmdev_mbxsetup = shmdev_mbx_setup; - - /* - * Finally, CAIF core module is called with details in place: - * 1. SHM base address - * 2. SHM size - * 3. MBX handle - */ - result = caif_shmcore_probe(&shmdev_lyr[i]); - if (result) { - pr_warn("ERROR[%d]," - "Could not probe SHM core (instance %d)" - " Bailing out ...\n", result, i); - goto clean; - } - } - - return 0; - -clean: - /* - * For now, we assume that even if one instance of SHM fails, we bail - * out of the driver support completely. For this, we need to release - * any memory allocated and unregister any instance of SHM net device. - */ - for (i = 0; i < MAX_SHM_INSTANCES; i++) { - if (shmdev_lyr[i].pshm_netdev) - unregister_netdev(shmdev_lyr[i].pshm_netdev); - } - return result; -} - -static void __exit caif_shmdev_exit(void) -{ - int i; - - for (i = 0; i < MAX_SHM_INSTANCES; i++) { - caif_shmcore_remove(shmdev_lyr[i].pshm_netdev); - kfree((void *)shmdev_lyr[i].shm_base_addr); - } - -} - -module_init(caif_shmdev_init); -module_exit(caif_shmdev_exit); diff --git a/drivers/net/caif/caif_shmcore.c b/drivers/net/caif/caif_shmcore.c deleted file mode 100644 index cca2afc945a..00000000000 --- a/drivers/net/caif/caif_shmcore.c +++ /dev/null @@ -1,744 +0,0 @@ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com - * Authors: Amarnath Revanna / amarnath.bangalore.revanna@stericsson.com, - * Daniel Martensson / daniel.martensson@stericsson.com - * License terms: GNU General Public License (GPL) version 2 - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt - -#include -#include -#include -#include -#include -#include - -#include -#include - -#define NR_TX_BUF 6 -#define NR_RX_BUF 6 -#define TX_BUF_SZ 0x2000 -#define RX_BUF_SZ 0x2000 - -#define CAIF_NEEDED_HEADROOM 32 - -#define CAIF_FLOW_ON 1 -#define CAIF_FLOW_OFF 0 - -#define LOW_WATERMARK 3 -#define HIGH_WATERMARK 4 - -/* Maximum number of CAIF buffers per shared memory buffer. */ -#define SHM_MAX_FRMS_PER_BUF 10 - -/* - * Size in bytes of the descriptor area - * (With end of descriptor signalling) - */ -#define SHM_CAIF_DESC_SIZE ((SHM_MAX_FRMS_PER_BUF + 1) * \ - sizeof(struct shm_pck_desc)) - -/* - * Offset to the first CAIF frame within a shared memory buffer. - * Aligned on 32 bytes. - */ -#define SHM_CAIF_FRM_OFS (SHM_CAIF_DESC_SIZE + (SHM_CAIF_DESC_SIZE % 32)) - -/* Number of bytes for CAIF shared memory header. */ -#define SHM_HDR_LEN 1 - -/* Number of padding bytes for the complete CAIF frame. */ -#define SHM_FRM_PAD_LEN 4 - -#define CAIF_MAX_MTU 4096 - -#define SHM_SET_FULL(x) (((x+1) & 0x0F) << 0) -#define SHM_GET_FULL(x) (((x >> 0) & 0x0F) - 1) - -#define SHM_SET_EMPTY(x) (((x+1) & 0x0F) << 4) -#define SHM_GET_EMPTY(x) (((x >> 4) & 0x0F) - 1) - -#define SHM_FULL_MASK (0x0F << 0) -#define SHM_EMPTY_MASK (0x0F << 4) - -struct shm_pck_desc { - /* - * Offset from start of shared memory area to start of - * shared memory CAIF frame. - */ - u32 frm_ofs; - u32 frm_len; -}; - -struct buf_list { - unsigned char *desc_vptr; - u32 phy_addr; - u32 index; - u32 len; - u32 frames; - u32 frm_ofs; - struct list_head list; -}; - -struct shm_caif_frm { - /* Number of bytes of padding before the CAIF frame. */ - u8 hdr_ofs; -}; - -struct shmdrv_layer { - /* caif_dev_common must always be first in the structure*/ - struct caif_dev_common cfdev; - - u32 shm_tx_addr; - u32 shm_rx_addr; - u32 shm_base_addr; - u32 tx_empty_available; - spinlock_t lock; - - struct list_head tx_empty_list; - struct list_head tx_pend_list; - struct list_head tx_full_list; - struct list_head rx_empty_list; - struct list_head rx_pend_list; - struct list_head rx_full_list; - - struct workqueue_struct *pshm_tx_workqueue; - struct workqueue_struct *pshm_rx_workqueue; - - struct work_struct shm_tx_work; - struct work_struct shm_rx_work; - - struct sk_buff_head sk_qhead; - struct shmdev_layer *pshm_dev; -}; - -static int shm_netdev_open(struct net_device *shm_netdev) -{ - netif_wake_queue(shm_netdev); - return 0; -} - -static int shm_netdev_close(struct net_device *shm_netdev) -{ - netif_stop_queue(shm_netdev); - return 0; -} - -int caif_shmdrv_rx_cb(u32 mbx_msg, void *priv) -{ - struct buf_list *pbuf; - struct shmdrv_layer *pshm_drv; - struct list_head *pos; - u32 avail_emptybuff = 0; - unsigned long flags = 0; - - pshm_drv = priv; - - /* Check for received buffers. */ - if (mbx_msg & SHM_FULL_MASK) { - int idx; - - spin_lock_irqsave(&pshm_drv->lock, flags); - - /* Check whether we have any outstanding buffers. */ - if (list_empty(&pshm_drv->rx_empty_list)) { - - /* Release spin lock. */ - spin_unlock_irqrestore(&pshm_drv->lock, flags); - - /* We print even in IRQ context... */ - pr_warn("No empty Rx buffers to fill: " - "mbx_msg:%x\n", mbx_msg); - - /* Bail out. */ - goto err_sync; - } - - pbuf = - list_entry(pshm_drv->rx_empty_list.next, - struct buf_list, list); - idx = pbuf->index; - - /* Check buffer synchronization. */ - if (idx != SHM_GET_FULL(mbx_msg)) { - - /* We print even in IRQ context... */ - pr_warn( - "phyif_shm_mbx_msg_cb: RX full out of sync:" - " idx:%d, msg:%x SHM_GET_FULL(mbx_msg):%x\n", - idx, mbx_msg, SHM_GET_FULL(mbx_msg)); - - spin_unlock_irqrestore(&pshm_drv->lock, flags); - - /* Bail out. */ - goto err_sync; - } - - list_del_init(&pbuf->list); - list_add_tail(&pbuf->list, &pshm_drv->rx_full_list); - - spin_unlock_irqrestore(&pshm_drv->lock, flags); - - /* Schedule RX work queue. */ - if (!work_pending(&pshm_drv->shm_rx_work)) - queue_work(pshm_drv->pshm_rx_workqueue, - &pshm_drv->shm_rx_work); - } - - /* Check for emptied buffers. */ - if (mbx_msg & SHM_EMPTY_MASK) { - int idx; - - spin_lock_irqsave(&pshm_drv->lock, flags); - - /* Check whether we have any outstanding buffers. */ - if (list_empty(&pshm_drv->tx_full_list)) { - - /* We print even in IRQ context... */ - pr_warn("No TX to empty: msg:%x\n", mbx_msg); - - spin_unlock_irqrestore(&pshm_drv->lock, flags); - - /* Bail out. */ - goto err_sync; - } - - pbuf = - list_entry(pshm_drv->tx_full_list.next, - struct buf_list, list); - idx = pbuf->index; - - /* Check buffer synchronization. */ - if (idx != SHM_GET_EMPTY(mbx_msg)) { - - spin_unlock_irqrestore(&pshm_drv->lock, flags); - - /* We print even in IRQ context... */ - pr_warn("TX empty " - "out of sync:idx:%d, msg:%x\n", idx, mbx_msg); - - /* Bail out. */ - goto err_sync; - } - list_del_init(&pbuf->list); - - /* Reset buffer parameters. */ - pbuf->frames = 0; - pbuf->frm_ofs = SHM_CAIF_FRM_OFS; - - list_add_tail(&pbuf->list, &pshm_drv->tx_empty_list); - - /* Check the available no. of buffers in the empty list */ - list_for_each(pos, &pshm_drv->tx_empty_list) - avail_emptybuff++; - - /* Check whether we have to wake up the transmitter. */ - if ((avail_emptybuff > HIGH_WATERMARK) && - (!pshm_drv->tx_empty_available)) { - pshm_drv->tx_empty_available = 1; - spin_unlock_irqrestore(&pshm_drv->lock, flags); - pshm_drv->cfdev.flowctrl - (pshm_drv->pshm_dev->pshm_netdev, - CAIF_FLOW_ON); - - - /* Schedule the work queue. if required */ - if (!work_pending(&pshm_drv->shm_tx_work)) - queue_work(pshm_drv->pshm_tx_workqueue, - &pshm_drv->shm_tx_work); - } else - spin_unlock_irqrestore(&pshm_drv->lock, flags); - } - - return 0; - -err_sync: - return -EIO; -} - -static void shm_rx_work_func(struct work_struct *rx_work) -{ - struct shmdrv_layer *pshm_drv; - struct buf_list *pbuf; - unsigned long flags = 0; - struct sk_buff *skb; - char *p; - int ret; - - pshm_drv = container_of(rx_work, struct shmdrv_layer, shm_rx_work); - - while (1) { - - struct shm_pck_desc *pck_desc; - - spin_lock_irqsave(&pshm_drv->lock, flags); - - /* Check for received buffers. */ - if (list_empty(&pshm_drv->rx_full_list)) { - spin_unlock_irqrestore(&pshm_drv->lock, flags); - break; - } - - pbuf = - list_entry(pshm_drv->rx_full_list.next, struct buf_list, - list); - list_del_init(&pbuf->list); - spin_unlock_irqrestore(&pshm_drv->lock, flags); - - /* Retrieve pointer to start of the packet descriptor area. */ - pck_desc = (struct shm_pck_desc *) pbuf->desc_vptr; - - /* - * Check whether descriptor contains a CAIF shared memory - * frame. - */ - while (pck_desc->frm_ofs) { - unsigned int frm_buf_ofs; - unsigned int frm_pck_ofs; - unsigned int frm_pck_len; - /* - * Check whether offset is within buffer limits - * (lower). - */ - if (pck_desc->frm_ofs < - (pbuf->phy_addr - pshm_drv->shm_base_addr)) - break; - /* - * Check whether offset is within buffer limits - * (higher). - */ - if (pck_desc->frm_ofs > - ((pbuf->phy_addr - pshm_drv->shm_base_addr) + - pbuf->len)) - break; - - /* Calculate offset from start of buffer. */ - frm_buf_ofs = - pck_desc->frm_ofs - (pbuf->phy_addr - - pshm_drv->shm_base_addr); - - /* - * Calculate offset and length of CAIF packet while - * taking care of the shared memory header. - */ - frm_pck_ofs = - frm_buf_ofs + SHM_HDR_LEN + - (*(pbuf->desc_vptr + frm_buf_ofs)); - frm_pck_len = - (pck_desc->frm_len - SHM_HDR_LEN - - (*(pbuf->desc_vptr + frm_buf_ofs))); - - /* Check whether CAIF packet is within buffer limits */ - if ((frm_pck_ofs + pck_desc->frm_len) > pbuf->len) - break; - - /* Get a suitable CAIF packet and copy in data. */ - skb = netdev_alloc_skb(pshm_drv->pshm_dev->pshm_netdev, - frm_pck_len + 1); - if (skb == NULL) - break; - - p = skb_put(skb, frm_pck_len); - memcpy(p, pbuf->desc_vptr + frm_pck_ofs, frm_pck_len); - - skb->protocol = htons(ETH_P_CAIF); - skb_reset_mac_header(skb); - skb->dev = pshm_drv->pshm_dev->pshm_netdev; - - /* Push received packet up the stack. */ - ret = netif_rx_ni(skb); - - if (!ret) { - pshm_drv->pshm_dev->pshm_netdev->stats. - rx_packets++; - pshm_drv->pshm_dev->pshm_netdev->stats. - rx_bytes += pck_desc->frm_len; - } else - ++pshm_drv->pshm_dev->pshm_netdev->stats. - rx_dropped; - /* Move to next packet descriptor. */ - pck_desc++; - } - - spin_lock_irqsave(&pshm_drv->lock, flags); - list_add_tail(&pbuf->list, &pshm_drv->rx_pend_list); - - spin_unlock_irqrestore(&pshm_drv->lock, flags); - - } - - /* Schedule the work queue. if required */ - if (!work_pending(&pshm_drv->shm_tx_work)) - queue_work(pshm_drv->pshm_tx_workqueue, &pshm_drv->shm_tx_work); - -} - -static void shm_tx_work_func(struct work_struct *tx_work) -{ - u32 mbox_msg; - unsigned int frmlen, avail_emptybuff, append = 0; - unsigned long flags = 0; - struct buf_list *pbuf = NULL; - struct shmdrv_layer *pshm_drv; - struct shm_caif_frm *frm; - struct sk_buff *skb; - struct shm_pck_desc *pck_desc; - struct list_head *pos; - - pshm_drv = container_of(tx_work, struct shmdrv_layer, shm_tx_work); - - do { - /* Initialize mailbox message. */ - mbox_msg = 0x00; - avail_emptybuff = 0; - - spin_lock_irqsave(&pshm_drv->lock, flags); - - /* Check for pending receive buffers. */ - if (!list_empty(&pshm_drv->rx_pend_list)) { - - pbuf = list_entry(pshm_drv->rx_pend_list.next, - struct buf_list, list); - - list_del_init(&pbuf->list); - list_add_tail(&pbuf->list, &pshm_drv->rx_empty_list); - /* - * Value index is never changed, - * so read access should be safe. - */ - mbox_msg |= SHM_SET_EMPTY(pbuf->index); - } - - skb = skb_peek(&pshm_drv->sk_qhead); - - if (skb == NULL) - goto send_msg; - /* Check the available no. of buffers in the empty list */ - list_for_each(pos, &pshm_drv->tx_empty_list) - avail_emptybuff++; - - if ((avail_emptybuff < LOW_WATERMARK) && - pshm_drv->tx_empty_available) { - /* Update blocking condition. */ - pshm_drv->tx_empty_available = 0; - spin_unlock_irqrestore(&pshm_drv->lock, flags); - pshm_drv->cfdev.flowctrl - (pshm_drv->pshm_dev->pshm_netdev, - CAIF_FLOW_OFF); - spin_lock_irqsave(&pshm_drv->lock, flags); - } - /* - * We simply return back to the caller if we do not have space - * either in Tx pending list or Tx empty list. In this case, - * we hold the received skb in the skb list, waiting to - * be transmitted once Tx buffers become available - */ - if (list_empty(&pshm_drv->tx_empty_list)) - goto send_msg; - - /* Get the first free Tx buffer. */ - pbuf = list_entry(pshm_drv->tx_empty_list.next, - struct buf_list, list); - do { - if (append) { - skb = skb_peek(&pshm_drv->sk_qhead); - if (skb == NULL) - break; - } - - frm = (struct shm_caif_frm *) - (pbuf->desc_vptr + pbuf->frm_ofs); - - frm->hdr_ofs = 0; - frmlen = 0; - frmlen += SHM_HDR_LEN + frm->hdr_ofs + skb->len; - - /* Add tail padding if needed. */ - if (frmlen % SHM_FRM_PAD_LEN) - frmlen += SHM_FRM_PAD_LEN - - (frmlen % SHM_FRM_PAD_LEN); - - /* - * Verify that packet, header and additional padding - * can fit within the buffer frame area. - */ - if (frmlen >= (pbuf->len - pbuf->frm_ofs)) - break; - - if (!append) { - list_del_init(&pbuf->list); - append = 1; - } - - skb = skb_dequeue(&pshm_drv->sk_qhead); - if (skb == NULL) - break; - /* Copy in CAIF frame. */ - skb_copy_bits(skb, 0, pbuf->desc_vptr + - pbuf->frm_ofs + SHM_HDR_LEN + - frm->hdr_ofs, skb->len); - - pshm_drv->pshm_dev->pshm_netdev->stats.tx_packets++; - pshm_drv->pshm_dev->pshm_netdev->stats.tx_bytes += - frmlen; - dev_kfree_skb_irq(skb); - - /* Fill in the shared memory packet descriptor area. */ - pck_desc = (struct shm_pck_desc *) (pbuf->desc_vptr); - /* Forward to current frame. */ - pck_desc += pbuf->frames; - pck_desc->frm_ofs = (pbuf->phy_addr - - pshm_drv->shm_base_addr) + - pbuf->frm_ofs; - pck_desc->frm_len = frmlen; - /* Terminate packet descriptor area. */ - pck_desc++; - pck_desc->frm_ofs = 0; - /* Update buffer parameters. */ - pbuf->frames++; - pbuf->frm_ofs += frmlen + (frmlen % 32); - - } while (pbuf->frames < SHM_MAX_FRMS_PER_BUF); - - /* Assign buffer as full. */ - list_add_tail(&pbuf->list, &pshm_drv->tx_full_list); - append = 0; - mbox_msg |= SHM_SET_FULL(pbuf->index); -send_msg: - spin_unlock_irqrestore(&pshm_drv->lock, flags); - - if (mbox_msg) - pshm_drv->pshm_dev->pshmdev_mbxsend - (pshm_drv->pshm_dev->shm_id, mbox_msg); - } while (mbox_msg); -} - -static int shm_netdev_tx(struct sk_buff *skb, struct net_device *shm_netdev) -{ - struct shmdrv_layer *pshm_drv; - - pshm_drv = netdev_priv(shm_netdev); - - skb_queue_tail(&pshm_drv->sk_qhead, skb); - - /* Schedule Tx work queue. for deferred processing of skbs*/ - if (!work_pending(&pshm_drv->shm_tx_work)) - queue_work(pshm_drv->pshm_tx_workqueue, &pshm_drv->shm_tx_work); - - return 0; -} - -static const struct net_device_ops netdev_ops = { - .ndo_open = shm_netdev_open, - .ndo_stop = shm_netdev_close, - .ndo_start_xmit = shm_netdev_tx, -}; - -static void shm_netdev_setup(struct net_device *pshm_netdev) -{ - struct shmdrv_layer *pshm_drv; - pshm_netdev->netdev_ops = &netdev_ops; - - pshm_netdev->mtu = CAIF_MAX_MTU; - pshm_netdev->type = ARPHRD_CAIF; - pshm_netdev->hard_header_len = CAIF_NEEDED_HEADROOM; - pshm_netdev->tx_queue_len = 0; - pshm_netdev->destructor = free_netdev; - - pshm_drv = netdev_priv(pshm_netdev); - - /* Initialize structures in a clean state. */ - memset(pshm_drv, 0, sizeof(struct shmdrv_layer)); - - pshm_drv->cfdev.link_select = CAIF_LINK_LOW_LATENCY; -} - -int caif_shmcore_probe(struct shmdev_layer *pshm_dev) -{ - int result, j; - struct shmdrv_layer *pshm_drv = NULL; - - pshm_dev->pshm_netdev = alloc_netdev(sizeof(struct shmdrv_layer), - "cfshm%d", shm_netdev_setup); - if (!pshm_dev->pshm_netdev) - return -ENOMEM; - - pshm_drv = netdev_priv(pshm_dev->pshm_netdev); - pshm_drv->pshm_dev = pshm_dev; - - /* - * Initialization starts with the verification of the - * availability of MBX driver by calling its setup function. - * MBX driver must be available by this time for proper - * functioning of SHM driver. - */ - if ((pshm_dev->pshmdev_mbxsetup - (caif_shmdrv_rx_cb, pshm_dev, pshm_drv)) != 0) { - pr_warn("Could not config. SHM Mailbox," - " Bailing out.....\n"); - free_netdev(pshm_dev->pshm_netdev); - return -ENODEV; - } - - skb_queue_head_init(&pshm_drv->sk_qhead); - - pr_info("SHM DEVICE[%d] PROBED BY DRIVER, NEW SHM DRIVER" - " INSTANCE AT pshm_drv =0x%p\n", - pshm_drv->pshm_dev->shm_id, pshm_drv); - - if (pshm_dev->shm_total_sz < - (NR_TX_BUF * TX_BUF_SZ + NR_RX_BUF * RX_BUF_SZ)) { - - pr_warn("ERROR, Amount of available" - " Phys. SHM cannot accommodate current SHM " - "driver configuration, Bailing out ...\n"); - free_netdev(pshm_dev->pshm_netdev); - return -ENOMEM; - } - - pshm_drv->shm_base_addr = pshm_dev->shm_base_addr; - pshm_drv->shm_tx_addr = pshm_drv->shm_base_addr; - - if (pshm_dev->shm_loopback) - pshm_drv->shm_rx_addr = pshm_drv->shm_tx_addr; - else - pshm_drv->shm_rx_addr = pshm_dev->shm_base_addr + - (NR_TX_BUF * TX_BUF_SZ); - - spin_lock_init(&pshm_drv->lock); - INIT_LIST_HEAD(&pshm_drv->tx_empty_list); - INIT_LIST_HEAD(&pshm_drv->tx_pend_list); - INIT_LIST_HEAD(&pshm_drv->tx_full_list); - - INIT_LIST_HEAD(&pshm_drv->rx_empty_list); - INIT_LIST_HEAD(&pshm_drv->rx_pend_list); - INIT_LIST_HEAD(&pshm_drv->rx_full_list); - - INIT_WORK(&pshm_drv->shm_tx_work, shm_tx_work_func); - INIT_WORK(&pshm_drv->shm_rx_work, shm_rx_work_func); - - pshm_drv->pshm_tx_workqueue = - create_singlethread_workqueue("shm_tx_work"); - pshm_drv->pshm_rx_workqueue = - create_singlethread_workqueue("shm_rx_work"); - - for (j = 0; j < NR_TX_BUF; j++) { - struct buf_list *tx_buf = - kmalloc(sizeof(struct buf_list), GFP_KERNEL); - - if (tx_buf == NULL) { - free_netdev(pshm_dev->pshm_netdev); - return -ENOMEM; - } - tx_buf->index = j; - tx_buf->phy_addr = pshm_drv->shm_tx_addr + (TX_BUF_SZ * j); - tx_buf->len = TX_BUF_SZ; - tx_buf->frames = 0; - tx_buf->frm_ofs = SHM_CAIF_FRM_OFS; - - if (pshm_dev->shm_loopback) - tx_buf->desc_vptr = (unsigned char *)tx_buf->phy_addr; - else - /* - * FIXME: the result of ioremap is not a pointer - arnd - */ - tx_buf->desc_vptr = - ioremap(tx_buf->phy_addr, TX_BUF_SZ); - - list_add_tail(&tx_buf->list, &pshm_drv->tx_empty_list); - } - - for (j = 0; j < NR_RX_BUF; j++) { - struct buf_list *rx_buf = - kmalloc(sizeof(struct buf_list), GFP_KERNEL); - - if (rx_buf == NULL) { - free_netdev(pshm_dev->pshm_netdev); - return -ENOMEM; - } - rx_buf->index = j; - rx_buf->phy_addr = pshm_drv->shm_rx_addr + (RX_BUF_SZ * j); - rx_buf->len = RX_BUF_SZ; - - if (pshm_dev->shm_loopback) - rx_buf->desc_vptr = (unsigned char *)rx_buf->phy_addr; - else - rx_buf->desc_vptr = - ioremap(rx_buf->phy_addr, RX_BUF_SZ); - list_add_tail(&rx_buf->list, &pshm_drv->rx_empty_list); - } - - pshm_drv->tx_empty_available = 1; - result = register_netdev(pshm_dev->pshm_netdev); - if (result) - pr_warn("ERROR[%d], SHM could not, " - "register with NW FRMWK Bailing out ...\n", result); - - return result; -} - -void caif_shmcore_remove(struct net_device *pshm_netdev) -{ - struct buf_list *pbuf; - struct shmdrv_layer *pshm_drv = NULL; - - pshm_drv = netdev_priv(pshm_netdev); - - while (!(list_empty(&pshm_drv->tx_pend_list))) { - pbuf = - list_entry(pshm_drv->tx_pend_list.next, - struct buf_list, list); - - list_del(&pbuf->list); - kfree(pbuf); - } - - while (!(list_empty(&pshm_drv->tx_full_list))) { - pbuf = - list_entry(pshm_drv->tx_full_list.next, - struct buf_list, list); - list_del(&pbuf->list); - kfree(pbuf); - } - - while (!(list_empty(&pshm_drv->tx_empty_list))) { - pbuf = - list_entry(pshm_drv->tx_empty_list.next, - struct buf_list, list); - list_del(&pbuf->list); - kfree(pbuf); - } - - while (!(list_empty(&pshm_drv->rx_full_list))) { - pbuf = - list_entry(pshm_drv->tx_full_list.next, - struct buf_list, list); - list_del(&pbuf->list); - kfree(pbuf); - } - - while (!(list_empty(&pshm_drv->rx_pend_list))) { - pbuf = - list_entry(pshm_drv->tx_pend_list.next, - struct buf_list, list); - list_del(&pbuf->list); - kfree(pbuf); - } - - while (!(list_empty(&pshm_drv->rx_empty_list))) { - pbuf = - list_entry(pshm_drv->rx_empty_list.next, - struct buf_list, list); - list_del(&pbuf->list); - kfree(pbuf); - } - - /* Destroy work queues. */ - destroy_workqueue(pshm_drv->pshm_tx_workqueue); - destroy_workqueue(pshm_drv->pshm_rx_workqueue); - - unregister_netdev(pshm_netdev); -} diff --git a/include/net/caif/caif_shm.h b/include/net/caif/caif_shm.h deleted file mode 100644 index 5bcce55438c..00000000000 --- a/include/net/caif/caif_shm.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) ST-Ericsson AB 2010 - * Contact: Sjur Brendeland / sjur.brandeland@stericsson.com - * Author: Amarnath Revanna / amarnath.bangalore.revanna@stericsson.com - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef CAIF_SHM_H_ -#define CAIF_SHM_H_ - -struct shmdev_layer { - u32 shm_base_addr; - u32 shm_total_sz; - u32 shm_id; - u32 shm_loopback; - void *hmbx; - int (*pshmdev_mbxsend) (u32 shm_id, u32 mbx_msg); - int (*pshmdev_mbxsetup) (void *pshmdrv_cb, - struct shmdev_layer *pshm_dev, void *pshm_drv); - struct net_device *pshm_netdev; -}; - -extern int caif_shmcore_probe(struct shmdev_layer *pshm_dev); -extern void caif_shmcore_remove(struct net_device *pshm_netdev); - -#endif -- cgit v1.2.3-70-g09d2 From 6681712d67eef14c4ce793561c3231659153a320 Mon Sep 17 00:00:00 2001 From: David Stevens Date: Fri, 15 Mar 2013 04:35:51 +0000 Subject: vxlan: generalize forwarding tables This patch generalizes VXLAN forwarding table entries allowing an administrator to: 1) specify multiple destinations for a given MAC 2) specify alternate vni's in the VXLAN header 3) specify alternate destination UDP ports 4) use multicast MAC addresses as fdb lookup keys 5) specify multicast destinations 6) specify the outgoing interface for forwarded packets The combination allows configuration of more complex topologies using VXLAN encapsulation. Changes since v1: rebase to 3.9.0-rc2 Signed-Off-By: David L Stevens Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 263 ++++++++++++++++++++++++++++++++--------- include/uapi/linux/neighbour.h | 3 + net/core/rtnetlink.c | 2 +- 3 files changed, 210 insertions(+), 58 deletions(-) (limited to 'include') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index db0df07c18d..33427fd6251 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -81,13 +81,22 @@ struct vxlan_net { struct hlist_head vni_list[VNI_HASH_SIZE]; }; +struct vxlan_rdst { + struct rcu_head rcu; + __be32 remote_ip; + __be16 remote_port; + u32 remote_vni; + u32 remote_ifindex; + struct vxlan_rdst *remote_next; +}; + /* Forwarding table entry */ struct vxlan_fdb { struct hlist_node hlist; /* linked list of entries */ struct rcu_head rcu; unsigned long updated; /* jiffies */ unsigned long used; - __be32 remote_ip; + struct vxlan_rdst remote; u16 state; /* see ndm_state */ u8 eth_addr[ETH_ALEN]; }; @@ -157,7 +166,8 @@ static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id) /* Fill in neighbour message in skbuff. */ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, const struct vxlan_fdb *fdb, - u32 portid, u32 seq, int type, unsigned int flags) + u32 portid, u32 seq, int type, unsigned int flags, + const struct vxlan_rdst *rdst) { unsigned long now = jiffies; struct nda_cacheinfo ci; @@ -176,7 +186,7 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, if (type == RTM_GETNEIGH) { ndm->ndm_family = AF_INET; - send_ip = fdb->remote_ip != 0; + send_ip = rdst->remote_ip != htonl(INADDR_ANY); send_eth = !is_zero_ether_addr(fdb->eth_addr); } else ndm->ndm_family = AF_BRIDGE; @@ -188,7 +198,17 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, if (send_eth && nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->eth_addr)) goto nla_put_failure; - if (send_ip && nla_put_be32(skb, NDA_DST, fdb->remote_ip)) + if (send_ip && nla_put_be32(skb, NDA_DST, rdst->remote_ip)) + goto nla_put_failure; + + if (rdst->remote_port && rdst->remote_port != vxlan_port && + nla_put_be16(skb, NDA_PORT, rdst->remote_port)) + goto nla_put_failure; + if (rdst->remote_vni != vxlan->vni && + nla_put_be32(skb, NDA_VNI, rdst->remote_vni)) + goto nla_put_failure; + if (rdst->remote_ifindex && + nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex)) goto nla_put_failure; ci.ndm_used = jiffies_to_clock_t(now - fdb->used); @@ -211,6 +231,9 @@ static inline size_t vxlan_nlmsg_size(void) return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ + nla_total_size(sizeof(__be32)) /* NDA_DST */ + + nla_total_size(sizeof(__be32)) /* NDA_PORT */ + + nla_total_size(sizeof(__be32)) /* NDA_VNI */ + + nla_total_size(sizeof(__u32)) /* NDA_IFINDEX */ + nla_total_size(sizeof(struct nda_cacheinfo)); } @@ -225,7 +248,7 @@ static void vxlan_fdb_notify(struct vxlan_dev *vxlan, if (skb == NULL) goto errout; - err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0); + err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, &fdb->remote); if (err < 0) { /* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); @@ -247,7 +270,8 @@ static void vxlan_ip_miss(struct net_device *dev, __be32 ipa) memset(&f, 0, sizeof f); f.state = NUD_STALE; - f.remote_ip = ipa; /* goes to NDA_DST */ + f.remote.remote_ip = ipa; /* goes to NDA_DST */ + f.remote.remote_vni = VXLAN_N_VID; vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH); } @@ -300,10 +324,38 @@ static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan, return NULL; } +/* Add/update destinations for multicast */ +static int vxlan_fdb_append(struct vxlan_fdb *f, + __be32 ip, __u32 port, __u32 vni, __u32 ifindex) +{ + struct vxlan_rdst *rd_prev, *rd; + + rd_prev = NULL; + for (rd = &f->remote; rd; rd = rd->remote_next) { + if (rd->remote_ip == ip && + rd->remote_port == port && + rd->remote_vni == vni && + rd->remote_ifindex == ifindex) + return 0; + rd_prev = rd; + } + rd = kmalloc(sizeof(*rd), GFP_ATOMIC); + if (rd == NULL) + return -ENOBUFS; + rd->remote_ip = ip; + rd->remote_port = port; + rd->remote_vni = vni; + rd->remote_ifindex = ifindex; + rd->remote_next = NULL; + rd_prev->remote_next = rd; + return 1; +} + /* Add new entry to forwarding table -- assumes lock held */ static int vxlan_fdb_create(struct vxlan_dev *vxlan, const u8 *mac, __be32 ip, - __u16 state, __u16 flags) + __u16 state, __u16 flags, + __u32 port, __u32 vni, __u32 ifindex) { struct vxlan_fdb *f; int notify = 0; @@ -320,6 +372,14 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan, f->updated = jiffies; notify = 1; } + if ((flags & NLM_F_APPEND) && + is_multicast_ether_addr(f->eth_addr)) { + int rc = vxlan_fdb_append(f, ip, port, vni, ifindex); + + if (rc < 0) + return rc; + notify |= rc; + } } else { if (!(flags & NLM_F_CREATE)) return -ENOENT; @@ -333,7 +393,11 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan, return -ENOMEM; notify = 1; - f->remote_ip = ip; + f->remote.remote_ip = ip; + f->remote.remote_port = port; + f->remote.remote_vni = vni; + f->remote.remote_ifindex = ifindex; + f->remote.remote_next = NULL; f->state = state; f->updated = f->used = jiffies; memcpy(f->eth_addr, mac, ETH_ALEN); @@ -349,6 +413,19 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan, return 0; } +void vxlan_fdb_free(struct rcu_head *head) +{ + struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu); + + while (f->remote.remote_next) { + struct vxlan_rdst *rd = f->remote.remote_next; + + f->remote.remote_next = rd->remote_next; + kfree(rd); + } + kfree(f); +} + static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f) { netdev_dbg(vxlan->dev, @@ -358,7 +435,7 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f) vxlan_fdb_notify(vxlan, f, RTM_DELNEIGH); hlist_del_rcu(&f->hlist); - kfree_rcu(f, rcu); + call_rcu(&f->rcu, vxlan_fdb_free); } /* Add static entry (via netlink) */ @@ -367,7 +444,9 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], const unsigned char *addr, u16 flags) { struct vxlan_dev *vxlan = netdev_priv(dev); + struct net *net = dev_net(vxlan->dev); __be32 ip; + u32 port, vni, ifindex; int err; if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_REACHABLE))) { @@ -384,8 +463,36 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ip = nla_get_be32(tb[NDA_DST]); + if (tb[NDA_PORT]) { + if (nla_len(tb[NDA_PORT]) != sizeof(u32)) + return -EINVAL; + port = nla_get_u32(tb[NDA_PORT]); + } else + port = vxlan_port; + + if (tb[NDA_VNI]) { + if (nla_len(tb[NDA_VNI]) != sizeof(u32)) + return -EINVAL; + vni = nla_get_u32(tb[NDA_VNI]); + } else + vni = vxlan->vni; + + if (tb[NDA_IFINDEX]) { + struct net_device *dev; + + if (nla_len(tb[NDA_IFINDEX]) != sizeof(u32)) + return -EINVAL; + ifindex = nla_get_u32(tb[NDA_IFINDEX]); + dev = dev_get_by_index(net, ifindex); + if (!dev) + return -EADDRNOTAVAIL; + dev_put(dev); + } else + ifindex = 0; + spin_lock_bh(&vxlan->hash_lock); - err = vxlan_fdb_create(vxlan, addr, ip, ndm->ndm_state, flags); + err = vxlan_fdb_create(vxlan, addr, ip, ndm->ndm_state, flags, port, + vni, ifindex); spin_unlock_bh(&vxlan->hash_lock); return err; @@ -423,18 +530,21 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, int err; hlist_for_each_entry_rcu(f, &vxlan->fdb_head[h], hlist) { - if (idx < cb->args[0]) - goto skip; - - err = vxlan_fdb_info(skb, vxlan, f, - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, - RTM_NEWNEIGH, - NLM_F_MULTI); - if (err < 0) - break; + struct vxlan_rdst *rd; + for (rd = &f->remote; rd; rd = rd->remote_next) { + if (idx < cb->args[0]) + goto skip; + + err = vxlan_fdb_info(skb, vxlan, f, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + RTM_NEWNEIGH, + NLM_F_MULTI, rd); + if (err < 0) + break; skip: - ++idx; + ++idx; + } } } @@ -454,22 +564,23 @@ static void vxlan_snoop(struct net_device *dev, f = vxlan_find_mac(vxlan, src_mac); if (likely(f)) { f->used = jiffies; - if (likely(f->remote_ip == src_ip)) + if (likely(f->remote.remote_ip == src_ip)) return; if (net_ratelimit()) netdev_info(dev, "%pM migrated from %pI4 to %pI4\n", - src_mac, &f->remote_ip, &src_ip); + src_mac, &f->remote.remote_ip, &src_ip); - f->remote_ip = src_ip; + f->remote.remote_ip = src_ip; f->updated = jiffies; } else { /* learned new entry */ spin_lock(&vxlan->hash_lock); err = vxlan_fdb_create(vxlan, src_mac, src_ip, NUD_REACHABLE, - NLM_F_EXCL|NLM_F_CREATE); + NLM_F_EXCL|NLM_F_CREATE, + vxlan_port, vxlan->vni, 0); spin_unlock(&vxlan->hash_lock); } } @@ -701,7 +812,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb) } f = vxlan_find_mac(vxlan, n->ha); - if (f && f->remote_ip == 0) { + if (f && f->remote.remote_ip == htonl(INADDR_ANY)) { /* bridge-local neighbor */ neigh_release(n); goto out; @@ -834,47 +945,26 @@ static int handle_offloads(struct sk_buff *skb) return 0; } -/* Transmit local packets over Vxlan - * - * Outer IP header inherits ECN and DF from inner header. - * Outer UDP destination is the VXLAN assigned port. - * source port is based on hash of flow - */ -static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, + struct vxlan_rdst *rdst, bool did_rsc) { struct vxlan_dev *vxlan = netdev_priv(dev); struct rtable *rt; const struct iphdr *old_iph; - struct ethhdr *eth; struct iphdr *iph; struct vxlanhdr *vxh; struct udphdr *uh; struct flowi4 fl4; unsigned int pkt_len = skb->len; __be32 dst; - __u16 src_port; + __u16 src_port, dst_port; + u32 vni; __be16 df = 0; __u8 tos, ttl; - bool did_rsc = false; - const struct vxlan_fdb *f; - - skb_reset_mac_header(skb); - eth = eth_hdr(skb); - - if ((vxlan->flags & VXLAN_F_PROXY) && ntohs(eth->h_proto) == ETH_P_ARP) - return arp_reduce(dev, skb); - else if ((vxlan->flags&VXLAN_F_RSC) && ntohs(eth->h_proto) == ETH_P_IP) - did_rsc = route_shortcircuit(dev, skb); - f = vxlan_find_mac(vxlan, eth->h_dest); - if (f == NULL) { - did_rsc = false; - dst = vxlan->gaddr; - if (!dst && (vxlan->flags & VXLAN_F_L2MISS) && - !is_multicast_ether_addr(eth->h_dest)) - vxlan_fdb_miss(vxlan, eth->h_dest); - } else - dst = f->remote_ip; + dst_port = rdst->remote_port ? rdst->remote_port : vxlan_port; + vni = rdst->remote_vni; + dst = rdst->remote_ip; if (!dst) { if (did_rsc) { @@ -922,7 +1012,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) src_port = vxlan_src_port(vxlan, skb); memset(&fl4, 0, sizeof(fl4)); - fl4.flowi4_oif = vxlan->link; + fl4.flowi4_oif = rdst->remote_ifindex; fl4.flowi4_tos = RT_TOS(tos); fl4.daddr = dst; fl4.saddr = vxlan->saddr; @@ -949,13 +1039,13 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); vxh->vx_flags = htonl(VXLAN_FLAGS); - vxh->vx_vni = htonl(vxlan->vni << 8); + vxh->vx_vni = htonl(vni << 8); __skb_push(skb, sizeof(*uh)); skb_reset_transport_header(skb); uh = udp_hdr(skb); - uh->dest = htons(vxlan_port); + uh->dest = htons(dst_port); uh->source = htons(src_port); uh->len = htons(skb->len); @@ -995,6 +1085,64 @@ tx_free: return NETDEV_TX_OK; } +/* Transmit local packets over Vxlan + * + * Outer IP header inherits ECN and DF from inner header. + * Outer UDP destination is the VXLAN assigned port. + * source port is based on hash of flow + */ +static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct vxlan_dev *vxlan = netdev_priv(dev); + struct ethhdr *eth; + bool did_rsc = false; + struct vxlan_rdst group, *rdst0, *rdst; + struct vxlan_fdb *f; + int rc1, rc; + + skb_reset_mac_header(skb); + eth = eth_hdr(skb); + + if ((vxlan->flags & VXLAN_F_PROXY) && ntohs(eth->h_proto) == ETH_P_ARP) + return arp_reduce(dev, skb); + else if ((vxlan->flags&VXLAN_F_RSC) && ntohs(eth->h_proto) == ETH_P_IP) + did_rsc = route_shortcircuit(dev, skb); + + f = vxlan_find_mac(vxlan, eth->h_dest); + if (f == NULL) { + did_rsc = false; + group.remote_port = vxlan_port; + group.remote_vni = vxlan->vni; + group.remote_ip = vxlan->gaddr; + group.remote_ifindex = vxlan->link; + group.remote_next = 0; + rdst0 = &group; + + if (group.remote_ip == htonl(INADDR_ANY) && + (vxlan->flags & VXLAN_F_L2MISS) && + !is_multicast_ether_addr(eth->h_dest)) + vxlan_fdb_miss(vxlan, eth->h_dest); + } else + rdst0 = &f->remote; + + rc = NETDEV_TX_OK; + + /* if there are multiple destinations, send copies */ + for (rdst = rdst0->remote_next; rdst; rdst = rdst->remote_next) { + struct sk_buff *skb1; + + skb1 = skb_clone(skb, GFP_ATOMIC); + rc1 = vxlan_xmit_one(skb1, dev, rdst, did_rsc); + if (rc == NETDEV_TX_OK) + rc = rc1; + } + + rc1 = vxlan_xmit_one(skb, dev, rdst0, did_rsc); + if (rc == NETDEV_TX_OK) + rc = rc1; + return rc; +} + /* Walk the forwarding table and purge stale entries */ static void vxlan_cleanup(unsigned long arg) { @@ -1558,6 +1706,7 @@ static void __exit vxlan_cleanup_module(void) { rtnl_link_unregister(&vxlan_link_ops); unregister_pernet_device(&vxlan_net_ops); + rcu_barrier(); } module_exit(vxlan_cleanup_module); diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h index adb068c53c4..f175212420a 100644 --- a/include/uapi/linux/neighbour.h +++ b/include/uapi/linux/neighbour.h @@ -21,6 +21,9 @@ enum { NDA_CACHEINFO, NDA_PROBES, NDA_VLAN, + NDA_PORT, + NDA_VNI, + NDA_IFINDEX, __NDA_MAX }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 55b5624a4b0..0e86baf8f80 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2112,7 +2112,7 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) } addr = nla_data(tb[NDA_LLADDR]); - if (!is_valid_ether_addr(addr)) { + if (is_zero_ether_addr(addr)) { pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ether address\n"); return -EINVAL; } -- cgit v1.2.3-70-g09d2 From a362db3d6c8a952cbde510b1fa35d0ee001b347e Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sat, 16 Mar 2013 04:47:55 +0000 Subject: net: fix some typos in netif features Cc: Pravin B Shelar Cc: "David S. Miller" Signed-off-by: Cong Wang Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- include/linux/netdev_features.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index f5e797c0c2a..d6ee2d008ee 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -102,8 +102,8 @@ enum { #define NETIF_F_VLAN_CHALLENGED __NETIF_F(VLAN_CHALLENGED) #define NETIF_F_RXFCS __NETIF_F(RXFCS) #define NETIF_F_RXALL __NETIF_F(RXALL) -#define NETIF_F_GRE_GSO __NETIF_F(GSO_GRE) -#define NETIF_F_UDP_TUNNEL __NETIF_F(UDP_TUNNEL) +#define NETIF_F_GSO_GRE __NETIF_F(GSO_GRE) +#define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL) /* Features valid for ethtool to change */ /* = all defined minus driver/device-class-related */ -- cgit v1.2.3-70-g09d2 From 1a2c6181c4a1922021b4d7df373bba612c3e5f04 Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Sun, 17 Mar 2013 08:23:34 +0000 Subject: tcp: Remove TCPCT TCPCT uses option-number 253, reserved for experimental use and should not be used in production environments. Further, TCPCT does not fully implement RFC 6013. As a nice side-effect, removing TCPCT increases TCP's performance for very short flows: Doing an apache-benchmark with -c 100 -n 100000, sending HTTP-requests for files of 1KB size. before this patch: average (among 7 runs) of 20845.5 Requests/Second after: average (among 7 runs) of 21403.6 Requests/Second Signed-off-by: Christoph Paasch Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 - drivers/infiniband/hw/cxgb4/cm.c | 2 +- include/linux/tcp.h | 10 -- include/net/request_sock.h | 8 +- include/net/tcp.h | 89 +---------- include/uapi/linux/tcp.h | 26 ---- net/dccp/ipv4.c | 5 +- net/dccp/ipv6.c | 5 +- net/ipv4/inet_connection_sock.c | 2 +- net/ipv4/syncookies.c | 3 +- net/ipv4/sysctl_net_ipv4.c | 7 - net/ipv4/tcp.c | 267 --------------------------------- net/ipv4/tcp_input.c | 69 +-------- net/ipv4/tcp_ipv4.c | 60 +------- net/ipv4/tcp_minisocks.c | 40 +---- net/ipv4/tcp_output.c | 219 +-------------------------- net/ipv6/syncookies.c | 3 +- net/ipv6/tcp_ipv6.c | 56 +------ 18 files changed, 38 insertions(+), 841 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 18a24c405ac..17953e2bc3e 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -175,14 +175,6 @@ tcp_congestion_control - STRING is inherited. [see setsockopt(listenfd, SOL_TCP, TCP_CONGESTION, "name" ...) ] -tcp_cookie_size - INTEGER - Default size of TCP Cookie Transactions (TCPCT) option, that may be - overridden on a per socket basis by the TCPCT socket option. - Values greater than the maximum (16) are interpreted as the maximum. - Values greater than zero and less than the minimum (8) are interpreted - as the minimum. Odd values are interpreted as the next even value. - Default: 0 (off). - tcp_dsack - BOOLEAN Allows TCP to send "duplicate" SACKs. diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 8dcc84fd9d3..54fd31fcc33 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2915,7 +2915,7 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos) */ memset(&tmp_opt, 0, sizeof(tmp_opt)); tcp_clear_options(&tmp_opt); - tcp_parse_options(skb, &tmp_opt, NULL, 0, NULL); + tcp_parse_options(skb, &tmp_opt, 0, NULL); req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req)); memset(req, 0, sizeof(*req)); diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 763c108ee03..ed6a7456eec 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -90,9 +90,6 @@ struct tcp_options_received { sack_ok : 4, /* SACK seen on SYN packet */ snd_wscale : 4, /* Window scaling received from sender */ rcv_wscale : 4; /* Window scaling to send to receiver */ - u8 cookie_plus:6, /* bytes in authenticator/cookie option */ - cookie_out_never:1, - cookie_in_always:1; u8 num_sacks; /* Number of SACK blocks */ u16 user_mss; /* mss requested by user in ioctl */ u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ @@ -102,7 +99,6 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt) { rx_opt->tstamp_ok = rx_opt->sack_ok = 0; rx_opt->wscale_ok = rx_opt->snd_wscale = 0; - rx_opt->cookie_plus = 0; } /* This is the max number of SACKS that we'll generate and process. It's safe @@ -320,12 +316,6 @@ struct tcp_sock { struct tcp_md5sig_info __rcu *md5sig_info; #endif - /* When the cookie options are generated and exchanged, then this - * object holds a reference to them (cookie_values->kref). Also - * contains related tcp_cookie_transactions fields. - */ - struct tcp_cookie_values *cookie_values; - /* TCP fastopen related information */ struct tcp_fastopen_request *fastopen_req; /* fastopen_rsk points to request_sock that resulted in this big diff --git a/include/net/request_sock.h b/include/net/request_sock.h index a51dbd17c2d..9069e65c1c5 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -27,19 +27,13 @@ struct sk_buff; struct dst_entry; struct proto; -/* empty to "strongly type" an otherwise void parameter. - */ -struct request_values { -}; - struct request_sock_ops { int family; int obj_size; struct kmem_cache *slab; char *slab_name; int (*rtx_syn_ack)(struct sock *sk, - struct request_sock *req, - struct request_values *rvp); + struct request_sock *req); void (*send_ack)(struct sock *sk, struct sk_buff *skb, struct request_sock *req); void (*send_reset)(struct sock *sk, diff --git a/include/net/tcp.h b/include/net/tcp.h index ab9f947b118..7f2f17198d7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -179,7 +179,6 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOPT_SACK 5 /* SACK Block */ #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ -#define TCPOPT_COOKIE 253 /* Cookie extension (experimental) */ #define TCPOPT_EXP 254 /* Experimental */ /* Magic number to be after the option value for sharing TCP * experimental options. See draft-ietf-tcpm-experimental-options-00.txt @@ -454,7 +453,7 @@ extern void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req); extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len); extern void tcp_parse_options(const struct sk_buff *skb, - struct tcp_options_received *opt_rx, const u8 **hvpp, + struct tcp_options_received *opt_rx, int estab, struct tcp_fastopen_cookie *foc); extern const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); @@ -476,7 +475,6 @@ extern int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, extern int tcp_connect(struct sock *sk); extern struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, - struct request_values *rvp, struct tcp_fastopen_cookie *foc); extern int tcp_disconnect(struct sock *sk, int flags); @@ -1589,91 +1587,6 @@ struct tcp_request_sock_ops { #endif }; -/* Using SHA1 for now, define some constants. - */ -#define COOKIE_DIGEST_WORDS (SHA_DIGEST_WORDS) -#define COOKIE_MESSAGE_WORDS (SHA_MESSAGE_BYTES / 4) -#define COOKIE_WORKSPACE_WORDS (COOKIE_DIGEST_WORDS + COOKIE_MESSAGE_WORDS) - -extern int tcp_cookie_generator(u32 *bakery); - -/** - * struct tcp_cookie_values - each socket needs extra space for the - * cookies, together with (optional) space for any SYN data. - * - * A tcp_sock contains a pointer to the current value, and this is - * cloned to the tcp_timewait_sock. - * - * @cookie_pair: variable data from the option exchange. - * - * @cookie_desired: user specified tcpct_cookie_desired. Zero - * indicates default (sysctl_tcp_cookie_size). - * After cookie sent, remembers size of cookie. - * Range 0, TCP_COOKIE_MIN to TCP_COOKIE_MAX. - * - * @s_data_desired: user specified tcpct_s_data_desired. When the - * constant payload is specified (@s_data_constant), - * holds its length instead. - * Range 0 to TCP_MSS_DESIRED. - * - * @s_data_payload: constant data that is to be included in the - * payload of SYN or SYNACK segments when the - * cookie option is present. - */ -struct tcp_cookie_values { - struct kref kref; - u8 cookie_pair[TCP_COOKIE_PAIR_SIZE]; - u8 cookie_pair_size; - u8 cookie_desired; - u16 s_data_desired:11, - s_data_constant:1, - s_data_in:1, - s_data_out:1, - s_data_unused:2; - u8 s_data_payload[0]; -}; - -static inline void tcp_cookie_values_release(struct kref *kref) -{ - kfree(container_of(kref, struct tcp_cookie_values, kref)); -} - -/* The length of constant payload data. Note that s_data_desired is - * overloaded, depending on s_data_constant: either the length of constant - * data (returned here) or the limit on variable data. - */ -static inline int tcp_s_data_size(const struct tcp_sock *tp) -{ - return (tp->cookie_values != NULL && tp->cookie_values->s_data_constant) - ? tp->cookie_values->s_data_desired - : 0; -} - -/** - * struct tcp_extend_values - tcp_ipv?.c to tcp_output.c workspace. - * - * As tcp_request_sock has already been extended in other places, the - * only remaining method is to pass stack values along as function - * parameters. These parameters are not needed after sending SYNACK. - * - * @cookie_bakery: cryptographic secret and message workspace. - * - * @cookie_plus: bytes in authenticator/cookie option, copied from - * struct tcp_options_received (above). - */ -struct tcp_extend_values { - struct request_values rv; - u32 cookie_bakery[COOKIE_WORKSPACE_WORDS]; - u8 cookie_plus:6, - cookie_out_never:1, - cookie_in_always:1; -}; - -static inline struct tcp_extend_values *tcp_xv(struct request_values *rvp) -{ - return (struct tcp_extend_values *)rvp; -} - extern void tcp_v4_init(void); extern void tcp_init(void); diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 6b1ead0b0c9..8d776ebc482 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -102,7 +102,6 @@ enum { #define TCP_QUICKACK 12 /* Block/reenable quick acks */ #define TCP_CONGESTION 13 /* Congestion control algorithm */ #define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */ -#define TCP_COOKIE_TRANSACTIONS 15 /* TCP Cookie Transactions */ #define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/ #define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */ #define TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */ @@ -199,29 +198,4 @@ struct tcp_md5sig { __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */ }; -/* for TCP_COOKIE_TRANSACTIONS (TCPCT) socket option */ -#define TCP_COOKIE_MIN 8 /* 64-bits */ -#define TCP_COOKIE_MAX 16 /* 128-bits */ -#define TCP_COOKIE_PAIR_SIZE (2*TCP_COOKIE_MAX) - -/* Flags for both getsockopt and setsockopt */ -#define TCP_COOKIE_IN_ALWAYS (1 << 0) /* Discard SYN without cookie */ -#define TCP_COOKIE_OUT_NEVER (1 << 1) /* Prohibit outgoing cookies, - * supercedes everything. */ - -/* Flags for getsockopt */ -#define TCP_S_DATA_IN (1 << 2) /* Was data received? */ -#define TCP_S_DATA_OUT (1 << 3) /* Was data sent? */ - -/* TCP_COOKIE_TRANSACTIONS data */ -struct tcp_cookie_transactions { - __u16 tcpct_flags; /* see above */ - __u8 __tcpct_pad1; /* zero */ - __u8 tcpct_cookie_desired; /* bytes */ - __u16 tcpct_s_data_desired; /* bytes of variable data */ - __u16 tcpct_used; /* bytes in value */ - __u8 tcpct_value[TCP_MSS_DEFAULT]; -}; - - #endif /* _UAPI_LINUX_TCP_H */ diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 4f9f5eb478f..ebc54fef85a 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -500,8 +500,7 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk, return &rt->dst; } -static int dccp_v4_send_response(struct sock *sk, struct request_sock *req, - struct request_values *rv_unused) +static int dccp_v4_send_response(struct sock *sk, struct request_sock *req) { int err = -1; struct sk_buff *skb; @@ -658,7 +657,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) dreq->dreq_gss = dreq->dreq_iss; dreq->dreq_service = service; - if (dccp_v4_send_response(sk, req, NULL)) + if (dccp_v4_send_response(sk, req)) goto drop_and_free; inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT); diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 6e05981f271..9c61f9c02fd 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -213,8 +213,7 @@ out: } -static int dccp_v6_send_response(struct sock *sk, struct request_sock *req, - struct request_values *rv_unused) +static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) { struct inet6_request_sock *ireq6 = inet6_rsk(req); struct ipv6_pinfo *np = inet6_sk(sk); @@ -428,7 +427,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) dreq->dreq_gss = dreq->dreq_iss; dreq->dreq_service = service; - if (dccp_v6_send_response(sk, req, NULL)) + if (dccp_v6_send_response(sk, req)) goto drop_and_free; inet6_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 786d97aee75..6acb541c909 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -559,7 +559,7 @@ static inline void syn_ack_recalc(struct request_sock *req, const int thresh, int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req) { - int err = req->rsk_ops->rtx_syn_ack(parent, req, NULL); + int err = req->rsk_ops->rtx_syn_ack(parent, req); if (!err) req->num_retrans++; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index ef54377fb11..7f4a5cb8f8d 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -267,7 +267,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt) { struct tcp_options_received tcp_opt; - const u8 *hash_location; struct inet_request_sock *ireq; struct tcp_request_sock *treq; struct tcp_sock *tp = tcp_sk(sk); @@ -294,7 +293,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, /* check for timestamp cookie support */ memset(&tcp_opt, 0, sizeof(tcp_opt)); - tcp_parse_options(skb, &tcp_opt, &hash_location, 0, NULL); + tcp_parse_options(skb, &tcp_opt, 0, NULL); if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) goto out; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index cca4550f408..cb45062c8be 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -732,13 +732,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, - { - .procname = "tcp_cookie_size", - .data = &sysctl_tcp_cookie_size, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_thin_linear_timeouts", .data = &sysctl_tcp_thin_linear_timeouts, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8d14573ade7..17a6810af5c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -409,15 +409,6 @@ void tcp_init_sock(struct sock *sk) icsk->icsk_sync_mss = tcp_sync_mss; - /* TCP Cookie Transactions */ - if (sysctl_tcp_cookie_size > 0) { - /* Default, cookies without s_data_payload. */ - tp->cookie_values = - kzalloc(sizeof(*tp->cookie_values), - sk->sk_allocation); - if (tp->cookie_values != NULL) - kref_init(&tp->cookie_values->kref); - } /* Presumed zeroed, in order of appearance: * cookie_in_always, cookie_out_never, * s_data_constant, s_data_in, s_data_out @@ -2397,92 +2388,6 @@ static int do_tcp_setsockopt(struct sock *sk, int level, release_sock(sk); return err; } - case TCP_COOKIE_TRANSACTIONS: { - struct tcp_cookie_transactions ctd; - struct tcp_cookie_values *cvp = NULL; - - if (sizeof(ctd) > optlen) - return -EINVAL; - if (copy_from_user(&ctd, optval, sizeof(ctd))) - return -EFAULT; - - if (ctd.tcpct_used > sizeof(ctd.tcpct_value) || - ctd.tcpct_s_data_desired > TCP_MSS_DESIRED) - return -EINVAL; - - if (ctd.tcpct_cookie_desired == 0) { - /* default to global value */ - } else if ((0x1 & ctd.tcpct_cookie_desired) || - ctd.tcpct_cookie_desired > TCP_COOKIE_MAX || - ctd.tcpct_cookie_desired < TCP_COOKIE_MIN) { - return -EINVAL; - } - - if (TCP_COOKIE_OUT_NEVER & ctd.tcpct_flags) { - /* Supercedes all other values */ - lock_sock(sk); - if (tp->cookie_values != NULL) { - kref_put(&tp->cookie_values->kref, - tcp_cookie_values_release); - tp->cookie_values = NULL; - } - tp->rx_opt.cookie_in_always = 0; /* false */ - tp->rx_opt.cookie_out_never = 1; /* true */ - release_sock(sk); - return err; - } - - /* Allocate ancillary memory before locking. - */ - if (ctd.tcpct_used > 0 || - (tp->cookie_values == NULL && - (sysctl_tcp_cookie_size > 0 || - ctd.tcpct_cookie_desired > 0 || - ctd.tcpct_s_data_desired > 0))) { - cvp = kzalloc(sizeof(*cvp) + ctd.tcpct_used, - GFP_KERNEL); - if (cvp == NULL) - return -ENOMEM; - - kref_init(&cvp->kref); - } - lock_sock(sk); - tp->rx_opt.cookie_in_always = - (TCP_COOKIE_IN_ALWAYS & ctd.tcpct_flags); - tp->rx_opt.cookie_out_never = 0; /* false */ - - if (tp->cookie_values != NULL) { - if (cvp != NULL) { - /* Changed values are recorded by a changed - * pointer, ensuring the cookie will differ, - * without separately hashing each value later. - */ - kref_put(&tp->cookie_values->kref, - tcp_cookie_values_release); - } else { - cvp = tp->cookie_values; - } - } - - if (cvp != NULL) { - cvp->cookie_desired = ctd.tcpct_cookie_desired; - - if (ctd.tcpct_used > 0) { - memcpy(cvp->s_data_payload, ctd.tcpct_value, - ctd.tcpct_used); - cvp->s_data_desired = ctd.tcpct_used; - cvp->s_data_constant = 1; /* true */ - } else { - /* No constant payload data. */ - cvp->s_data_desired = ctd.tcpct_s_data_desired; - cvp->s_data_constant = 0; /* false */ - } - - tp->cookie_values = cvp; - } - release_sock(sk); - return err; - } default: /* fallthru */ break; @@ -2902,41 +2807,6 @@ static int do_tcp_getsockopt(struct sock *sk, int level, return -EFAULT; return 0; - case TCP_COOKIE_TRANSACTIONS: { - struct tcp_cookie_transactions ctd; - struct tcp_cookie_values *cvp = tp->cookie_values; - - if (get_user(len, optlen)) - return -EFAULT; - if (len < sizeof(ctd)) - return -EINVAL; - - memset(&ctd, 0, sizeof(ctd)); - ctd.tcpct_flags = (tp->rx_opt.cookie_in_always ? - TCP_COOKIE_IN_ALWAYS : 0) - | (tp->rx_opt.cookie_out_never ? - TCP_COOKIE_OUT_NEVER : 0); - - if (cvp != NULL) { - ctd.tcpct_flags |= (cvp->s_data_in ? - TCP_S_DATA_IN : 0) - | (cvp->s_data_out ? - TCP_S_DATA_OUT : 0); - - ctd.tcpct_cookie_desired = cvp->cookie_desired; - ctd.tcpct_s_data_desired = cvp->s_data_desired; - - memcpy(&ctd.tcpct_value[0], &cvp->cookie_pair[0], - cvp->cookie_pair_size); - ctd.tcpct_used = cvp->cookie_pair_size; - } - - if (put_user(sizeof(ctd), optlen)) - return -EFAULT; - if (copy_to_user(optval, &ctd, sizeof(ctd))) - return -EFAULT; - return 0; - } case TCP_THIN_LINEAR_TIMEOUTS: val = tp->thin_lto; break; @@ -3409,134 +3279,6 @@ EXPORT_SYMBOL(tcp_md5_hash_key); #endif -/* Each Responder maintains up to two secret values concurrently for - * efficient secret rollover. Each secret value has 4 states: - * - * Generating. (tcp_secret_generating != tcp_secret_primary) - * Generates new Responder-Cookies, but not yet used for primary - * verification. This is a short-term state, typically lasting only - * one round trip time (RTT). - * - * Primary. (tcp_secret_generating == tcp_secret_primary) - * Used both for generation and primary verification. - * - * Retiring. (tcp_secret_retiring != tcp_secret_secondary) - * Used for verification, until the first failure that can be - * verified by the newer Generating secret. At that time, this - * cookie's state is changed to Secondary, and the Generating - * cookie's state is changed to Primary. This is a short-term state, - * typically lasting only one round trip time (RTT). - * - * Secondary. (tcp_secret_retiring == tcp_secret_secondary) - * Used for secondary verification, after primary verification - * failures. This state lasts no more than twice the Maximum Segment - * Lifetime (2MSL). Then, the secret is discarded. - */ -struct tcp_cookie_secret { - /* The secret is divided into two parts. The digest part is the - * equivalent of previously hashing a secret and saving the state, - * and serves as an initialization vector (IV). The message part - * serves as the trailing secret. - */ - u32 secrets[COOKIE_WORKSPACE_WORDS]; - unsigned long expires; -}; - -#define TCP_SECRET_1MSL (HZ * TCP_PAWS_MSL) -#define TCP_SECRET_2MSL (HZ * TCP_PAWS_MSL * 2) -#define TCP_SECRET_LIFE (HZ * 600) - -static struct tcp_cookie_secret tcp_secret_one; -static struct tcp_cookie_secret tcp_secret_two; - -/* Essentially a circular list, without dynamic allocation. */ -static struct tcp_cookie_secret *tcp_secret_generating; -static struct tcp_cookie_secret *tcp_secret_primary; -static struct tcp_cookie_secret *tcp_secret_retiring; -static struct tcp_cookie_secret *tcp_secret_secondary; - -static DEFINE_SPINLOCK(tcp_secret_locker); - -/* Select a pseudo-random word in the cookie workspace. - */ -static inline u32 tcp_cookie_work(const u32 *ws, const int n) -{ - return ws[COOKIE_DIGEST_WORDS + ((COOKIE_MESSAGE_WORDS-1) & ws[n])]; -} - -/* Fill bakery[COOKIE_WORKSPACE_WORDS] with generator, updating as needed. - * Called in softirq context. - * Returns: 0 for success. - */ -int tcp_cookie_generator(u32 *bakery) -{ - unsigned long jiffy = jiffies; - - if (unlikely(time_after_eq(jiffy, tcp_secret_generating->expires))) { - spin_lock_bh(&tcp_secret_locker); - if (!time_after_eq(jiffy, tcp_secret_generating->expires)) { - /* refreshed by another */ - memcpy(bakery, - &tcp_secret_generating->secrets[0], - COOKIE_WORKSPACE_WORDS); - } else { - /* still needs refreshing */ - get_random_bytes(bakery, COOKIE_WORKSPACE_WORDS); - - /* The first time, paranoia assumes that the - * randomization function isn't as strong. But, - * this secret initialization is delayed until - * the last possible moment (packet arrival). - * Although that time is observable, it is - * unpredictably variable. Mash in the most - * volatile clock bits available, and expire the - * secret extra quickly. - */ - if (unlikely(tcp_secret_primary->expires == - tcp_secret_secondary->expires)) { - struct timespec tv; - - getnstimeofday(&tv); - bakery[COOKIE_DIGEST_WORDS+0] ^= - (u32)tv.tv_nsec; - - tcp_secret_secondary->expires = jiffy - + TCP_SECRET_1MSL - + (0x0f & tcp_cookie_work(bakery, 0)); - } else { - tcp_secret_secondary->expires = jiffy - + TCP_SECRET_LIFE - + (0xff & tcp_cookie_work(bakery, 1)); - tcp_secret_primary->expires = jiffy - + TCP_SECRET_2MSL - + (0x1f & tcp_cookie_work(bakery, 2)); - } - memcpy(&tcp_secret_secondary->secrets[0], - bakery, COOKIE_WORKSPACE_WORDS); - - rcu_assign_pointer(tcp_secret_generating, - tcp_secret_secondary); - rcu_assign_pointer(tcp_secret_retiring, - tcp_secret_primary); - /* - * Neither call_rcu() nor synchronize_rcu() needed. - * Retiring data is not freed. It is replaced after - * further (locked) pointer updates, and a quiet time - * (minimum 1MSL, maximum LIFE - 2MSL). - */ - } - spin_unlock_bh(&tcp_secret_locker); - } else { - rcu_read_lock_bh(); - memcpy(bakery, - &rcu_dereference(tcp_secret_generating)->secrets[0], - COOKIE_WORKSPACE_WORDS); - rcu_read_unlock_bh(); - } - return 0; -} -EXPORT_SYMBOL(tcp_cookie_generator); - void tcp_done(struct sock *sk) { struct request_sock *req = tcp_sk(sk)->fastopen_rsk; @@ -3591,7 +3333,6 @@ void __init tcp_init(void) unsigned long limit; int max_rshare, max_wshare, cnt; unsigned int i; - unsigned long jiffy = jiffies; BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb)); @@ -3667,13 +3408,5 @@ void __init tcp_init(void) tcp_register_congestion_control(&tcp_reno); - memset(&tcp_secret_one.secrets[0], 0, sizeof(tcp_secret_one.secrets)); - memset(&tcp_secret_two.secrets[0], 0, sizeof(tcp_secret_two.secrets)); - tcp_secret_one.expires = jiffy; /* past due */ - tcp_secret_two.expires = jiffy; /* past due */ - tcp_secret_generating = &tcp_secret_one; - tcp_secret_primary = &tcp_secret_one; - tcp_secret_retiring = &tcp_secret_two; - tcp_secret_secondary = &tcp_secret_two; tcp_tasklet_init(); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 836d74dd018..19f0149fb6a 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3760,8 +3760,8 @@ old_ack: * But, this can also be called on packets in the established flow when * the fast version below fails. */ -void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *opt_rx, - const u8 **hvpp, int estab, +void tcp_parse_options(const struct sk_buff *skb, + struct tcp_options_received *opt_rx, int estab, struct tcp_fastopen_cookie *foc) { const unsigned char *ptr; @@ -3845,31 +3845,6 @@ void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *o */ break; #endif - case TCPOPT_COOKIE: - /* This option is variable length. - */ - switch (opsize) { - case TCPOLEN_COOKIE_BASE: - /* not yet implemented */ - break; - case TCPOLEN_COOKIE_PAIR: - /* not yet implemented */ - break; - case TCPOLEN_COOKIE_MIN+0: - case TCPOLEN_COOKIE_MIN+2: - case TCPOLEN_COOKIE_MIN+4: - case TCPOLEN_COOKIE_MIN+6: - case TCPOLEN_COOKIE_MAX: - /* 16-bit multiple */ - opt_rx->cookie_plus = opsize; - *hvpp = ptr; - break; - default: - /* ignore option */ - break; - } - break; - case TCPOPT_EXP: /* Fast Open option shares code 254 using a * 16 bits magic number. It's valid only in @@ -3915,8 +3890,7 @@ static bool tcp_parse_aligned_timestamp(struct tcp_sock *tp, const struct tcphdr * If it is wrong it falls back on tcp_parse_options(). */ static bool tcp_fast_parse_options(const struct sk_buff *skb, - const struct tcphdr *th, - struct tcp_sock *tp, const u8 **hvpp) + const struct tcphdr *th, struct tcp_sock *tp) { /* In the spirit of fast parsing, compare doff directly to constant * values. Because equality is used, short doff can be ignored here. @@ -3930,7 +3904,7 @@ static bool tcp_fast_parse_options(const struct sk_buff *skb, return true; } - tcp_parse_options(skb, &tp->rx_opt, hvpp, 1, NULL); + tcp_parse_options(skb, &tp->rx_opt, 1, NULL); if (tp->rx_opt.saw_tstamp) tp->rx_opt.rcv_tsecr -= tp->tsoffset; @@ -5311,12 +5285,10 @@ out: static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th, int syn_inerr) { - const u8 *hash_location; struct tcp_sock *tp = tcp_sk(sk); /* RFC1323: H1. Apply PAWS check first. */ - if (tcp_fast_parse_options(skb, th, tp, &hash_location) && - tp->rx_opt.saw_tstamp && + if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp && tcp_paws_discard(sk, skb)) { if (!th->rst) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED); @@ -5670,12 +5642,11 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, if (mss == tp->rx_opt.user_mss) { struct tcp_options_received opt; - const u8 *hash_location; /* Get original SYNACK MSS value if user MSS sets mss_clamp */ tcp_clear_options(&opt); opt.user_mss = opt.mss_clamp = 0; - tcp_parse_options(synack, &opt, &hash_location, 0, NULL); + tcp_parse_options(synack, &opt, 0, NULL); mss = opt.mss_clamp; } @@ -5706,14 +5677,12 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th, unsigned int len) { - const u8 *hash_location; struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); - struct tcp_cookie_values *cvp = tp->cookie_values; struct tcp_fastopen_cookie foc = { .len = -1 }; int saved_clamp = tp->rx_opt.mss_clamp; - tcp_parse_options(skb, &tp->rx_opt, &hash_location, 0, &foc); + tcp_parse_options(skb, &tp->rx_opt, 0, &foc); if (tp->rx_opt.saw_tstamp) tp->rx_opt.rcv_tsecr -= tp->tsoffset; @@ -5810,30 +5779,6 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, * is initialized. */ tp->copied_seq = tp->rcv_nxt; - if (cvp != NULL && - cvp->cookie_pair_size > 0 && - tp->rx_opt.cookie_plus > 0) { - int cookie_size = tp->rx_opt.cookie_plus - - TCPOLEN_COOKIE_BASE; - int cookie_pair_size = cookie_size - + cvp->cookie_desired; - - /* A cookie extension option was sent and returned. - * Note that each incoming SYNACK replaces the - * Responder cookie. The initial exchange is most - * fragile, as protection against spoofing relies - * entirely upon the sequence and timestamp (above). - * This replacement strategy allows the correct pair to - * pass through, while any others will be filtered via - * Responder verification later. - */ - if (sizeof(cvp->cookie_pair) >= cookie_pair_size) { - memcpy(&cvp->cookie_pair[cvp->cookie_desired], - hash_location, cookie_size); - cvp->cookie_pair_size = cookie_pair_size; - } - } - smp_mb(); tcp_finish_connect(sk, skb); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b7ab868c828..b27c758ca23 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -838,7 +838,6 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, */ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, - struct request_values *rvp, u16 queue_mapping, bool nocache) { @@ -851,7 +850,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL) return -1; - skb = tcp_make_synack(sk, dst, req, rvp, NULL); + skb = tcp_make_synack(sk, dst, req, NULL); if (skb) { __tcp_v4_send_check(skb, ireq->loc_addr, ireq->rmt_addr); @@ -868,10 +867,9 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, return err; } -static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req, - struct request_values *rvp) +static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) { - int res = tcp_v4_send_synack(sk, NULL, req, rvp, 0, false); + int res = tcp_v4_send_synack(sk, NULL, req, 0, false); if (!res) TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); @@ -1371,8 +1369,7 @@ static bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, static int tcp_v4_conn_req_fastopen(struct sock *sk, struct sk_buff *skb, struct sk_buff *skb_synack, - struct request_sock *req, - struct request_values *rvp) + struct request_sock *req) { struct tcp_sock *tp = tcp_sk(sk); struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; @@ -1467,9 +1464,7 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk, int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) { - struct tcp_extend_values tmp_ext; struct tcp_options_received tmp_opt; - const u8 *hash_location; struct request_sock *req; struct inet_request_sock *ireq; struct tcp_sock *tp = tcp_sk(sk); @@ -1519,42 +1514,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) tcp_clear_options(&tmp_opt); tmp_opt.mss_clamp = TCP_MSS_DEFAULT; tmp_opt.user_mss = tp->rx_opt.user_mss; - tcp_parse_options(skb, &tmp_opt, &hash_location, 0, - want_cookie ? NULL : &foc); - - if (tmp_opt.cookie_plus > 0 && - tmp_opt.saw_tstamp && - !tp->rx_opt.cookie_out_never && - (sysctl_tcp_cookie_size > 0 || - (tp->cookie_values != NULL && - tp->cookie_values->cookie_desired > 0))) { - u8 *c; - u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS]; - int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE; - - if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0) - goto drop_and_release; - - /* Secret recipe starts with IP addresses */ - *mess++ ^= (__force u32)daddr; - *mess++ ^= (__force u32)saddr; - - /* plus variable length Initiator Cookie */ - c = (u8 *)mess; - while (l-- > 0) - *c++ ^= *hash_location++; - - want_cookie = false; /* not our kind of cookie */ - tmp_ext.cookie_out_never = 0; /* false */ - tmp_ext.cookie_plus = tmp_opt.cookie_plus; - } else if (!tp->rx_opt.cookie_in_always) { - /* redundant indications, but ensure initialization. */ - tmp_ext.cookie_out_never = 1; /* true */ - tmp_ext.cookie_plus = 0; - } else { - goto drop_and_release; - } - tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always; + tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); if (want_cookie && !tmp_opt.saw_tstamp) tcp_clear_options(&tmp_opt); @@ -1636,7 +1596,6 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) * of tcp_v4_send_synack()->tcp_select_initial_window(). */ skb_synack = tcp_make_synack(sk, dst, req, - (struct request_values *)&tmp_ext, fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL); if (skb_synack) { @@ -1660,8 +1619,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (fastopen_cookie_present(&foc) && foc.len != 0) NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL); - } else if (tcp_v4_conn_req_fastopen(sk, skb, skb_synack, req, - (struct request_values *)&tmp_ext)) + } else if (tcp_v4_conn_req_fastopen(sk, skb, skb_synack, req)) goto drop_and_free; return 0; @@ -2241,12 +2199,6 @@ void tcp_v4_destroy_sock(struct sock *sk) if (inet_csk(sk)->icsk_bind_hash) inet_put_port(sk); - /* TCP Cookie Transactions */ - if (tp->cookie_values != NULL) { - kref_put(&tp->cookie_values->kref, - tcp_cookie_values_release); - tp->cookie_values = NULL; - } BUG_ON(tp->fastopen_rsk != NULL); /* If socket is aborted during connect operation */ diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 4bdb09fca40..8f0234f8bb9 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -93,13 +93,12 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, const struct tcphdr *th) { struct tcp_options_received tmp_opt; - const u8 *hash_location; struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); bool paws_reject = false; tmp_opt.saw_tstamp = 0; if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) { - tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL); + tcp_parse_options(skb, &tmp_opt, 0, NULL); if (tmp_opt.saw_tstamp) { tmp_opt.rcv_tsecr -= tcptw->tw_ts_offset; @@ -388,32 +387,6 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, struct tcp_request_sock *treq = tcp_rsk(req); struct inet_connection_sock *newicsk = inet_csk(newsk); struct tcp_sock *newtp = tcp_sk(newsk); - struct tcp_sock *oldtp = tcp_sk(sk); - struct tcp_cookie_values *oldcvp = oldtp->cookie_values; - - /* TCP Cookie Transactions require space for the cookie pair, - * as it differs for each connection. There is no need to - * copy any s_data_payload stored at the original socket. - * Failure will prevent resuming the connection. - * - * Presumed copied, in order of appearance: - * cookie_in_always, cookie_out_never - */ - if (oldcvp != NULL) { - struct tcp_cookie_values *newcvp = - kzalloc(sizeof(*newtp->cookie_values), - GFP_ATOMIC); - - if (newcvp != NULL) { - kref_init(&newcvp->kref); - newcvp->cookie_desired = - oldcvp->cookie_desired; - newtp->cookie_values = newcvp; - } else { - /* Not Yet Implemented */ - newtp->cookie_values = NULL; - } - } /* Now setup tcp_sock */ newtp->pred_flags = 0; @@ -422,8 +395,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->rcv_nxt = treq->rcv_isn + 1; newtp->snd_sml = newtp->snd_una = - newtp->snd_nxt = newtp->snd_up = - treq->snt_isn + 1 + tcp_s_data_size(oldtp); + newtp->snd_nxt = newtp->snd_up = treq->snt_isn + 1; tcp_prequeue_init(newtp); INIT_LIST_HEAD(&newtp->tsq_node); @@ -460,8 +432,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, tcp_set_ca_state(newsk, TCP_CA_Open); tcp_init_xmit_timers(newsk); skb_queue_head_init(&newtp->out_of_order_queue); - newtp->write_seq = newtp->pushed_seq = - treq->snt_isn + 1 + tcp_s_data_size(oldtp); + newtp->write_seq = newtp->pushed_seq = treq->snt_isn + 1; newtp->rx_opt.saw_tstamp = 0; @@ -538,7 +509,6 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, bool fastopen) { struct tcp_options_received tmp_opt; - const u8 *hash_location; struct sock *child; const struct tcphdr *th = tcp_hdr(skb); __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); @@ -548,7 +518,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, tmp_opt.saw_tstamp = 0; if (th->doff > (sizeof(struct tcphdr)>>2)) { - tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL); + tcp_parse_options(skb, &tmp_opt, 0, NULL); if (tmp_opt.saw_tstamp) { tmp_opt.ts_recent = req->ts_recent; @@ -648,7 +618,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, */ if ((flg & TCP_FLAG_ACK) && !fastopen && (TCP_SKB_CB(skb)->ack_seq != - tcp_rsk(req)->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk)))) + tcp_rsk(req)->snt_isn + 1)) return sk; /* Also, it would be not so bad idea to check rcv_tsecr, which diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8e7742f0b5d..ac5871ebe08 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -65,9 +65,6 @@ int sysctl_tcp_base_mss __read_mostly = TCP_BASE_MSS; /* By default, RFC2861 behavior. */ int sysctl_tcp_slow_start_after_idle __read_mostly = 1; -int sysctl_tcp_cookie_size __read_mostly = 0; /* TCP_COOKIE_MAX */ -EXPORT_SYMBOL_GPL(sysctl_tcp_cookie_size); - static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp); @@ -386,7 +383,6 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp) #define OPTION_TS (1 << 1) #define OPTION_MD5 (1 << 2) #define OPTION_WSCALE (1 << 3) -#define OPTION_COOKIE_EXTENSION (1 << 4) #define OPTION_FAST_OPEN_COOKIE (1 << 8) struct tcp_out_options { @@ -400,36 +396,6 @@ struct tcp_out_options { struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie */ }; -/* The sysctl int routines are generic, so check consistency here. - */ -static u8 tcp_cookie_size_check(u8 desired) -{ - int cookie_size; - - if (desired > 0) - /* previously specified */ - return desired; - - cookie_size = ACCESS_ONCE(sysctl_tcp_cookie_size); - if (cookie_size <= 0) - /* no default specified */ - return 0; - - if (cookie_size <= TCP_COOKIE_MIN) - /* value too small, specify minimum */ - return TCP_COOKIE_MIN; - - if (cookie_size >= TCP_COOKIE_MAX) - /* value too large, specify maximum */ - return TCP_COOKIE_MAX; - - if (cookie_size & 1) - /* 8-bit multiple, illegal, fix it */ - cookie_size++; - - return (u8)cookie_size; -} - /* Write previously computed TCP options to the packet. * * Beware: Something in the Internet is very sensitive to the ordering of @@ -448,27 +414,9 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, { u16 options = opts->options; /* mungable copy */ - /* Having both authentication and cookies for security is redundant, - * and there's certainly not enough room. Instead, the cookie-less - * extension variant is proposed. - * - * Consider the pessimal case with authentication. The options - * could look like: - * COOKIE|MD5(20) + MSS(4) + SACK|TS(12) + WSCALE(4) == 40 - */ if (unlikely(OPTION_MD5 & options)) { - if (unlikely(OPTION_COOKIE_EXTENSION & options)) { - *ptr++ = htonl((TCPOPT_COOKIE << 24) | - (TCPOLEN_COOKIE_BASE << 16) | - (TCPOPT_MD5SIG << 8) | - TCPOLEN_MD5SIG); - } else { - *ptr++ = htonl((TCPOPT_NOP << 24) | - (TCPOPT_NOP << 16) | - (TCPOPT_MD5SIG << 8) | - TCPOLEN_MD5SIG); - } - options &= ~OPTION_COOKIE_EXTENSION; + *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | + (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG); /* overload cookie hash location */ opts->hash_location = (__u8 *)ptr; ptr += 4; @@ -497,44 +445,6 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, *ptr++ = htonl(opts->tsecr); } - /* Specification requires after timestamp, so do it now. - * - * Consider the pessimal case without authentication. The options - * could look like: - * MSS(4) + SACK|TS(12) + COOKIE(20) + WSCALE(4) == 40 - */ - if (unlikely(OPTION_COOKIE_EXTENSION & options)) { - __u8 *cookie_copy = opts->hash_location; - u8 cookie_size = opts->hash_size; - - /* 8-bit multiple handled in tcp_cookie_size_check() above, - * and elsewhere. - */ - if (0x2 & cookie_size) { - __u8 *p = (__u8 *)ptr; - - /* 16-bit multiple */ - *p++ = TCPOPT_COOKIE; - *p++ = TCPOLEN_COOKIE_BASE + cookie_size; - *p++ = *cookie_copy++; - *p++ = *cookie_copy++; - ptr++; - cookie_size -= 2; - } else { - /* 32-bit multiple */ - *ptr++ = htonl(((TCPOPT_NOP << 24) | - (TCPOPT_NOP << 16) | - (TCPOPT_COOKIE << 8) | - TCPOLEN_COOKIE_BASE) + - cookie_size); - } - - if (cookie_size > 0) { - memcpy(ptr, cookie_copy, cookie_size); - ptr += (cookie_size / 4); - } - } - if (unlikely(OPTION_SACK_ADVERTISE & options)) { *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | @@ -593,11 +503,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, struct tcp_md5sig_key **md5) { struct tcp_sock *tp = tcp_sk(sk); - struct tcp_cookie_values *cvp = tp->cookie_values; unsigned int remaining = MAX_TCP_OPTION_SPACE; - u8 cookie_size = (!tp->rx_opt.cookie_out_never && cvp != NULL) ? - tcp_cookie_size_check(cvp->cookie_desired) : - 0; struct tcp_fastopen_request *fastopen = tp->fastopen_req; #ifdef CONFIG_TCP_MD5SIG @@ -649,52 +555,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, tp->syn_fastopen = 1; } } - /* Note that timestamps are required by the specification. - * - * Odd numbers of bytes are prohibited by the specification, ensuring - * that the cookie is 16-bit aligned, and the resulting cookie pair is - * 32-bit aligned. - */ - if (*md5 == NULL && - (OPTION_TS & opts->options) && - cookie_size > 0) { - int need = TCPOLEN_COOKIE_BASE + cookie_size; - - if (0x2 & need) { - /* 32-bit multiple */ - need += 2; /* NOPs */ - - if (need > remaining) { - /* try shrinking cookie to fit */ - cookie_size -= 2; - need -= 4; - } - } - while (need > remaining && TCP_COOKIE_MIN <= cookie_size) { - cookie_size -= 4; - need -= 4; - } - if (TCP_COOKIE_MIN <= cookie_size) { - opts->options |= OPTION_COOKIE_EXTENSION; - opts->hash_location = (__u8 *)&cvp->cookie_pair[0]; - opts->hash_size = cookie_size; - - /* Remember for future incarnations. */ - cvp->cookie_desired = cookie_size; - - if (cvp->cookie_desired != cvp->cookie_pair_size) { - /* Currently use random bytes as a nonce, - * assuming these are completely unpredictable - * by hostile users of the same system. - */ - get_random_bytes(&cvp->cookie_pair[0], - cookie_size); - cvp->cookie_pair_size = cookie_size; - } - remaining -= need; - } - } return MAX_TCP_OPTION_SPACE - remaining; } @@ -704,14 +565,10 @@ static unsigned int tcp_synack_options(struct sock *sk, unsigned int mss, struct sk_buff *skb, struct tcp_out_options *opts, struct tcp_md5sig_key **md5, - struct tcp_extend_values *xvp, struct tcp_fastopen_cookie *foc) { struct inet_request_sock *ireq = inet_rsk(req); unsigned int remaining = MAX_TCP_OPTION_SPACE; - u8 cookie_plus = (xvp != NULL && !xvp->cookie_out_never) ? - xvp->cookie_plus : - 0; #ifdef CONFIG_TCP_MD5SIG *md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req); @@ -759,28 +616,7 @@ static unsigned int tcp_synack_options(struct sock *sk, remaining -= need; } } - /* Similar rationale to tcp_syn_options() applies here, too. - * If the options fit, the same options should fit now! - */ - if (*md5 == NULL && - ireq->tstamp_ok && - cookie_plus > TCPOLEN_COOKIE_BASE) { - int need = cookie_plus; /* has TCPOLEN_COOKIE_BASE */ - - if (0x2 & need) { - /* 32-bit multiple */ - need += 2; /* NOPs */ - } - if (need <= remaining) { - opts->options |= OPTION_COOKIE_EXTENSION; - opts->hash_size = cookie_plus - TCPOLEN_COOKIE_BASE; - remaining -= need; - } else { - /* There's no error return, so flag it. */ - xvp->cookie_out_never = 1; /* true */ - opts->hash_size = 0; - } - } + return MAX_TCP_OPTION_SPACE - remaining; } @@ -2802,32 +2638,24 @@ int tcp_send_synack(struct sock *sk) * sk: listener socket * dst: dst entry attached to the SYNACK * req: request_sock pointer - * rvp: request_values pointer * * Allocate one skb and build a SYNACK packet. * @dst is consumed : Caller should not use it again. */ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, - struct request_values *rvp, struct tcp_fastopen_cookie *foc) { struct tcp_out_options opts; - struct tcp_extend_values *xvp = tcp_xv(rvp); struct inet_request_sock *ireq = inet_rsk(req); struct tcp_sock *tp = tcp_sk(sk); - const struct tcp_cookie_values *cvp = tp->cookie_values; struct tcphdr *th; struct sk_buff *skb; struct tcp_md5sig_key *md5; int tcp_header_size; int mss; - int s_data_desired = 0; - if (cvp != NULL && cvp->s_data_constant && cvp->s_data_desired) - s_data_desired = cvp->s_data_desired; - skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired, - sk_gfp_atomic(sk, GFP_ATOMIC)); + skb = alloc_skb(MAX_TCP_HEADER + 15, sk_gfp_atomic(sk, GFP_ATOMIC)); if (unlikely(!skb)) { dst_release(dst); return NULL; @@ -2869,9 +2697,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, else #endif TCP_SKB_CB(skb)->when = tcp_time_stamp; - tcp_header_size = tcp_synack_options(sk, req, mss, - skb, &opts, &md5, xvp, foc) - + sizeof(*th); + tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, &md5, + foc) + sizeof(*th); skb_push(skb, tcp_header_size); skb_reset_transport_header(skb); @@ -2889,40 +2716,6 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn, TCPHDR_SYN | TCPHDR_ACK); - if (OPTION_COOKIE_EXTENSION & opts.options) { - if (s_data_desired) { - u8 *buf = skb_put(skb, s_data_desired); - - /* copy data directly from the listening socket. */ - memcpy(buf, cvp->s_data_payload, s_data_desired); - TCP_SKB_CB(skb)->end_seq += s_data_desired; - } - - if (opts.hash_size > 0) { - __u32 workspace[SHA_WORKSPACE_WORDS]; - u32 *mess = &xvp->cookie_bakery[COOKIE_DIGEST_WORDS]; - u32 *tail = &mess[COOKIE_MESSAGE_WORDS-1]; - - /* Secret recipe depends on the Timestamp, (future) - * Sequence and Acknowledgment Numbers, Initiator - * Cookie, and others handled by IP variant caller. - */ - *tail-- ^= opts.tsval; - *tail-- ^= tcp_rsk(req)->rcv_isn + 1; - *tail-- ^= TCP_SKB_CB(skb)->seq + 1; - - /* recommended */ - *tail-- ^= (((__force u32)th->dest << 16) | (__force u32)th->source); - *tail-- ^= (u32)(unsigned long)cvp; /* per sockopt */ - - sha_transform((__u32 *)&xvp->cookie_bakery[0], - (char *)mess, - &workspace[0]); - opts.hash_location = - (__u8 *)&xvp->cookie_bakery[0]; - } - } - th->seq = htonl(TCP_SKB_CB(skb)->seq); /* XXX data is queued and acked as is. No buffer/window check */ th->ack_seq = htonl(tcp_rsk(req)->rcv_nxt); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 8a0848b60b3..d5dda20bd71 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -149,7 +149,6 @@ static inline int cookie_check(const struct sk_buff *skb, __u32 cookie) struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) { struct tcp_options_received tcp_opt; - const u8 *hash_location; struct inet_request_sock *ireq; struct inet6_request_sock *ireq6; struct tcp_request_sock *treq; @@ -177,7 +176,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) /* check for timestamp cookie support */ memset(&tcp_opt, 0, sizeof(tcp_opt)); - tcp_parse_options(skb, &tcp_opt, &hash_location, 0, NULL); + tcp_parse_options(skb, &tcp_opt, 0, NULL); if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) goto out; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 9b6460055df..0a97add2ab7 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -454,7 +454,6 @@ out: static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, struct flowi6 *fl6, struct request_sock *req, - struct request_values *rvp, u16 queue_mapping) { struct inet6_request_sock *treq = inet6_rsk(req); @@ -466,7 +465,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, if (!dst && (dst = inet6_csk_route_req(sk, fl6, req)) == NULL) goto done; - skb = tcp_make_synack(sk, dst, req, rvp, NULL); + skb = tcp_make_synack(sk, dst, req, NULL); if (skb) { __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr); @@ -481,13 +480,12 @@ done: return err; } -static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req, - struct request_values *rvp) +static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) { struct flowi6 fl6; int res; - res = tcp_v6_send_synack(sk, NULL, &fl6, req, rvp, 0); + res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0); if (!res) TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); return res; @@ -940,9 +938,7 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) */ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) { - struct tcp_extend_values tmp_ext; struct tcp_options_received tmp_opt; - const u8 *hash_location; struct request_sock *req; struct inet6_request_sock *treq; struct ipv6_pinfo *np = inet6_sk(sk); @@ -980,50 +976,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) tcp_clear_options(&tmp_opt); tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); tmp_opt.user_mss = tp->rx_opt.user_mss; - tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL); - - if (tmp_opt.cookie_plus > 0 && - tmp_opt.saw_tstamp && - !tp->rx_opt.cookie_out_never && - (sysctl_tcp_cookie_size > 0 || - (tp->cookie_values != NULL && - tp->cookie_values->cookie_desired > 0))) { - u8 *c; - u32 *d; - u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS]; - int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE; - - if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0) - goto drop_and_free; - - /* Secret recipe starts with IP addresses */ - d = (__force u32 *)&ipv6_hdr(skb)->daddr.s6_addr32[0]; - *mess++ ^= *d++; - *mess++ ^= *d++; - *mess++ ^= *d++; - *mess++ ^= *d++; - d = (__force u32 *)&ipv6_hdr(skb)->saddr.s6_addr32[0]; - *mess++ ^= *d++; - *mess++ ^= *d++; - *mess++ ^= *d++; - *mess++ ^= *d++; - - /* plus variable length Initiator Cookie */ - c = (u8 *)mess; - while (l-- > 0) - *c++ ^= *hash_location++; - - want_cookie = false; /* not our kind of cookie */ - tmp_ext.cookie_out_never = 0; /* false */ - tmp_ext.cookie_plus = tmp_opt.cookie_plus; - } else if (!tp->rx_opt.cookie_in_always) { - /* redundant indications, but ensure initialization. */ - tmp_ext.cookie_out_never = 1; /* true */ - tmp_ext.cookie_plus = 0; - } else { - goto drop_and_free; - } - tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always; + tcp_parse_options(skb, &tmp_opt, 0, NULL); if (want_cookie && !tmp_opt.saw_tstamp) tcp_clear_options(&tmp_opt); @@ -1101,7 +1054,6 @@ have_isn: goto drop_and_release; if (tcp_v6_send_synack(sk, dst, &fl6, req, - (struct request_values *)&tmp_ext, skb_get_queue_mapping(skb)) || want_cookie) goto drop_and_free; -- cgit v1.2.3-70-g09d2 From 8655cc490e83f66476de8c1294411860325c3531 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 19 Feb 2013 21:10:30 +0000 Subject: iio: Add broken out info_mask fields for shared_by_type and separate This simplifies the code, removes an extensive layer of 'helper' macros and gives us twice as much room to play with in these masks before we have any need to be clever. Signed-off-by: Jonathan Cameron Acked-by: Lars-Peter Clausen --- drivers/iio/industrialio-core.c | 30 ++++++++++++++++++++++++++++++ include/linux/iio/iio.h | 10 +++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 6d8b0278564..f05289f7b51 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -708,6 +708,36 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, goto error_ret; attrcount++; } + for_each_set_bit(i, &chan->info_mask_separate, sizeof(long)*8) { + ret = __iio_add_chan_devattr(iio_chan_info_postfix[i], + chan, + &iio_read_channel_info, + &iio_write_channel_info, + i, + 0, + &indio_dev->dev, + &indio_dev->channel_attr_list); + if (ret < 0) + goto error_ret; + attrcount++; + } + for_each_set_bit(i, &chan->info_mask_shared_by_type, sizeof(long)*8) { + ret = __iio_add_chan_devattr(iio_chan_info_postfix[i], + chan, + &iio_read_channel_info, + &iio_write_channel_info, + i, + 1, + &indio_dev->dev, + &indio_dev->channel_attr_list); + if (ret == -EBUSY) { + ret = 0; + continue; + } else if (ret < 0) { + goto error_ret; + } + attrcount++; + } if (chan->ext_info) { unsigned int i = 0; diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index da8c776ba0b..76976509d62 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -218,6 +218,10 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, * endianness: little or big endian * @info_mask: What information is to be exported about this channel. * This includes calibbias, scale etc. + * @info_mask_separate: What information is to be exported that is specific to + * this channel. + * @info_mask_shared_by_type: What information is to be exported that is shared +* by all channels of the same type. * @event_mask: What events can this channel produce. * @ext_info: Array of extended info attributes for this channel. * The array is NULL terminated, the last element should @@ -253,6 +257,8 @@ struct iio_chan_spec { enum iio_endian endianness; } scan_type; long info_mask; + long info_mask_separate; + long info_mask_shared_by_type; long event_mask; const struct iio_chan_spec_ext_info *ext_info; const char *extend_name; @@ -275,7 +281,9 @@ struct iio_chan_spec { static inline bool iio_channel_has_info(const struct iio_chan_spec *chan, enum iio_chan_info_enum type) { - return chan->info_mask & IIO_CHAN_INFO_BITS(type); + return (chan->info_mask & IIO_CHAN_INFO_BITS(type)) | + (chan->info_mask_separate & type) | + (chan->info_mask_shared_by_type & type); } #define IIO_ST(si, rb, sb, sh) \ -- cgit v1.2.3-70-g09d2 From 5ea864940e8ab63c2669902650b807d0507c390d Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Wed, 27 Feb 2013 19:41:59 +0000 Subject: iio:st_sensors move to info_mask_(shared_by_type/separate) The original info_mask is going away in favour of the broken out versions. Signed-off-by: Jonathan Cameron Acked-by: Denis Ciocca --- include/linux/iio/common/st_sensors.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 1f86a97ab2e..7c0c0d3aef3 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -15,6 +15,7 @@ #include #include #include +#include #define ST_SENSORS_TX_MAX_LENGTH 2 #define ST_SENSORS_RX_MAX_LENGTH 6 @@ -45,8 +46,8 @@ { \ .type = device_type, \ .modified = 1, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ .scan_index = index, \ .channel2 = mod, \ .address = addr, \ -- cgit v1.2.3-70-g09d2 From ea0c68006321eea78a3702a9d68ff9395e06da38 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Wed, 27 Feb 2013 19:42:39 +0000 Subject: iio:adc:ad_sigma_delta move to info_mask_(shared_by_type/separate) The original info_mask is going away in favour of the broken out versions. Signed-off-by: Jonathan Cameron Acked-by: Lars-Peter Clausen --- include/linux/iio/adc/ad_sigma_delta.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 2e4eab9868a..e7fdec4db9d 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -133,9 +133,9 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig); .channel2 = (_channel2), \ .address = (_address), \ .extend_name = (_extend_name), \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT | \ - IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .scan_index = (_si), \ .scan_type = { \ .sign = 'u', \ -- cgit v1.2.3-70-g09d2 From b841f8abc27466026ecf4e5590c6c737c2e86e7e Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Wed, 27 Feb 2013 19:35:55 +0000 Subject: staging:iio:accel:adis move to info_mask_(shared_by_type/separate) The original info_mask is going away in favour of the broken out versions. Signed-off-by: Jonathan Cameron Acked-by: Lars-Peter Clausen --- drivers/staging/iio/accel/adis16201_core.c | 8 +++---- drivers/staging/iio/accel/adis16203_core.c | 2 +- drivers/staging/iio/accel/adis16204_core.c | 8 +++---- drivers/staging/iio/accel/adis16209_core.c | 4 ++-- drivers/staging/iio/accel/adis16240_core.c | 9 +++----- drivers/staging/iio/gyro/adis16260_core.c | 4 ++-- include/linux/iio/imu/adis.h | 34 +++++++++++++++--------------- 7 files changed, 32 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c index 9e5791ff2a0..ab8ec7af88b 100644 --- a/drivers/staging/iio/accel/adis16201_core.c +++ b/drivers/staging/iio/accel/adis16201_core.c @@ -134,14 +134,14 @@ static const struct iio_chan_spec adis16201_channels[] = { ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT, ADIS16201_SCAN_SUPPLY, 12), ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT, ADIS16201_SCAN_TEMP, 12), ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT, ADIS16201_SCAN_ACC_X, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 14), ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT, ADIS16201_SCAN_ACC_Y, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 14), ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC, ADIS16201_SCAN_AUX_ADC, 12), ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT, ADIS16201_SCAN_INCLI_X, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 14), ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT, ADIS16201_SCAN_INCLI_Y, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 14), IIO_CHAN_SOFT_TIMESTAMP(7) }; diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c index 8c235273ff1..b08ac8fdeee 100644 --- a/drivers/staging/iio/accel/adis16203_core.c +++ b/drivers/staging/iio/accel/adis16203_core.c @@ -102,7 +102,7 @@ static const struct iio_chan_spec adis16203_channels[] = { ADIS_SUPPLY_CHAN(ADIS16203_SUPPLY_OUT, ADIS16203_SCAN_SUPPLY, 12), ADIS_AUX_ADC_CHAN(ADIS16203_AUX_ADC, ADIS16203_SCAN_AUX_ADC, 12), ADIS_INCLI_CHAN(X, ADIS16203_XINCL_OUT, ADIS16203_SCAN_INCLI_X, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 14), /* Fixme: Not what it appears to be - see data sheet */ ADIS_INCLI_CHAN(Y, ADIS16203_YINCL_OUT, ADIS16203_SCAN_INCLI_Y, 0, 14), ADIS_TEMP_CHAN(ADIS16203_TEMP_OUT, ADIS16203_SCAN_TEMP, 12), diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c index f3592668e06..792ec25a50d 100644 --- a/drivers/staging/iio/accel/adis16204_core.c +++ b/drivers/staging/iio/accel/adis16204_core.c @@ -140,13 +140,11 @@ static const struct iio_chan_spec adis16204_channels[] = { ADIS_AUX_ADC_CHAN(ADIS16204_AUX_ADC, ADIS16204_SCAN_AUX_ADC, 12), ADIS_TEMP_CHAN(ADIS16204_TEMP_OUT, ADIS16204_SCAN_TEMP, 12), ADIS_ACCEL_CHAN(X, ADIS16204_XACCL_OUT, ADIS16204_SCAN_ACC_X, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | - IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 14), ADIS_ACCEL_CHAN(Y, ADIS16204_YACCL_OUT, ADIS16204_SCAN_ACC_Y, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | - IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 14), ADIS_ACCEL_CHAN(ROOT_SUM_SQUARED_X_Y, ADIS16204_XY_RSS_OUT, - ADIS16204_SCAN_ACC_XY, IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 14), + ADIS16204_SCAN_ACC_XY, BIT(IIO_CHAN_INFO_PEAK), 14), IIO_CHAN_SOFT_TIMESTAMP(5), }; diff --git a/drivers/staging/iio/accel/adis16209_core.c b/drivers/staging/iio/accel/adis16209_core.c index 69c50ee44ce..323c169d699 100644 --- a/drivers/staging/iio/accel/adis16209_core.c +++ b/drivers/staging/iio/accel/adis16209_core.c @@ -133,9 +133,9 @@ static const struct iio_chan_spec adis16209_channels[] = { ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 14), ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 12), ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT, ADIS16209_SCAN_ACC_X, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 14), ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT, ADIS16209_SCAN_ACC_Y, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 14), ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 12), ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X, 0, 14), ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y, 0, 14), diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c index e97fa0b0233..fd1f0fd0fba 100644 --- a/drivers/staging/iio/accel/adis16240_core.c +++ b/drivers/staging/iio/accel/adis16240_core.c @@ -176,14 +176,11 @@ static const struct iio_chan_spec adis16240_channels[] = { ADIS_SUPPLY_CHAN(ADIS16240_SUPPLY_OUT, ADIS16240_SCAN_SUPPLY, 10), ADIS_AUX_ADC_CHAN(ADIS16240_AUX_ADC, ADIS16240_SCAN_AUX_ADC, 10), ADIS_ACCEL_CHAN(X, ADIS16240_XACCL_OUT, ADIS16240_SCAN_ACC_X, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | - IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 10), + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 10), ADIS_ACCEL_CHAN(Y, ADIS16240_YACCL_OUT, ADIS16240_SCAN_ACC_Y, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | - IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 10), + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 10), ADIS_ACCEL_CHAN(Z, ADIS16240_ZACCL_OUT, ADIS16240_SCAN_ACC_Z, - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | - IIO_CHAN_INFO_PEAK_SEPARATE_BIT, 10), + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), 10), ADIS_TEMP_CHAN(ADIS16240_TEMP_OUT, ADIS16240_SCAN_TEMP, 10), IIO_CHAN_SOFT_TIMESTAMP(6) }; diff --git a/drivers/staging/iio/gyro/adis16260_core.c b/drivers/staging/iio/gyro/adis16260_core.c index 6e80b8c768a..620d63fd099 100644 --- a/drivers/staging/iio/gyro/adis16260_core.c +++ b/drivers/staging/iio/gyro/adis16260_core.c @@ -124,8 +124,8 @@ static IIO_DEVICE_ATTR(sampling_frequency_available, #define ADIS16260_GYRO_CHANNEL_SET(axis, mod) \ struct iio_chan_spec adis16260_channels_##axis[] = { \ ADIS_GYRO_CHAN(mod, ADIS16260_GYRO_OUT, ADIS16260_SCAN_GYRO, \ - IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \ - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, 14), \ + BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), 14), \ ADIS_INCLI_CHAN(mod, ADIS16260_ANGL_OUT, ADIS16260_SCAN_ANGL, 0, 14), \ ADIS_TEMP_CHAN(ADIS16260_TEMP_OUT, ADIS16260_SCAN_TEMP, 12), \ ADIS_SUPPLY_CHAN(ADIS16260_SUPPLY_OUT, ADIS16260_SCAN_SUPPLY, 12), \ diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index ff781dca2e9..b665dc7f017 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -162,8 +162,8 @@ int adis_single_conversion(struct iio_dev *indio_dev, .indexed = 1, \ .channel = (chan), \ .extend_name = name, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ .address = (addr), \ .scan_index = (si), \ .scan_type = { \ @@ -184,9 +184,9 @@ int adis_single_conversion(struct iio_dev *indio_dev, .type = IIO_TEMP, \ .indexed = 1, \ .channel = 0, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \ - IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ .address = (addr), \ .scan_index = (si), \ .scan_type = { \ @@ -197,13 +197,13 @@ int adis_single_conversion(struct iio_dev *indio_dev, }, \ } -#define ADIS_MOD_CHAN(_type, mod, addr, si, info, bits) { \ +#define ADIS_MOD_CHAN(_type, mod, addr, si, info_sep, bits) { \ .type = (_type), \ .modified = 1, \ .channel2 = IIO_MOD_ ## mod, \ - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ - IIO_CHAN_INFO_SCALE_SHARED_BIT | \ - info, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + info_sep, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .address = (addr), \ .scan_index = (si), \ .scan_type = { \ @@ -214,17 +214,17 @@ int adis_single_conversion(struct iio_dev *indio_dev, }, \ } -#define ADIS_ACCEL_CHAN(mod, addr, si, info, bits) \ - ADIS_MOD_CHAN(IIO_ACCEL, mod, addr, si, info, bits) +#define ADIS_ACCEL_CHAN(mod, addr, si, info_sep, bits) \ + ADIS_MOD_CHAN(IIO_ACCEL, mod, addr, si, info_sep, bits) -#define ADIS_GYRO_CHAN(mod, addr, si, info, bits) \ - ADIS_MOD_CHAN(IIO_ANGL_VEL, mod, addr, si, info, bits) +#define ADIS_GYRO_CHAN(mod, addr, si, info_sep, bits) \ + ADIS_MOD_CHAN(IIO_ANGL_VEL, mod, addr, si, info_sep, bits) -#define ADIS_INCLI_CHAN(mod, addr, si, info, bits) \ - ADIS_MOD_CHAN(IIO_INCLI, mod, addr, si, info, bits) +#define ADIS_INCLI_CHAN(mod, addr, si, info_sep, bits) \ + ADIS_MOD_CHAN(IIO_INCLI, mod, addr, si, info_sep, bits) -#define ADIS_ROT_CHAN(mod, addr, si, info, bits) \ - ADIS_MOD_CHAN(IIO_ROT, mod, addr, si, info, bits) +#define ADIS_ROT_CHAN(mod, addr, si, info_sep, bits) \ + ADIS_MOD_CHAN(IIO_ROT, mod, addr, si, info_sep, bits) #ifdef CONFIG_IIO_ADIS_LIB_BUFFER -- cgit v1.2.3-70-g09d2 From b9606e2aa97d3d831d1236c0e789a33a2f867a8a Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Wed, 27 Feb 2013 19:43:52 +0000 Subject: iio:core drop info_mask from struct iio_info This has been replaced by the pair of masks info_mask_separate and info_mask_shared_by_type. Other variants may follow. Signed-off-by: Jonathan Cameron Acked-by: Lars-Peter Clausen --- drivers/iio/industrialio-core.c | 17 ---------- include/linux/iio/iio.h | 73 +---------------------------------------- 2 files changed, 1 insertion(+), 89 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index f05289f7b51..e145931ef1b 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -691,23 +691,6 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, if (chan->channel < 0) return 0; - for_each_set_bit(i, &chan->info_mask, sizeof(long)*8) { - ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2], - chan, - &iio_read_channel_info, - &iio_write_channel_info, - i/2, - !(i%2), - &indio_dev->dev, - &indio_dev->channel_attr_list); - if (ret == -EBUSY && (i%2 == 0)) { - ret = 0; - continue; - } - if (ret < 0) - goto error_ret; - attrcount++; - } for_each_set_bit(i, &chan->info_mask_separate, sizeof(long)*8) { ret = __iio_add_chan_devattr(iio_chan_info_postfix[i], chan, diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 76976509d62..8d171f42763 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -38,76 +38,6 @@ enum iio_chan_info_enum { IIO_CHAN_INFO_HYSTERESIS, }; -#define IIO_CHAN_INFO_SHARED_BIT(type) BIT(type*2) -#define IIO_CHAN_INFO_SEPARATE_BIT(type) BIT(type*2 + 1) -#define IIO_CHAN_INFO_BITS(type) (IIO_CHAN_INFO_SHARED_BIT(type) | \ - IIO_CHAN_INFO_SEPARATE_BIT(type)) - -#define IIO_CHAN_INFO_RAW_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_RAW) -#define IIO_CHAN_INFO_PROCESSED_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_PROCESSED) -#define IIO_CHAN_INFO_SCALE_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_SCALE) -#define IIO_CHAN_INFO_SCALE_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_SCALE) -#define IIO_CHAN_INFO_OFFSET_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_OFFSET) -#define IIO_CHAN_INFO_OFFSET_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_OFFSET) -#define IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_CALIBSCALE) -#define IIO_CHAN_INFO_CALIBSCALE_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_CALIBSCALE) -#define IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_CALIBBIAS) -#define IIO_CHAN_INFO_CALIBBIAS_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_CALIBBIAS) -#define IIO_CHAN_INFO_PEAK_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_PEAK) -#define IIO_CHAN_INFO_PEAK_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_PEAK) -#define IIO_CHAN_INFO_PEAKSCALE_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_PEAKSCALE) -#define IIO_CHAN_INFO_PEAKSCALE_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_PEAKSCALE) -#define IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT( \ - IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW) -#define IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT( \ - IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW) -#define IIO_CHAN_INFO_AVERAGE_RAW_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_AVERAGE_RAW) -#define IIO_CHAN_INFO_AVERAGE_RAW_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_AVERAGE_RAW) -#define IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT( \ - IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) -#define IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT( \ - IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) -#define IIO_CHAN_INFO_SAMP_FREQ_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_SAMP_FREQ) -#define IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_SAMP_FREQ) -#define IIO_CHAN_INFO_FREQUENCY_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_FREQUENCY) -#define IIO_CHAN_INFO_FREQUENCY_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_FREQUENCY) -#define IIO_CHAN_INFO_PHASE_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_PHASE) -#define IIO_CHAN_INFO_PHASE_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_PHASE) -#define IIO_CHAN_INFO_HARDWAREGAIN_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_HARDWAREGAIN) -#define IIO_CHAN_INFO_HARDWAREGAIN_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_HARDWAREGAIN) -#define IIO_CHAN_INFO_HYSTERESIS_SEPARATE_BIT \ - IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_HYSTERESIS) -#define IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT \ - IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_HYSTERESIS) - enum iio_endian { IIO_CPU, IIO_BE, @@ -281,8 +211,7 @@ struct iio_chan_spec { static inline bool iio_channel_has_info(const struct iio_chan_spec *chan, enum iio_chan_info_enum type) { - return (chan->info_mask & IIO_CHAN_INFO_BITS(type)) | - (chan->info_mask_separate & type) | + return (chan->info_mask_separate & type) | (chan->info_mask_shared_by_type & type); } -- cgit v1.2.3-70-g09d2 From 717bfb5f46f0ee809f6ce04ebdf44521730fff05 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 17 Mar 2013 20:07:25 +0800 Subject: ALSA: snd-usb: handle raw data format of UAC2 devices UAC2 compliant audio devices may announce the capability to transport raw audio data on their endpoints. Catch this and handle it as 'special' stream on the ALSA side. Signed-off-by: Daniel Mack Reported-by: Andreas Koch Signed-off-by: Takashi Iwai --- include/linux/usb/audio-v2.h | 2 ++ sound/usb/format.c | 11 +++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index ed13053153f..c5f2158ab00 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -170,6 +170,8 @@ struct uac2_as_header_descriptor { __u8 iChannelNames; } __attribute__((packed)); +#define UAC2_FORMAT_TYPE_I_RAW_DATA (1 << 31) + /* 4.10.1.2 Class-Specific AS Isochronous Audio Data Endpoint Descriptor */ struct uac2_iso_endpoint_descriptor { diff --git a/sound/usb/format.c b/sound/usb/format.c index b30d6fb89b4..a695cafc059 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -47,7 +47,7 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, int protocol) { int sample_width, sample_bytes; - u64 pcm_formats; + u64 pcm_formats = 0; switch (protocol) { case UAC_VERSION_1: @@ -63,14 +63,17 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, struct uac_format_type_i_ext_descriptor *fmt = _fmt; sample_width = fmt->bBitResolution; sample_bytes = fmt->bSubslotSize; + + if (format & UAC2_FORMAT_TYPE_I_RAW_DATA) + pcm_formats |= SNDRV_PCM_FMTBIT_SPECIAL; + format <<= 1; break; } } - pcm_formats = 0; - - if (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED)) { + if ((pcm_formats == 0) && + (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED))) { /* some devices don't define this correctly... */ snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n", chip->dev->devnum, fp->iface, fp->altsetting); -- cgit v1.2.3-70-g09d2 From 1f0972f5b05a674d73e4eb314fa1b6c78e37aef1 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 13:24:19 +0200 Subject: usb: phy: nop: Add some parameters to platform data Add clk_rate parameter to platform data. If supplied, the NOP phy driver will program the clock to that rate during probe. Also add 2 flags, needs_vcc and needs_reset. If the flag is set and the regulator couldn't be found then the driver will bail out with -EPROBE_DEFER. Signed-off-by: Roger Quadros Acked-by: Felipe Balbi Signed-off-by: Felipe Balbi --- include/linux/usb/nop-usb-xceiv.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/usb/nop-usb-xceiv.h b/include/linux/usb/nop-usb-xceiv.h index 28884c71741..148d35171aa 100644 --- a/include/linux/usb/nop-usb-xceiv.h +++ b/include/linux/usb/nop-usb-xceiv.h @@ -5,6 +5,11 @@ struct nop_usb_xceiv_platform_data { enum usb_phy_type type; + unsigned long clk_rate; + + /* if set fails with -EPROBE_DEFER if can't get regulator */ + unsigned int needs_vcc:1; + unsigned int needs_reset:1; }; #if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) -- cgit v1.2.3-70-g09d2 From 4495afcf713adb5bdb16504052952bdd0d11f90a Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 26 Feb 2013 20:03:28 +0530 Subject: usb: dwc3: omap: remove platform data associated with dwc3-omap omap5 is not going to have support for non-dt boot making the platform data associated with dwc3 useless. Removed it here. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 24 ++++++++++-------------- include/linux/platform_data/dwc3-omap.h | 4 ---- 2 files changed, 10 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index e1206b41993..43a248219aa 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -309,7 +309,6 @@ static int dwc3_omap_remove_core(struct device *dev, void *c) static int dwc3_omap_probe(struct platform_device *pdev) { - struct dwc3_omap_data *pdata = pdev->dev.platform_data; struct device_node *node = pdev->dev.of_node; struct dwc3_omap *omap; @@ -326,6 +325,11 @@ static int dwc3_omap_probe(struct platform_device *pdev) void __iomem *base; void *context; + if (!node) { + dev_err(dev, "device node not found\n"); + return -EINVAL; + } + omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); if (!omap) { dev_err(dev, "not enough memory\n"); @@ -387,12 +391,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); - if (node) - of_property_read_u32(node, "utmi-mode", &utmi_mode); - else if (pdata) - utmi_mode = pdata->utmi_mode; - else - dev_dbg(dev, "missing platform data\n"); + of_property_read_u32(node, "utmi-mode", &utmi_mode); switch (utmi_mode) { case DWC3_OMAP_UTMI_MODE_SW: @@ -435,13 +434,10 @@ static int dwc3_omap_probe(struct platform_device *pdev) dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg); - if (node) { - ret = of_platform_populate(node, NULL, NULL, dev); - if (ret) { - dev_err(&pdev->dev, - "failed to add create dwc3 core\n"); - return ret; - } + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) { + dev_err(&pdev->dev, "failed to create dwc3 core\n"); + return ret; } return 0; diff --git a/include/linux/platform_data/dwc3-omap.h b/include/linux/platform_data/dwc3-omap.h index ada401244e0..1d36ca874cc 100644 --- a/include/linux/platform_data/dwc3-omap.h +++ b/include/linux/platform_data/dwc3-omap.h @@ -41,7 +41,3 @@ enum dwc3_omap_utmi_mode { DWC3_OMAP_UTMI_MODE_HW, DWC3_OMAP_UTMI_MODE_SW, }; - -struct dwc3_omap_data { - enum dwc3_omap_utmi_mode utmi_mode; -}; -- cgit v1.2.3-70-g09d2 From f07bd56bbdaa2340ebf46af9a37e7b2d1b4578e3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 14:52:24 +0200 Subject: usb: gadget: udc-core: allow udc class register gadget device Currently all UDC drivers are calling device_register() before calling usb_add_gadget_udc(). In order to avoid code duplication, we can allow udc-core.c register that device. However that would become a really large patch, so to cope with the meanwhile and allow us to write bite-sized patches, we're adding a flag which will be set by UDC driver once it removes the code for registering the gadget device. Once all are converted, the new flag will be removed. Reviewed-by: Tomasz Figa Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc-core.c | 23 +++++++++++++++++++---- include/linux/usb/gadget.h | 4 ++++ 2 files changed, 23 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 2a9cd369f71..919505426ec 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -173,6 +173,14 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) if (!udc) goto err1; + if (gadget->register_my_device) { + dev_set_name(&gadget->dev, "gadget"); + + ret = device_register(&gadget->dev); + if (ret) + goto err2; + } + device_initialize(&udc->dev); udc->dev.release = usb_udc_release; udc->dev.class = udc_class; @@ -180,7 +188,7 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) udc->dev.parent = parent; ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); if (ret) - goto err2; + goto err3; udc->gadget = gadget; @@ -189,18 +197,22 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) ret = device_add(&udc->dev); if (ret) - goto err3; + goto err4; mutex_unlock(&udc_lock); return 0; -err3: + +err4: list_del(&udc->list); mutex_unlock(&udc_lock); -err2: +err3: put_device(&udc->dev); +err2: + if (gadget->register_my_device) + put_device(&gadget->dev); err1: return ret; } @@ -254,6 +266,9 @@ found: kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); device_unregister(&udc->dev); + + if (gadget->register_my_device) + device_unregister(&gadget->dev); } EXPORT_SYMBOL_GPL(usb_del_gadget_udc); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 2e297e80d59..fcd9ef8d3f7 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -494,6 +494,9 @@ struct usb_gadget_ops { * only supports HNP on a different root port. * @b_hnp_enable: OTG device feature flag, indicating that the A-Host * enabled HNP support. + * @register_my_device: Flag telling udc-core that UDC driver didn't + * register the gadget device to the driver model. Temporary until + * all UDC drivers are fixed up properly. * @name: Identifies the controller hardware type. Used in diagnostics * and sometimes configuration. * @dev: Driver model state for this abstract device. @@ -531,6 +534,7 @@ struct usb_gadget { unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; + unsigned register_my_device:1; const char *name; struct device dev; unsigned out_epnum; -- cgit v1.2.3-70-g09d2 From 7bce401cc6db5508ef2517e45bd8caf7ce0a15ee Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 17:41:00 +0200 Subject: usb: gadget: drop now unnecessary flag We don't need the ->register_my_device flag anymore because all UDC drivers have been properly converted. Let's remove every history of it. Signed-off-by: Felipe Balbi --- drivers/usb/chipidea/udc.c | 1 - drivers/usb/dwc3/gadget.c | 1 - drivers/usb/gadget/amd5536udc.c | 1 - drivers/usb/gadget/at91_udc.c | 1 - drivers/usb/gadget/atmel_usba_udc.c | 1 - drivers/usb/gadget/bcm63xx_udc.c | 1 - drivers/usb/gadget/dummy_hcd.c | 1 - drivers/usb/gadget/fsl_qe_udc.c | 1 - drivers/usb/gadget/fsl_udc_core.c | 1 - drivers/usb/gadget/fusb300_udc.c | 1 - drivers/usb/gadget/goku_udc.c | 1 - drivers/usb/gadget/imx_udc.c | 1 - drivers/usb/gadget/lpc32xx_udc.c | 1 - drivers/usb/gadget/m66592-udc.c | 1 - drivers/usb/gadget/mv_u3d_core.c | 1 - drivers/usb/gadget/mv_udc_core.c | 1 - drivers/usb/gadget/net2272.c | 1 - drivers/usb/gadget/net2280.c | 1 - drivers/usb/gadget/omap_udc.c | 1 - drivers/usb/gadget/pch_udc.c | 1 - drivers/usb/gadget/pxa25x_udc.c | 1 - drivers/usb/gadget/pxa27x_udc.c | 1 - drivers/usb/gadget/r8a66597-udc.c | 1 - drivers/usb/gadget/s3c-hsotg.c | 1 - drivers/usb/gadget/s3c-hsudc.c | 1 - drivers/usb/gadget/s3c2410_udc.c | 1 - drivers/usb/gadget/udc-core.c | 18 +++++++----------- drivers/usb/musb/musb_gadget.c | 1 - drivers/usb/renesas_usbhs/mod_gadget.c | 1 - include/linux/usb/gadget.h | 4 ---- 30 files changed, 7 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index e95e8bbde98..1b65ac8f3c9 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1721,7 +1721,6 @@ static int udc_start(struct ci13xxx *ci) ci->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask; ci->gadget.dev.parent = dev; ci->gadget.dev.release = udc_release; - ci->gadget.register_my_device = true; /* alloc resources */ ci->qh_pool = dma_pool_create("ci13xxx_qh", dev, diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 10bb161eec8..65493b6cd5a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2499,7 +2499,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.dev.dma_parms = dwc->dev->dma_parms; dwc->gadget.dev.dma_mask = dwc->dev->dma_mask; dwc->gadget.dev.release = dwc3_gadget_release; - dwc->gadget.register_my_device = true; dwc->gadget.name = "dwc3-gadget"; /* diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index eee01ea70f8..eec4461fb45 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3275,7 +3275,6 @@ static int udc_probe(struct udc *dev) dev->gadget.dev.release = gadget_release; dev->gadget.name = name; dev->gadget.max_speed = USB_SPEED_HIGH; - dev->gadget.register_my_device = true; /* init registers, interrupts, ... */ startup_registers(dev); diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 47b7e58f841..9936de9bbe5 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1726,7 +1726,6 @@ static int at91udc_probe(struct platform_device *pdev) /* init software state */ udc = &controller; - udc->gadget.register_my_device = true; udc->gadget.dev.parent = dev; if (pdev->dev.of_node) at91udc_of_init(udc, pdev->dev.of_node); diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 2404d0c2566..41518e61280 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1902,7 +1902,6 @@ static int __init usba_udc_probe(struct platform_device *pdev) udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = pdev->dev.dma_mask; - udc->gadget.register_my_device = true; platform_set_drvdata(pdev, udc); diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c index c020b877219..d4f73e1b37e 100644 --- a/drivers/usb/gadget/bcm63xx_udc.c +++ b/drivers/usb/gadget/bcm63xx_udc.c @@ -2374,7 +2374,6 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) udc->gadget.dev.parent = dev; udc->gadget.dev.release = bcm63xx_udc_gadget_release; udc->gadget.dev.dma_mask = dev->dma_mask; - udc->gadget.register_my_device = true; if (!pd->use_fullspeed && !use_fullspeed) udc->gadget.max_speed = USB_SPEED_HIGH; diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index a6950aa8f3b..c4f27d5a2b9 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -983,7 +983,6 @@ static int dummy_udc_probe(struct platform_device *pdev) dum->gadget.name = gadget_name; dum->gadget.ops = &dummy_ops; dum->gadget.max_speed = USB_SPEED_SUPER; - dum->gadget.register_my_device = true; dum->gadget.dev.parent = &pdev->dev; dum->gadget.dev.release = dummy_gadget_release; diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 0f78cd859d6..0e7531bd33f 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -2525,7 +2525,6 @@ static int qe_udc_probe(struct platform_device *ofdev) udc->gadget.name = driver_name; udc->gadget.dev.release = qe_udc_release; udc->gadget.dev.parent = &ofdev->dev; - udc->gadget.register_my_device = true; /* initialize qe_ep struct */ for (i = 0; i < USB_MAX_ENDPOINTS ; i++) { diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 9140a2daad8..f33b9005eea 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -2524,7 +2524,6 @@ static int __init fsl_udc_probe(struct platform_device *pdev) udc_controller->gadget.dev.release = fsl_udc_release; udc_controller->gadget.dev.parent = &pdev->dev; udc_controller->gadget.dev.of_node = pdev->dev.of_node; - udc_controller->gadget.register_my_device = true; if (!IS_ERR_OR_NULL(udc_controller->transceiver)) udc_controller->gadget.is_otg = 1; diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index d29017218b0..2d3c8b351f4 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -1427,7 +1427,6 @@ static int __init fusb300_probe(struct platform_device *pdev) fusb300->gadget.dev.dma_mask = pdev->dev.dma_mask; fusb300->gadget.dev.release = pdev->dev.release; fusb300->gadget.name = udc_name; - fusb300->gadget.register_my_device = true; fusb300->reg = reg; ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED, diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index b4ea2cf465a..8a6c66618bd 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1758,7 +1758,6 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; - dev->gadget.register_my_device = true; /* now all the pci goodies ... */ retval = pci_enable_device(pdev); diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index 435b20346ea..9c5b7451a7d 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1461,7 +1461,6 @@ static int __init imx_udc_probe(struct platform_device *pdev) imx_usb->clk = clk; imx_usb->dev = &pdev->dev; - imx_usb->gadget.register_my_device = true; imx_usb->gadget.dev.parent = &pdev->dev; imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c index 329e1c5f0ef..67c3ef9d9be 100644 --- a/drivers/usb/gadget/lpc32xx_udc.c +++ b/drivers/usb/gadget/lpc32xx_udc.c @@ -3090,7 +3090,6 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev) /* init software state */ udc->gadget.dev.parent = dev; - udc->gadget.register_my_device = true; udc->pdev = pdev; udc->dev = &pdev->dev; udc->enabled = 0; diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 43ad70dff74..eb61d0b54f2 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1612,7 +1612,6 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; m66592->gadget.dev.release = pdev->dev.release; m66592->gadget.name = udc_name; - m66592->gadget.register_my_device = true; init_timer(&m66592->timer); m66592->timer.function = m66592_timer; diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 734ade11505..e5735fc610d 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1959,7 +1959,6 @@ static int mv_u3d_probe(struct platform_device *dev) u3d->gadget.dev.dma_mask = dev->dev.dma_mask; u3d->gadget.dev.release = mv_u3d_gadget_release; u3d->gadget.name = driver_name; /* gadget name */ - u3d->gadget.register_my_device = true; mv_u3d_eps_init(u3d); diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index a7afdfb413b..be35573f870 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2313,7 +2313,6 @@ static int mv_udc_probe(struct platform_device *pdev) udc->gadget.dev.dma_mask = pdev->dev.dma_mask; udc->gadget.dev.release = gadget_release; udc->gadget.name = driver_name; /* gadget name */ - udc->gadget.register_my_device = true; eps_init(udc); diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index 635248f42dc..78c8bb53833 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -2239,7 +2239,6 @@ static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq) ret->gadget.dev.dma_mask = dev->dma_mask; ret->gadget.dev.release = net2272_gadget_release; ret->gadget.name = driver_name; - ret->gadget.register_my_device = true; return ret; } diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index c55af429350..2089d9b0058 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2714,7 +2714,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->gadget.dev.dma_mask = pdev->dev.dma_mask; dev->gadget.dev.release = gadget_release; dev->gadget.name = driver_name; - dev->gadget.register_my_device = true; /* now all the pci goodies ... */ if (pci_enable_device (pdev) < 0) { diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index c979272e7c8..b23c861e2a9 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2634,7 +2634,6 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) udc->gadget.dev.release = omap_udc_release; udc->gadget.dev.parent = &odev->dev; - udc->gadget.register_my_device = true; if (use_dma) udc->gadget.dev.dma_mask = odev->dev.dma_mask; diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 703214543dd..e8c9afd8fbf 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -3198,7 +3198,6 @@ static int pch_udc_probe(struct pci_dev *pdev, dev->gadget.dev.release = gadget_release; dev->gadget.name = KBUILD_MODNAME; dev->gadget.max_speed = USB_SPEED_HIGH; - dev->gadget.register_my_device = true; /* Put the device in disconnected state till a driver is bound */ pch_udc_set_disconnect(dev); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 8996fcb053e..e29bb878b2d 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2140,7 +2140,6 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->gadget.dev.parent = &pdev->dev; dev->gadget.dev.dma_mask = pdev->dev.dma_mask; - dev->gadget.register_my_device = true; the_controller = dev; platform_set_drvdata(pdev, dev); diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 1c5bfaafa6c..07ce1477f91 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2457,7 +2457,6 @@ static int __init pxa_udc_probe(struct platform_device *pdev) udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = NULL; - udc->gadget.register_my_device = true; udc->vbus_sensed = 0; the_controller = udc; diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index ae94c0eaf63..a67d47708b9 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1919,7 +1919,6 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->gadget.dev.dma_mask = pdev->dev.dma_mask; r8a66597->gadget.dev.release = pdev->dev.release; r8a66597->gadget.name = udc_name; - r8a66597->gadget.register_my_device = true; init_timer(&r8a66597->timer); r8a66597->timer.function = r8a66597_timer; diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 5fbd233eb6a..8ae0bd99ffd 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -3573,7 +3573,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->gadget.dev.parent = dev; hsotg->gadget.dev.dma_mask = dev->dma_mask; hsotg->gadget.dev.release = s3c_hsotg_release; - hsotg->gadget.register_my_device = true; /* reset the system */ diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index c4ff747f53f..7fc3de537c9 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1312,7 +1312,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) hsudc->gadget.is_otg = 0; hsudc->gadget.is_a_peripheral = 0; hsudc->gadget.speed = USB_SPEED_UNKNOWN; - hsudc->gadget.register_my_device = true; s3c_hsudc_setup_ep(hsudc); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index c4134948dd9..a669081bbb8 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1826,7 +1826,6 @@ static int s3c2410_udc_probe(struct platform_device *pdev) udc->gadget.dev.parent = &pdev->dev; udc->gadget.dev.dma_mask = pdev->dev.dma_mask; - udc->gadget.register_my_device = true; the_controller = udc; platform_set_drvdata(pdev, udc); diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 919505426ec..40b1d888d5a 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -173,13 +173,11 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) if (!udc) goto err1; - if (gadget->register_my_device) { - dev_set_name(&gadget->dev, "gadget"); + dev_set_name(&gadget->dev, "gadget"); - ret = device_register(&gadget->dev); - if (ret) - goto err2; - } + ret = device_register(&gadget->dev); + if (ret) + goto err2; device_initialize(&udc->dev); udc->dev.release = usb_udc_release; @@ -211,8 +209,8 @@ err3: put_device(&udc->dev); err2: - if (gadget->register_my_device) - put_device(&gadget->dev); + put_device(&gadget->dev); + err1: return ret; } @@ -266,9 +264,7 @@ found: kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); device_unregister(&udc->dev); - - if (gadget->register_my_device) - device_unregister(&gadget->dev); + device_unregister(&gadget->dev); } EXPORT_SYMBOL_GPL(usb_del_gadget_udc); diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index cadb750921e..e363033f675 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1891,7 +1891,6 @@ int musb_gadget_setup(struct musb *musb) musb->g.dev.dma_mask = musb->controller->dma_mask; musb->g.dev.release = musb_gadget_release; musb->g.name = musb_driver_name; - musb->g.register_my_device = true; musb->g.is_otg = 1; musb_g_init_endpoints(musb); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 5d5fab0ad0d..6a3afa9b764 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -981,7 +981,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) gpriv->gadget.name = "renesas_usbhs_udc"; gpriv->gadget.ops = &usbhsg_gadget_ops; gpriv->gadget.max_speed = USB_SPEED_HIGH; - gpriv->gadget.register_my_device = true; INIT_LIST_HEAD(&gpriv->gadget.ep_list); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index fcd9ef8d3f7..2e297e80d59 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -494,9 +494,6 @@ struct usb_gadget_ops { * only supports HNP on a different root port. * @b_hnp_enable: OTG device feature flag, indicating that the A-Host * enabled HNP support. - * @register_my_device: Flag telling udc-core that UDC driver didn't - * register the gadget device to the driver model. Temporary until - * all UDC drivers are fixed up properly. * @name: Identifies the controller hardware type. Used in diagnostics * and sometimes configuration. * @dev: Driver model state for this abstract device. @@ -534,7 +531,6 @@ struct usb_gadget { unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; - unsigned register_my_device:1; const char *name; struct device dev; unsigned out_epnum; -- cgit v1.2.3-70-g09d2 From d1e3d757f7aa91f15db347fc05ffd7ef7f413091 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 24 Jan 2013 22:29:48 +0200 Subject: usb: common: introduce usb_state_string() this function will receive enum usb_device_state and return a human-readable string from it or, case an unknown value is passed as argument, the string "UNKNOWN". Signed-off-by: Felipe Balbi --- drivers/usb/usb-common.c | 21 +++++++++++++++++++++ include/linux/usb/ch9.h | 9 +++++++++ 2 files changed, 30 insertions(+) (limited to 'include') diff --git a/drivers/usb/usb-common.c b/drivers/usb/usb-common.c index d29503e954a..070b681e5d1 100644 --- a/drivers/usb/usb-common.c +++ b/drivers/usb/usb-common.c @@ -32,4 +32,25 @@ const char *usb_speed_string(enum usb_device_speed speed) } EXPORT_SYMBOL_GPL(usb_speed_string); +const char *usb_state_string(enum usb_device_state state) +{ + static const char *const names[] = { + [USB_STATE_NOTATTACHED] = "not attached", + [USB_STATE_ATTACHED] = "attached", + [USB_STATE_POWERED] = "powered", + [USB_STATE_RECONNECTING] = "reconnecting", + [USB_STATE_UNAUTHENTICATED] = "unauthenticated", + [USB_STATE_DEFAULT] = "default", + [USB_STATE_ADDRESS] = "addresssed", + [USB_STATE_CONFIGURED] = "configured", + [USB_STATE_SUSPENDED] = "suspended", + }; + + if (state < 0 || state >= ARRAY_SIZE(names)) + return "UNKNOWN"; + + return names[state]; +} +EXPORT_SYMBOL_GPL(usb_state_string); + MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 9c210f2283d..27603bcbb9b 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -43,4 +43,13 @@ */ extern const char *usb_speed_string(enum usb_device_speed speed); + +/** + * usb_state_string - Returns human readable name for the state. + * @state: The state to return a human-readable name for. If it's not + * any of the states devices in usb_device_state_string enum, + * the string UNKNOWN will be returned. + */ +extern const char *usb_state_string(enum usb_device_state state); + #endif /* __LINUX_USB_CH9_H */ -- cgit v1.2.3-70-g09d2 From 49401f4169c0e5a1b38f1a676d6f12eecaf77485 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 12:57:04 +0200 Subject: usb: gadget: introduce gadget state tracking that's useful information to expose to userland. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc-core.c | 23 +++++++++++++++++++++++ include/linux/usb/gadget.h | 9 +++++++++ 2 files changed, 32 insertions(+) (limited to 'include') diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 40b1d888d5a..8a1eeb24ae6 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -101,6 +101,16 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); /* ------------------------------------------------------------------------- */ +void usb_gadget_set_state(struct usb_gadget *gadget, + enum usb_device_state state) +{ + gadget->state = state; + sysfs_notify(&gadget->dev.kobj, NULL, "status"); +} +EXPORT_SYMBOL_GPL(usb_gadget_set_state); + +/* ------------------------------------------------------------------------- */ + /** * usb_gadget_udc_start - tells usb device controller to start up * @gadget: The gadget we want to get started @@ -197,6 +207,8 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) if (ret) goto err4; + usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); + mutex_unlock(&udc_lock); return 0; @@ -406,6 +418,16 @@ static ssize_t usb_udc_softconn_store(struct device *dev, } static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); +static ssize_t usb_gadget_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + struct usb_gadget *gadget = udc->gadget; + + return sprintf(buf, "%s\n", usb_state_string(gadget->state)); +} +static DEVICE_ATTR(state, S_IRUGO, usb_gadget_state_show, NULL); + #define USB_UDC_SPEED_ATTR(name, param) \ ssize_t usb_udc_##param##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ @@ -439,6 +461,7 @@ static USB_UDC_ATTR(a_alt_hnp_support); static struct attribute *usb_udc_attrs[] = { &dev_attr_srp.attr, &dev_attr_soft_connect.attr, + &dev_attr_state.attr, &dev_attr_current_speed.attr, &dev_attr_maximum_speed.attr, diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 2e297e80d59..32b734d88d6 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -482,6 +482,7 @@ struct usb_gadget_ops { * @speed: Speed of current connection to USB host. * @max_speed: Maximal speed the UDC can handle. UDC must support this * and all slower speeds. + * @state: the state we are now (attached, suspended, configured, etc) * @sg_supported: true if we can handle scatter-gather * @is_otg: True if the USB device port uses a Mini-AB jack, so that the * gadget driver must provide a USB OTG descriptor. @@ -525,6 +526,7 @@ struct usb_gadget { struct list_head ep_list; /* of usb_ep */ enum usb_device_speed speed; enum usb_device_speed max_speed; + enum usb_device_state state; unsigned sg_supported:1; unsigned is_otg:1; unsigned is_a_peripheral:1; @@ -959,6 +961,13 @@ extern void usb_gadget_unmap_request(struct usb_gadget *gadget, /*-------------------------------------------------------------------------*/ +/* utility to set gadget state properly */ + +extern void usb_gadget_set_state(struct usb_gadget *gadget, + enum usb_device_state state); + +/*-------------------------------------------------------------------------*/ + /* utility wrapping a simple endpoint selection policy */ extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *, -- cgit v1.2.3-70-g09d2 From 792bfcf7a1cd7913fa5d55f2b3a40e3275e98f6f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 26 Feb 2013 14:47:44 +0200 Subject: usb: gadget: udc-core: introduce usb_add_gadget_udc_release() not all UDC drivers need a proper release function, for those which don't need it, we udc-core will provide a no-op release method so we can remove "redefinition" of such methods in almost every UDC driver. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc-core.c | 39 ++++++++++++++++++++++++++++++++++----- include/linux/usb/gadget.h | 2 ++ 2 files changed, 36 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 2423d024654..a50811e35bd 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -166,15 +166,23 @@ static void usb_udc_release(struct device *dev) } static const struct attribute_group *usb_udc_attr_groups[]; + +static void usb_udc_nop_release(struct device *dev) +{ + dev_vdbg(dev, "%s\n", __func__); +} + /** - * usb_add_gadget_udc - adds a new gadget to the udc class driver list - * @parent: the parent device to this udc. Usually the controller - * driver's device. - * @gadget: the gadget to be added to the list + * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be added to the list. + * @release: a gadget release function. * * Returns zero on success, negative errno otherwise. */ -int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) { struct usb_udc *udc; int ret = -ENOMEM; @@ -190,6 +198,13 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) gadget->dev.dma_parms = parent->dma_parms; gadget->dev.dma_mask = parent->dma_mask; + if (release) { + gadget->dev.release = release; + } else { + if (!gadget->dev.release) + gadget->dev.release = usb_udc_nop_release; + } + ret = device_register(&gadget->dev); if (ret) goto err2; @@ -231,6 +246,20 @@ err2: err1: return ret; } +EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); + +/** + * usb_add_gadget_udc - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller + * driver's device. + * @gadget: the gadget to be added to the list + * + * Returns zero on success, negative errno otherwise. + */ +int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +{ + return usb_add_gadget_udc_release(parent, gadget, NULL); +} EXPORT_SYMBOL_GPL(usb_add_gadget_udc); static void usb_gadget_remove_driver(struct usb_udc *udc) diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 32b734d88d6..c454a88abf2 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -874,6 +874,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver); */ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver); +extern int usb_add_gadget_udc_release(struct device *parent, + struct usb_gadget *gadget, void (*release)(struct device *dev)); extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget); extern void usb_del_gadget_udc(struct usb_gadget *gadget); extern int udc_attach_driver(const char *name, -- cgit v1.2.3-70-g09d2 From 42c0bf1ce7c067bbc3e77d5626f102a16bc4fb6b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 10:39:57 +0200 Subject: usb: otg: prefix otg_state_string with usb_ all other functions under drivers/usb/ start with usb_, let's do the same thing. This patch is in preparation for moving otg_state_string to usb-common.c and deleting otg.c completely. Signed-off-by: Felipe Balbi --- drivers/usb/musb/am35x.c | 8 ++++---- drivers/usb/musb/blackfin.c | 6 +++--- drivers/usb/musb/da8xx.c | 8 ++++---- drivers/usb/musb/davinci.c | 4 ++-- drivers/usb/musb/musb_core.c | 39 ++++++++++++++++++++------------------- drivers/usb/musb/musb_dsps.c | 8 ++++---- drivers/usb/musb/musb_gadget.c | 8 ++++---- drivers/usb/musb/musb_host.c | 2 +- drivers/usb/musb/musb_virthub.c | 4 ++-- drivers/usb/musb/omap2430.c | 6 +++--- drivers/usb/musb/tusb6010.c | 14 +++++++------- drivers/usb/otg/fsl_otg.c | 2 +- drivers/usb/otg/isp1301_omap.c | 6 +++--- drivers/usb/otg/otg.c | 4 ++-- drivers/usb/otg/otg_fsm.c | 2 +- include/linux/usb/otg.h | 4 ++-- 16 files changed, 63 insertions(+), 62 deletions(-) (limited to 'include') diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 59eea219034..2231850c062 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -149,7 +149,7 @@ static void otg_timer(unsigned long _musb) */ devctl = musb_readb(mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -195,7 +195,7 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { dev_dbg(musb->controller, "%s active, deleting timer\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; return; @@ -208,7 +208,7 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); } @@ -298,7 +298,7 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci) /* NOTE: this must complete power-on within 100 ms. */ dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index dbb31b30c7f..5e63b160db0 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -280,13 +280,13 @@ static void musb_conn_timer_handler(unsigned long _musb) break; default: dev_dbg(musb->controller, "%s state not handled\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); break; } spin_unlock_irqrestore(&musb->lock, flags); dev_dbg(musb->controller, "state is %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } static void bfin_musb_enable(struct musb *musb) @@ -307,7 +307,7 @@ static void bfin_musb_set_vbus(struct musb *musb, int is_on) dev_dbg(musb->controller, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL)); } diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 7c71769d71f..ea7e591093e 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -198,7 +198,7 @@ static void otg_timer(unsigned long _musb) */ devctl = musb_readb(mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -267,7 +267,7 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { dev_dbg(musb->controller, "%s active, deleting timer\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; return; @@ -280,7 +280,7 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); } @@ -360,7 +360,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index e040d910373..bea6cc35471 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -215,7 +215,7 @@ static void otg_timer(unsigned long _musb) */ devctl = musb_readb(mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "poll devctl %02x (%s)\n", devctl, - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -349,7 +349,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) davinci_musb_source_power(musb, drvvbus, 0); dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); retval = IRQ_HANDLED; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index fad8571ed43..6bd879257e4 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -372,13 +372,13 @@ static void musb_otg_timer_func(unsigned long data) case OTG_STATE_A_SUSPEND: case OTG_STATE_A_WAIT_BCON: dev_dbg(musb->controller, "HNP: %s timeout\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); musb_platform_set_vbus(musb, 0); musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; default: dev_dbg(musb->controller, "HNP: Unhandled mode %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } musb->ignore_disconnect = 0; spin_unlock_irqrestore(&musb->lock, flags); @@ -393,13 +393,14 @@ void musb_hnp_stop(struct musb *musb) void __iomem *mbase = musb->mregs; u8 reg; - dev_dbg(musb->controller, "HNP: stop from %s\n", otg_state_string(musb->xceiv->state)); + dev_dbg(musb->controller, "HNP: stop from %s\n", + usb_otg_state_string(musb->xceiv->state)); switch (musb->xceiv->state) { case OTG_STATE_A_PERIPHERAL: musb_g_disconnect(musb); dev_dbg(musb->controller, "HNP: back to %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); break; case OTG_STATE_B_HOST: dev_dbg(musb->controller, "HNP: Disabling HR\n"); @@ -413,7 +414,7 @@ void musb_hnp_stop(struct musb *musb) break; default: dev_dbg(musb->controller, "HNP: Stopping in unknown state %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } /* @@ -451,7 +452,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ if (int_usb & MUSB_INTR_RESUME) { handled = IRQ_HANDLED; - dev_dbg(musb->controller, "RESUME (%s)\n", otg_state_string(musb->xceiv->state)); + dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->state)); if (devctl & MUSB_DEVCTL_HM) { void __iomem *mbase = musb->mregs; @@ -493,7 +494,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, default: WARNING("bogus %s RESUME (%s)\n", "host", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } else { switch (musb->xceiv->state) { @@ -522,7 +523,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, default: WARNING("bogus %s RESUME (%s)\n", "peripheral", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } } @@ -538,7 +539,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } dev_dbg(musb->controller, "SESSION_REQUEST (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); /* IRQ arrives from ID pin sense or (later, if VBUS power * is removed) SRP. responses are time critical: @@ -603,7 +604,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, } dev_dbg(musb->controller, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), devctl, ({ char *s; switch (devctl & MUSB_DEVCTL_VBUS) { @@ -628,7 +629,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (int_usb & MUSB_INTR_SUSPEND) { dev_dbg(musb->controller, "SUSPEND (%s) devctl %02x\n", - otg_state_string(musb->xceiv->state), devctl); + usb_otg_state_string(musb->xceiv->state), devctl); handled = IRQ_HANDLED; switch (musb->xceiv->state) { @@ -745,12 +746,12 @@ b_host: usb_hcd_resume_root_hub(hcd); dev_dbg(musb->controller, "CONNECT (%s) devctl %02x\n", - otg_state_string(musb->xceiv->state), devctl); + usb_otg_state_string(musb->xceiv->state), devctl); } if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) { dev_dbg(musb->controller, "DISCONNECT (%s) as %s, devctl %02x\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), MUSB_MODE(musb), devctl); handled = IRQ_HANDLED; @@ -787,7 +788,7 @@ b_host: break; default: WARNING("unhandled DISCONNECT transition (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); break; } } @@ -813,7 +814,7 @@ b_host: } } else { dev_dbg(musb->controller, "BUS RESET as %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); switch (musb->xceiv->state) { case OTG_STATE_A_SUSPEND: /* We need to ignore disconnect on suspend @@ -826,7 +827,7 @@ b_host: case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ /* never use invalid T(a_wait_bcon) */ dev_dbg(musb->controller, "HNP: in %s, %d msec timeout\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), TA_WAIT_BCON(musb)); mod_timer(&musb->otg_timer, jiffies + msecs_to_jiffies(TA_WAIT_BCON(musb))); @@ -838,7 +839,7 @@ b_host: break; case OTG_STATE_B_WAIT_ACON: dev_dbg(musb->controller, "HNP: RESET (%s), to b_peripheral\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb_g_reset(musb); break; @@ -850,7 +851,7 @@ b_host: break; default: dev_dbg(musb->controller, "Unhandled BUS RESET as %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } } @@ -1632,7 +1633,7 @@ musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) int ret = -EINVAL; spin_lock_irqsave(&musb->lock, flags); - ret = sprintf(buf, "%s\n", otg_state_string(musb->xceiv->state)); + ret = sprintf(buf, "%s\n", usb_otg_state_string(musb->xceiv->state)); spin_unlock_irqrestore(&musb->lock, flags); return ret; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 4b4987461ad..1ea553d2b77 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -225,7 +225,7 @@ static void otg_timer(unsigned long _musb) */ devctl = dsps_readb(mregs, MUSB_DEVCTL); dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -274,7 +274,7 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { dev_dbg(musb->controller, "%s active, deleting timer\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); del_timer(&glue->timer[pdev->id]); glue->last_timer[pdev->id] = jiffies; return; @@ -289,7 +289,7 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout) glue->last_timer[pdev->id] = timeout; dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), jiffies_to_msecs(timeout - jiffies)); mod_timer(&glue->timer[pdev->id], timeout); } @@ -378,7 +378,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) /* NOTE: this must complete power-on within 100 ms. */ dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 6101ebf803f..e8408883ab0 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1571,7 +1571,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) goto done; default: dev_dbg(musb->controller, "Unhandled wake: %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); goto done; } @@ -1970,7 +1970,7 @@ void musb_g_resume(struct musb *musb) break; default: WARNING("unhandled RESUME transition (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } @@ -2000,7 +2000,7 @@ void musb_g_suspend(struct musb *musb) * A_PERIPHERAL may need care too */ WARNING("unhandled SUSPEND transition (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } @@ -2034,7 +2034,7 @@ void musb_g_disconnect(struct musb *musb) switch (musb->xceiv->state) { default: dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); break; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 1ce1fcf3f3e..51e9e8a3844 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2453,7 +2453,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd) if (musb->is_active) { WARNING("trying to suspend as %s while active\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); return -EBUSY; } else return 0; diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index f70579154de..ef7d11045f5 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -95,7 +95,7 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) break; default: dev_dbg(musb->controller, "bogus rh suspend? %s\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } else if (power & MUSB_POWER_SUSPENDM) { power &= ~MUSB_POWER_SUSPENDM; @@ -203,7 +203,7 @@ void musb_root_disconnect(struct musb *musb) break; default: dev_dbg(musb->controller, "host disconnect (%s)\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } } diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 1a42a458f2c..8ba9bb2a91a 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -117,7 +117,7 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) if (musb->is_active || ((musb->a_wait_bcon == 0) && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { dev_dbg(musb->controller, "%s active, deleting timer\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); del_timer(&musb_idle_timer); last_timer = jiffies; return; @@ -134,7 +134,7 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(&musb_idle_timer, timeout); } @@ -200,7 +200,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on) dev_dbg(musb->controller, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL)); } diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 464bd23cccd..7369ba33c94 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -423,7 +423,7 @@ static void musb_do_idle(unsigned long _musb) && (musb->idle_timeout == 0 || time_after(jiffies, musb->idle_timeout))) { dev_dbg(musb->controller, "Nothing connected %s, turning off VBUS\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); } /* FALLTHROUGH */ case OTG_STATE_A_IDLE: @@ -478,7 +478,7 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) if (musb->is_active || ((musb->a_wait_bcon == 0) && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { dev_dbg(musb->controller, "%s active, deleting timer\n", - otg_state_string(musb->xceiv->state)); + usb_otg_state_string(musb->xceiv->state)); del_timer(&musb_idle_timer); last_timer = jiffies; return; @@ -495,7 +495,7 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) last_timer = timeout; dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(&musb_idle_timer, timeout); } @@ -571,7 +571,7 @@ static void tusb_musb_set_vbus(struct musb *musb, int is_on) musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); dev_dbg(musb->controller, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", - otg_state_string(musb->xceiv->state), + usb_otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL), musb_readl(tbase, TUSB_DEV_OTG_STAT), conf, prcm); @@ -678,13 +678,13 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) musb->is_active = 0; } dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", - otg_state_string(musb->xceiv->state), otg_stat); + usb_otg_state_string(musb->xceiv->state), otg_stat); idle_timeout = jiffies + (1 * HZ); schedule_work(&musb->irq_work); } else /* A-dev state machine */ { dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", - otg_state_string(musb->xceiv->state), otg_stat); + usb_otg_state_string(musb->xceiv->state), otg_stat); switch (musb->xceiv->state) { case OTG_STATE_A_IDLE: @@ -733,7 +733,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) u8 devctl; dev_dbg(musb->controller, "%s timer, %03x\n", - otg_state_string(musb->xceiv->state), otg_stat); + usb_otg_state_string(musb->xceiv->state), otg_stat); switch (musb->xceiv->state) { case OTG_STATE_A_WAIT_VRISE: diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c index 37e8e157831..72a2a00c248 100644 --- a/drivers/usb/otg/fsl_otg.c +++ b/drivers/usb/otg/fsl_otg.c @@ -992,7 +992,7 @@ static int show_fsl_usb2_otg_state(struct device *dev, /* State */ t = scnprintf(next, size, "OTG state: %s\n\n", - otg_state_string(fsl_otg_dev->phy.state)); + usb_otg_state_string(fsl_otg_dev->phy.state)); size -= t; next += t; diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index af9cb11626b..8fe0c3b9526 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -236,7 +236,7 @@ isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits) static inline const char *state_name(struct isp1301 *isp) { - return otg_state_string(isp->phy.state); + return usb_otg_state_string(isp->phy.state); } /*-------------------------------------------------------------------------*/ @@ -481,7 +481,7 @@ static void check_state(struct isp1301 *isp, const char *tag) if (isp->phy.state == state && !extra) return; pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag, - otg_state_string(state), fsm, state_name(isp), + usb_otg_state_string(state), fsm, state_name(isp), omap_readl(OTG_CTRL)); } @@ -1077,7 +1077,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat) if (state != isp->phy.state) pr_debug(" isp, %s -> %s\n", - otg_state_string(state), state_name(isp)); + usb_otg_state_string(state), state_name(isp)); #ifdef CONFIG_USB_OTG /* update the OTG controller state to match the isp1301; may diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 358cfd9bce8..fd9a4b7bebe 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -11,7 +11,7 @@ #include #include -const char *otg_state_string(enum usb_otg_state state) +const char *usb_otg_state_string(enum usb_otg_state state) { switch (state) { case OTG_STATE_A_IDLE: @@ -44,4 +44,4 @@ const char *otg_state_string(enum usb_otg_state state) return "UNDEFINED"; } } -EXPORT_SYMBOL(otg_state_string); +EXPORT_SYMBOL(usb_otg_state_string); diff --git a/drivers/usb/otg/otg_fsm.c b/drivers/usb/otg/otg_fsm.c index ade131a8ae5..1f729a15dec 100644 --- a/drivers/usb/otg/otg_fsm.c +++ b/drivers/usb/otg/otg_fsm.c @@ -119,7 +119,7 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) state_changed = 1; if (fsm->otg->phy->state == new_state) return 0; - VDBG("Set state: %s\n", otg_state_string(new_state)); + VDBG("Set state: %s\n", usb_otg_state_string(new_state)); otg_leave_state(fsm, fsm->otg->phy->state); switch (new_state) { case OTG_STATE_B_IDLE: diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index e8a5fe87c6b..9f9fb3927b0 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -37,9 +37,9 @@ struct usb_otg { }; #ifdef CONFIG_USB_OTG_UTILS -extern const char *otg_state_string(enum usb_otg_state state); +extern const char *usb_otg_state_string(enum usb_otg_state state); #else -static inline const char *otg_state_string(enum usb_otg_state state) +static inline const char *usb_otg_state_string(enum usb_otg_state state) { return NULL; } -- cgit v1.2.3-70-g09d2 From 7009bdd7f31ed6e769af0f76e2368bb6033be572 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 10:45:56 +0200 Subject: usb: otg: move usb_otg_state_string to usb-common.c otg.c only had a single function definition which might make more sense to be placed in usb-common.c. While doing that, we also delete otg.c since it's now empty. Signed-off-by: Felipe Balbi --- drivers/usb/otg/Makefile | 3 --- drivers/usb/otg/otg.c | 47 ----------------------------------------------- drivers/usb/usb-common.c | 26 ++++++++++++++++++++++++++ include/linux/usb/otg.h | 7 ------- 4 files changed, 26 insertions(+), 57 deletions(-) delete mode 100644 drivers/usb/otg/otg.c (limited to 'include') diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index a844b8d35d1..6abc45388e2 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -5,9 +5,6 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG -# infrastructure -obj-$(CONFIG_USB_OTG_UTILS) += otg.o - # transceiver drivers obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c deleted file mode 100644 index fd9a4b7bebe..00000000000 --- a/drivers/usb/otg/otg.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * otg.c -- USB OTG utility code - * - * Copyright (C) 2004 Texas Instruments - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ -#include -#include - -const char *usb_otg_state_string(enum usb_otg_state state) -{ - switch (state) { - case OTG_STATE_A_IDLE: - return "a_idle"; - case OTG_STATE_A_WAIT_VRISE: - return "a_wait_vrise"; - case OTG_STATE_A_WAIT_BCON: - return "a_wait_bcon"; - case OTG_STATE_A_HOST: - return "a_host"; - case OTG_STATE_A_SUSPEND: - return "a_suspend"; - case OTG_STATE_A_PERIPHERAL: - return "a_peripheral"; - case OTG_STATE_A_WAIT_VFALL: - return "a_wait_vfall"; - case OTG_STATE_A_VBUS_ERR: - return "a_vbus_err"; - case OTG_STATE_B_IDLE: - return "b_idle"; - case OTG_STATE_B_SRP_INIT: - return "b_srp_init"; - case OTG_STATE_B_PERIPHERAL: - return "b_peripheral"; - case OTG_STATE_B_WAIT_ACON: - return "b_wait_acon"; - case OTG_STATE_B_HOST: - return "b_host"; - default: - return "UNDEFINED"; - } -} -EXPORT_SYMBOL(usb_otg_state_string); diff --git a/drivers/usb/usb-common.c b/drivers/usb/usb-common.c index 070b681e5d1..0db0a919d72 100644 --- a/drivers/usb/usb-common.c +++ b/drivers/usb/usb-common.c @@ -14,6 +14,32 @@ #include #include #include +#include + +const char *usb_otg_state_string(enum usb_otg_state state) +{ + static const char *const names[] = { + [OTG_STATE_A_IDLE] = "a_idle", + [OTG_STATE_A_WAIT_VRISE] = "a_wait_vrise", + [OTG_STATE_A_WAIT_BCON] = "a_wait_bcon", + [OTG_STATE_A_HOST] = "a_host", + [OTG_STATE_A_SUSPEND] = "a_suspend", + [OTG_STATE_A_PERIPHERAL] = "a_peripheral", + [OTG_STATE_A_WAIT_VFALL] = "a_wait_vfall", + [OTG_STATE_A_VBUS_ERR] = "a_vbus_err", + [OTG_STATE_B_IDLE] = "b_idle", + [OTG_STATE_B_SRP_INIT] = "b_srp_init", + [OTG_STATE_B_PERIPHERAL] = "b_peripheral", + [OTG_STATE_B_WAIT_ACON] = "b_wait_acon", + [OTG_STATE_B_HOST] = "b_host", + }; + + if (state < 0 || state >= ARRAY_SIZE(names)) + return "UNDEFINED"; + + return names[state]; +} +EXPORT_SYMBOL_GPL(usb_otg_state_string); const char *usb_speed_string(enum usb_device_speed speed) { diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 9f9fb3927b0..291e01ba32e 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -36,14 +36,7 @@ struct usb_otg { }; -#ifdef CONFIG_USB_OTG_UTILS extern const char *usb_otg_state_string(enum usb_otg_state state); -#else -static inline const char *usb_otg_state_string(enum usb_otg_state state) -{ - return NULL; -} -#endif /* Context: can sleep */ static inline int -- cgit v1.2.3-70-g09d2 From edc7cb2e955f222fe51cd44c1cf9c94d58017344 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 7 Mar 2013 11:13:43 +0200 Subject: usb: phy: make it a menuconfig We already have a considerable amount of USB PHY drivers, making it a menuconfig just prevents us from adding too much churn to USB's menuconfig. While at that, also select USB_OTG_UTILS from this new menuconfig just to keep backwards compatibility until we manage to remove that symbol. Signed-off-by: Felipe Balbi --- drivers/Makefile | 2 +- drivers/usb/phy/Kconfig | 17 ++++++++++++----- drivers/usb/phy/Makefile | 2 +- include/linux/usb/phy.h | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/Makefile b/drivers/Makefile index dce39a95fa7..3c200a243af 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -79,7 +79,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/ obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_UWB) += uwb/ -obj-$(CONFIG_USB_OTG_UTILS) += usb/ +obj-$(CONFIG_USB_PHY) += usb/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_PCI) += usb/ obj-$(CONFIG_USB_GADGET) += usb/ diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 32ce740a9dd..832cd694fb8 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -1,8 +1,17 @@ # # Physical Layer USB driver configuration # -comment "USB Physical Layer drivers" - depends on USB || USB_GADGET +menuconfig USB_PHY + tristate "USB Physical Layer drivers" + select USB_OTG_UTILS + help + USB controllers (those which are host, device or DRD) need a + device to handle the physical layer signalling, commonly called + a PHY. + + The following drivers add support for such PHY devices. + +if USB_PHY config USB_OTG_UTILS bool @@ -10,8 +19,6 @@ config USB_OTG_UTILS Select this to make sure the build includes objects from the OTG infrastructure directory. -if USB || USB_GADGET - # # USB Transceiver Drivers # @@ -206,4 +213,4 @@ 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). -endif # USB || OTG +endif # USB_PHY diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 34488ceef49..d10a8b387ff 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -4,7 +4,7 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG -obj-$(CONFIG_USB_OTG_UTILS) += phy.o +obj-$(CONFIG_USB_PHY) += phy.o # transceiver drivers, keep the list sorted diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index 15847cbdb51..b001dc3d635 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -161,7 +161,7 @@ usb_phy_shutdown(struct usb_phy *x) } /* for usb host and peripheral controller drivers */ -#ifdef CONFIG_USB_OTG_UTILS +#if IS_ENABLED(CONFIG_USB_PHY) extern struct usb_phy *usb_get_phy(enum usb_phy_type type); extern struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type); -- cgit v1.2.3-70-g09d2 From b774212ea5f13911a5e0211a7088e42dad46b4c8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 Mar 2013 13:22:58 +0200 Subject: usb: phy: introduce ->set_vbus() method this method will be used to enable or disable the charge pump. Whenever we have DRD devices, we need to be able to turn VBUS on or off whenever we want. Note that in the ideal case, this would be controlled by the ID-pin Interrupt, but not all devices have ID-pin properly routed since manufacturers can choose to save that trace if they're building a host-only product out of a DRD IP. This is also useful during debugging where we might not have the proper cable hanging around. Signed-off-by: Felipe Balbi --- include/linux/usb/phy.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index b001dc3d635..b7c2217c585 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -91,6 +91,9 @@ struct usb_phy { int (*init)(struct usb_phy *x); void (*shutdown)(struct usb_phy *x); + /* enable/disable VBUS */ + int (*set_vbus)(struct usb_phy *x, int on); + /* effective for B devices, ignored for A-peripheral */ int (*set_power)(struct usb_phy *x, unsigned mA); @@ -160,6 +163,24 @@ usb_phy_shutdown(struct usb_phy *x) x->shutdown(x); } +static inline int +usb_phy_vbus_on(struct usb_phy *x) +{ + if (!x->set_vbus) + return 0; + + return x->set_vbus(x, true); +} + +static inline int +usb_phy_vbus_off(struct usb_phy *x) +{ + if (!x->set_vbus) + return 0; + + return x->set_vbus(x, false); +} + /* for usb host and peripheral controller drivers */ #if IS_ENABLED(CONFIG_USB_PHY) extern struct usb_phy *usb_get_phy(enum usb_phy_type type); -- cgit v1.2.3-70-g09d2 From 2ba7943af0f0cca5a069cd3aff807815bc76fff1 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 7 Mar 2013 18:51:44 +0530 Subject: usb: dwc3: dwc3-omap: return -EPROBE_DEFER if probe has not yet executed return -EPROBE_DEFER from dwc3_omap_mailbox in dwc3-omap.c, if the probe of dwc3-omap has not yet been executed or failed. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 7 +++++-- include/linux/usb/dwc3-omap.h | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 546f1fd8492..2fe9723ff1d 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -138,11 +138,14 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value) writel(value, base + offset); } -void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) +int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) { u32 val; struct dwc3_omap *omap = _omap; + if (!omap) + return -EPROBE_DEFER; + switch (status) { case OMAP_DWC3_ID_GROUND: dev_dbg(omap->dev, "ID GND\n"); @@ -185,7 +188,7 @@ void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) dev_dbg(omap->dev, "ID float\n"); } - return; + return 0; } EXPORT_SYMBOL_GPL(dwc3_omap_mailbox); diff --git a/include/linux/usb/dwc3-omap.h b/include/linux/usb/dwc3-omap.h index 51eae14477f..5615f4d8272 100644 --- a/include/linux/usb/dwc3-omap.h +++ b/include/linux/usb/dwc3-omap.h @@ -19,11 +19,11 @@ enum omap_dwc3_vbus_id_status { }; #if (defined(CONFIG_USB_DWC3) || defined(CONFIG_USB_DWC3_MODULE)) -extern void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status); +extern int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status); #else -static inline void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) +static inline int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) { - return; + return -ENODEV; } #endif -- cgit v1.2.3-70-g09d2 From b7fa5c2aec5be083eb2719b405089703608e9bc6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 14 Mar 2013 17:59:06 +0200 Subject: usb: phy: return -ENXIO when PHY layer isn't enabled in cases where PHY layer isn't enabled, we want to still return an error code (actually an error pointer) so that our users don't need to cope with either error pointer of NULL. This will simplify users as below: - return IS_ERR(phy) ? PTR_ERR(phy) : -ENODEV; + return PTR_ERR(phy); Acked-by: Kishon Vijay Abraham I Reported-by: Alan Stern Signed-off-by: Felipe Balbi --- include/linux/usb/phy.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index b7c2217c585..6b5978f5763 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -197,29 +197,29 @@ extern int usb_bind_phy(const char *dev_name, u8 index, #else static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) { - return NULL; + return ERR_PTR(-ENXIO); } static inline struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) { - return NULL; + return ERR_PTR(-ENXIO); } static inline struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index) { - return NULL; + return ERR_PTR(-ENXIO); } static inline struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index) { - return NULL; + return ERR_PTR(-ENXIO); } static inline struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, const char *phandle, u8 index) { - return NULL; + return ERR_PTR(-ENXIO); } static inline void usb_put_phy(struct usb_phy *x) -- cgit v1.2.3-70-g09d2 From e6251fc244a18a53830f38de84e4fcaee2f58662 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Fri, 15 Mar 2013 16:32:05 +0100 Subject: itg3200: fix incorrect ifdef comment Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- include/linux/iio/gyro/itg3200.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/gyro/itg3200.h b/include/linux/iio/gyro/itg3200.h index c53f16914b7..2a820850f28 100644 --- a/include/linux/iio/gyro/itg3200.h +++ b/include/linux/iio/gyro/itg3200.h @@ -149,6 +149,6 @@ static inline void itg3200_buffer_unconfigure(struct iio_dev *indio_dev) { } -#endif /* CONFIG_IIO_RING_BUFFER */ +#endif /* CONFIG_IIO_BUFFER */ #endif /* ITG3200_H_ */ -- cgit v1.2.3-70-g09d2 From 5a20d339c785d98d8b050b9afc098e4184a6098c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 3 Mar 2013 13:58:05 +0900 Subject: f2fs: align f2fs maximum name length to linux based filesystem The maximum filename length supported in linux is 255 characters. So let's follow that. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 3 +++ fs/f2fs/namei.c | 2 +- fs/f2fs/super.c | 2 +- include/linux/f2fs_fs.h | 17 +++++++++-------- 4 files changed, 14 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index a1f38443ece..2851ae6948a 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -189,6 +189,9 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, unsigned int max_depth; unsigned int level; + if (namelen > F2FS_NAME_LEN) + return NULL; + if (npages == 0) return NULL; diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 1a49b881bac..d4a171b1a68 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -197,7 +197,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, struct f2fs_dir_entry *de; struct page *page; - if (dentry->d_name.len > F2FS_MAX_NAME_LEN) + if (dentry->d_name.len > F2FS_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); de = f2fs_find_entry(dir, &dentry->d_name, &page); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 8c117649a03..1c7f595ca47 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -180,7 +180,7 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_files = sbi->total_node_count; buf->f_ffree = sbi->total_node_count - valid_inode_count(sbi); - buf->f_namelen = F2FS_MAX_NAME_LEN; + buf->f_namelen = F2FS_NAME_LEN; buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index f9a12f6243a..df6fab82f87 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -139,7 +139,7 @@ struct f2fs_extent { __le32 len; /* lengh of the extent */ } __packed; -#define F2FS_MAX_NAME_LEN 256 +#define F2FS_NAME_LEN 255 #define ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ #define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ @@ -165,7 +165,8 @@ struct f2fs_inode { __le32 i_flags; /* file attributes */ __le32 i_pino; /* parent inode number */ __le32 i_namelen; /* file name length */ - __u8 i_name[F2FS_MAX_NAME_LEN]; /* file name for SPOR */ + __u8 i_name[F2FS_NAME_LEN]; /* file name for SPOR */ + __u8 i_reserved2; /* for backward compatibility */ struct f2fs_extent i_ext; /* caching a largest extent */ @@ -362,10 +363,10 @@ struct f2fs_summary_block { typedef __le32 f2fs_hash_t; /* One directory entry slot covers 8bytes-long file name */ -#define F2FS_NAME_LEN 8 -#define F2FS_NAME_LEN_BITS 3 +#define F2FS_SLOT_LEN 8 +#define F2FS_SLOT_LEN_BITS 3 -#define GET_DENTRY_SLOTS(x) ((x + F2FS_NAME_LEN - 1) >> F2FS_NAME_LEN_BITS) +#define GET_DENTRY_SLOTS(x) ((x + F2FS_SLOT_LEN - 1) >> F2FS_SLOT_LEN_BITS) /* the number of dentry in a block */ #define NR_DENTRY_IN_BLOCK 214 @@ -377,10 +378,10 @@ typedef __le32 f2fs_hash_t; #define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \ BITS_PER_BYTE) #define SIZE_OF_RESERVED (PAGE_SIZE - ((SIZE_OF_DIR_ENTRY + \ - F2FS_NAME_LEN) * \ + F2FS_SLOT_LEN) * \ NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP)) -/* One directory entry slot representing F2FS_NAME_LEN-sized file name */ +/* One directory entry slot representing F2FS_SLOT_LEN-sized file name */ struct f2fs_dir_entry { __le32 hash_code; /* hash code of file name */ __le32 ino; /* inode number */ @@ -394,7 +395,7 @@ struct f2fs_dentry_block { __u8 dentry_bitmap[SIZE_OF_DENTRY_BITMAP]; __u8 reserved[SIZE_OF_RESERVED]; struct f2fs_dir_entry dentry[NR_DENTRY_IN_BLOCK]; - __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_NAME_LEN]; + __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN]; } __packed; /* file types used in inode_info->flags */ -- cgit v1.2.3-70-g09d2 From 443580486e3b96578928c1c91e8fbdcf0c9c9c7f Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Mon, 18 Feb 2013 23:28:34 +0900 Subject: irqchip: Renesas INTC External IRQ pin driver This patch adds a driver for external IRQ pins connected to the INTC block on recent SoCs from Renesas. The INTC hardware block usually contains a rather wide range of features ranging from external IRQ pin handling to legacy interrupt controller support. On older SoCs the INTC is used as a general purpose interrupt controller both for external IRQ pins and on-chip devices. On more recent ARM based SoCs with Cortex-A9 the main interrupt controller is the GIC, but IRQ trigger setup still need to happen in the INTC hardware block. This driver implements the glue code needed to configure IRQ trigger and also handle mask/unmask and demux of external IRQ pins hooked up from the INTC to the GIC. Tested on sh73a0 and r8a7779. The hardware varies quite a bit with SoC model, for instance register width and bitfield widths vary wildly. The driver requires one GIC SPI per external IRQ pin to operate. Each driver instance will handle up to 8 external IRQ pins. The SoCs using this driver are currently mainly used together with regular platform devices so this driver allows configuration via platform data to support things like static interrupt base address. DT support will be added incrementally in the not so distant future. Signed-off-by: Magnus Damm Acked-by: Thomas Gleixner Signed-off-by: Simon Horman --- drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-renesas-intc-irqpin.c | 464 +++++++++++++++++++++ .../linux/platform_data/irq-renesas-intc-irqpin.h | 10 + 4 files changed, 479 insertions(+) create mode 100644 drivers/irqchip/irq-renesas-intc-irqpin.c create mode 100644 include/linux/platform_data/irq-renesas-intc-irqpin.h (limited to 'include') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a350969e5ef..0f5f1c3825b 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -25,6 +25,10 @@ config ARM_VIC_NR The maximum number of VICs available in the system, for power management. +config RENESAS_INTC_IRQPIN + bool + select IRQ_DOMAIN + config VERSATILE_FPGA_IRQ bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 98e3b87bdf1..1aaa4073ab6 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o obj-$(CONFIG_ARM_VIC) += irq-vic.o +obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c new file mode 100644 index 00000000000..1e5058a5651 --- /dev/null +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -0,0 +1,464 @@ +/* + * Renesas INTC External IRQ Pin Driver + * + * Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ + +#define INTC_IRQPIN_REG_SENSE 0 /* ICRn */ +#define INTC_IRQPIN_REG_PRIO 1 /* INTPRInn */ +#define INTC_IRQPIN_REG_SOURCE 2 /* INTREQnn */ +#define INTC_IRQPIN_REG_MASK 3 /* INTMSKnn */ +#define INTC_IRQPIN_REG_CLEAR 4 /* INTMSKCLRnn */ +#define INTC_IRQPIN_REG_NR 5 + +/* INTC external IRQ PIN hardware register access: + * + * SENSE is read-write 32-bit with 2-bits or 4-bits per IRQ (*) + * PRIO is read-write 32-bit with 4-bits per IRQ (**) + * SOURCE is read-only 32-bit or 8-bit with 1-bit per IRQ (***) + * MASK is write-only 32-bit or 8-bit with 1-bit per IRQ (***) + * CLEAR is write-only 32-bit or 8-bit with 1-bit per IRQ (***) + * + * (*) May be accessed by more than one driver instance - lock needed + * (**) Read-modify-write access by one driver instance - lock needed + * (***) Accessed by one driver instance only - no locking needed + */ + +struct intc_irqpin_iomem { + void __iomem *iomem; + unsigned long (*read)(void __iomem *iomem); + void (*write)(void __iomem *iomem, unsigned long data); + int width; +}; + +struct intc_irqpin_irq { + int hw_irq; + int irq; + struct intc_irqpin_priv *p; +}; + +struct intc_irqpin_priv { + struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; + struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; + struct renesas_intc_irqpin_config config; + unsigned int number_of_irqs; + struct platform_device *pdev; + struct irq_chip irq_chip; + struct irq_domain *irq_domain; +}; + +static unsigned long intc_irqpin_read32(void __iomem *iomem) +{ + return ioread32(iomem); +} + +static unsigned long intc_irqpin_read8(void __iomem *iomem) +{ + return ioread8(iomem); +} + +static void intc_irqpin_write32(void __iomem *iomem, unsigned long data) +{ + iowrite32(data, iomem); +} + +static void intc_irqpin_write8(void __iomem *iomem, unsigned long data) +{ + iowrite8(data, iomem); +} + +static inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p, + int reg) +{ + struct intc_irqpin_iomem *i = &p->iomem[reg]; + return i->read(i->iomem); +} + +static inline void intc_irqpin_write(struct intc_irqpin_priv *p, + int reg, unsigned long data) +{ + struct intc_irqpin_iomem *i = &p->iomem[reg]; + i->write(i->iomem, data); +} + +static inline unsigned long intc_irqpin_hwirq_mask(struct intc_irqpin_priv *p, + int reg, int hw_irq) +{ + return BIT((p->iomem[reg].width - 1) - hw_irq); +} + +static inline void intc_irqpin_irq_write_hwirq(struct intc_irqpin_priv *p, + int reg, int hw_irq) +{ + intc_irqpin_write(p, reg, intc_irqpin_hwirq_mask(p, reg, hw_irq)); +} + +static DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */ + +static void intc_irqpin_read_modify_write(struct intc_irqpin_priv *p, + int reg, int shift, + int width, int value) +{ + unsigned long flags; + unsigned long tmp; + + raw_spin_lock_irqsave(&intc_irqpin_lock, flags); + + tmp = intc_irqpin_read(p, reg); + tmp &= ~(((1 << width) - 1) << shift); + tmp |= value << shift; + intc_irqpin_write(p, reg, tmp); + + raw_spin_unlock_irqrestore(&intc_irqpin_lock, flags); +} + +static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, + int irq, int do_mask) +{ + int bitfield_width = 4; /* PRIO assumed to have fixed bitfield width */ + int shift = (7 - irq) * bitfield_width; /* PRIO assumed to be 32-bit */ + + intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_PRIO, + shift, bitfield_width, + do_mask ? 0 : (1 << bitfield_width) - 1); +} + +static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) +{ + int bitfield_width = p->config.sense_bitfield_width; + int shift = (7 - irq) * bitfield_width; /* SENSE assumed to be 32-bit */ + + dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); + + if (value >= (1 << bitfield_width)) + return -EINVAL; + + intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_SENSE, shift, + bitfield_width, value); + return 0; +} + +static void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) +{ + dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", + str, i->irq, i->hw_irq, + irq_find_mapping(i->p->irq_domain, i->hw_irq)); +} + +static void intc_irqpin_irq_enable(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + intc_irqpin_dbg(&p->irq[hw_irq], "enable"); + intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); +} + +static void intc_irqpin_irq_disable(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + intc_irqpin_dbg(&p->irq[hw_irq], "disable"); + intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); +} + +static void intc_irqpin_irq_enable_force(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int irq = p->irq[irqd_to_hwirq(d)].irq; + + intc_irqpin_irq_enable(d); + irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); +} + +static void intc_irqpin_irq_disable_force(struct irq_data *d) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int irq = p->irq[irqd_to_hwirq(d)].irq; + + irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); + intc_irqpin_irq_disable(d); +} + +#define INTC_IRQ_SENSE_VALID 0x10 +#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) + +static unsigned char intc_irqpin_sense[IRQ_TYPE_SENSE_MASK + 1] = { + [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x00), + [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x01), + [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x02), + [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x03), + [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x04), +}; + +static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) +{ + unsigned char value = intc_irqpin_sense[type & IRQ_TYPE_SENSE_MASK]; + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + + if (!(value & INTC_IRQ_SENSE_VALID)) + return -EINVAL; + + return intc_irqpin_set_sense(p, irqd_to_hwirq(d), + value ^ INTC_IRQ_SENSE_VALID); +} + +static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) +{ + struct intc_irqpin_irq *i = dev_id; + struct intc_irqpin_priv *p = i->p; + unsigned long bit; + + intc_irqpin_dbg(i, "demux1"); + bit = intc_irqpin_hwirq_mask(p, INTC_IRQPIN_REG_SOURCE, i->hw_irq); + + if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { + intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); + intc_irqpin_dbg(i, "demux2"); + generic_handle_irq(irq_find_mapping(p->irq_domain, i->hw_irq)); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct intc_irqpin_priv *p = h->host_data; + + intc_irqpin_dbg(&p->irq[hw], "map"); + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); + set_irq_flags(virq, IRQF_VALID); /* kill me now */ + return 0; +} + +static struct irq_domain_ops intc_irqpin_irq_domain_ops = { + .map = intc_irqpin_irq_domain_map, +}; + +static int intc_irqpin_probe(struct platform_device *pdev) +{ + struct renesas_intc_irqpin_config *pdata = pdev->dev.platform_data; + struct intc_irqpin_priv *p; + struct intc_irqpin_iomem *i; + struct resource *io[INTC_IRQPIN_REG_NR]; + struct resource *irq; + struct irq_chip *irq_chip; + void (*enable_fn)(struct irq_data *d); + void (*disable_fn)(struct irq_data *d); + const char *name = dev_name(&pdev->dev); + int ret; + int k; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + ret = -ENOMEM; + goto err0; + } + + /* deal with driver instance configuration */ + if (pdata) + memcpy(&p->config, pdata, sizeof(*pdata)); + if (!p->config.sense_bitfield_width) + p->config.sense_bitfield_width = 4; /* default to 4 bits */ + + p->pdev = pdev; + platform_set_drvdata(pdev, p); + + /* get hold of manadatory IOMEM */ + for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { + io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); + if (!io[k]) { + dev_err(&pdev->dev, "not enough IOMEM resources\n"); + ret = -EINVAL; + goto err1; + } + } + + /* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */ + for (k = 0; k < INTC_IRQPIN_MAX; k++) { + irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); + if (!irq) + break; + + p->irq[k].hw_irq = k; + p->irq[k].p = p; + p->irq[k].irq = irq->start; + } + + p->number_of_irqs = k; + if (p->number_of_irqs < 1) { + dev_err(&pdev->dev, "not enough IRQ resources\n"); + ret = -EINVAL; + goto err1; + } + + /* ioremap IOMEM and setup read/write callbacks */ + for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { + i = &p->iomem[k]; + + switch (resource_size(io[k])) { + case 1: + i->width = 8; + i->read = intc_irqpin_read8; + i->write = intc_irqpin_write8; + break; + case 4: + i->width = 32; + i->read = intc_irqpin_read32; + i->write = intc_irqpin_write32; + break; + default: + dev_err(&pdev->dev, "IOMEM size mismatch\n"); + ret = -EINVAL; + goto err2; + } + + i->iomem = ioremap_nocache(io[k]->start, resource_size(io[k])); + if (!i->iomem) { + dev_err(&pdev->dev, "failed to remap IOMEM\n"); + ret = -ENXIO; + goto err2; + } + } + + /* mask all interrupts using priority */ + for (k = 0; k < p->number_of_irqs; k++) + intc_irqpin_mask_unmask_prio(p, k, 1); + + /* use more severe masking method if requested */ + if (p->config.control_parent) { + enable_fn = intc_irqpin_irq_enable_force; + disable_fn = intc_irqpin_irq_disable_force; + } else { + enable_fn = intc_irqpin_irq_enable; + disable_fn = intc_irqpin_irq_disable; + } + + irq_chip = &p->irq_chip; + irq_chip->name = name; + irq_chip->irq_mask = disable_fn; + irq_chip->irq_unmask = enable_fn; + irq_chip->irq_enable = enable_fn; + irq_chip->irq_disable = disable_fn; + irq_chip->irq_set_type = intc_irqpin_irq_set_type; + irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; + + p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, + p->number_of_irqs, + p->config.irq_base, + &intc_irqpin_irq_domain_ops, p); + if (!p->irq_domain) { + ret = -ENXIO; + dev_err(&pdev->dev, "cannot initialize irq domain\n"); + goto err2; + } + + /* request and set priority on interrupts one by one */ + for (k = 0; k < p->number_of_irqs; k++) { + if (request_irq(p->irq[k].irq, intc_irqpin_irq_handler, + 0, name, &p->irq[k])) { + dev_err(&pdev->dev, "failed to request low IRQ\n"); + ret = -ENOENT; + goto err3; + } + intc_irqpin_mask_unmask_prio(p, k, 0); + } + + dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); + + /* warn in case of mismatch if irq base is specified */ + if (p->config.irq_base) { + k = irq_find_mapping(p->irq_domain, 0); + if (p->config.irq_base != k) + dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", + p->config.irq_base, k); + } + + return 0; + +err3: + for (; k >= 0; k--) + free_irq(p->irq[k - 1].irq, &p->irq[k - 1]); + + irq_domain_remove(p->irq_domain); +err2: + for (k = 0; k < INTC_IRQPIN_REG_NR; k++) + iounmap(p->iomem[k].iomem); +err1: + kfree(p); +err0: + return ret; +} + +static int intc_irqpin_remove(struct platform_device *pdev) +{ + struct intc_irqpin_priv *p = platform_get_drvdata(pdev); + int k; + + for (k = 0; k < p->number_of_irqs; k++) + free_irq(p->irq[k].irq, &p->irq[k]); + + irq_domain_remove(p->irq_domain); + + for (k = 0; k < INTC_IRQPIN_REG_NR; k++) + iounmap(p->iomem[k].iomem); + + kfree(p); + return 0; +} + +static struct platform_driver intc_irqpin_device_driver = { + .probe = intc_irqpin_probe, + .remove = intc_irqpin_remove, + .driver = { + .name = "renesas_intc_irqpin", + } +}; + +static int __init intc_irqpin_init(void) +{ + return platform_driver_register(&intc_irqpin_device_driver); +} +postcore_initcall(intc_irqpin_init); + +static void __exit intc_irqpin_exit(void) +{ + platform_driver_unregister(&intc_irqpin_device_driver); +} +module_exit(intc_irqpin_exit); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/irq-renesas-intc-irqpin.h b/include/linux/platform_data/irq-renesas-intc-irqpin.h new file mode 100644 index 00000000000..00ccac34dac --- /dev/null +++ b/include/linux/platform_data/irq-renesas-intc-irqpin.h @@ -0,0 +1,10 @@ +#ifndef __IRQ_RENESAS_INTC_IRQPIN_H__ +#define __IRQ_RENESAS_INTC_IRQPIN_H__ + +struct renesas_intc_irqpin_config { + unsigned int sense_bitfield_width; + unsigned int irq_base; + bool control_parent; +}; + +#endif /* __IRQ_RENESAS_INTC_IRQPIN_H__ */ -- cgit v1.2.3-70-g09d2 From 0ca8712285e9e762ce4f5faf9f803b52e48c6837 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 26 Feb 2013 20:59:23 +0900 Subject: irqchip: intc-irqpin: GPL header for platform data Add GPL header to platform data include file. Signed-off-by: Magnus Damm Reviewed-by: Thomas Gleixner Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- include/linux/platform_data/irq-renesas-intc-irqpin.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'include') diff --git a/include/linux/platform_data/irq-renesas-intc-irqpin.h b/include/linux/platform_data/irq-renesas-intc-irqpin.h index 00ccac34dac..e4cb911066a 100644 --- a/include/linux/platform_data/irq-renesas-intc-irqpin.h +++ b/include/linux/platform_data/irq-renesas-intc-irqpin.h @@ -1,3 +1,22 @@ +/* + * Renesas INTC External IRQ Pin Driver + * + * Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #ifndef __IRQ_RENESAS_INTC_IRQPIN_H__ #define __IRQ_RENESAS_INTC_IRQPIN_H__ -- cgit v1.2.3-70-g09d2 From fbc83b7f59dd8ed1154286b6de00b6d03c24a3c4 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 27 Feb 2013 17:15:01 +0900 Subject: irqchip: Renesas IRQC driver This patch adds a driver for external IRQ pins connected to the IRQC hardware block on recent SoCs from Renesas. The IRQC hardware block is used together with more recent ARM based SoCs using the GIC. As usual the GIC requires external IRQ trigger setup somewhere else which in this particular case happens to be IRQC. This driver implements the glue code needed to configure IRQ trigger and also handle mask/unmask and demux of external IRQ pins hooked up from the IRQC to the GIC. Tested on r8a73a4 but is designed to work with a wide range of SoCs. The driver requires one GIC SPI per external IRQ pin to operate. Each driver instance will handle up to 32 external IRQ pins. The SoCs using this driver are currently mainly used together with regular platform devices so this driver allows configuration via platform data to support things like static interrupt base address. DT support will be added incrementally in the not so distant future. Signed-off-by: Magnus Damm Tested-by: Guennadi Liakhovetski Signed-off-by: Simon Horman --- drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-renesas-irqc.c | 298 +++++++++++++++++++++++++ include/linux/platform_data/irq-renesas-irqc.h | 27 +++ 4 files changed, 330 insertions(+) create mode 100644 drivers/irqchip/irq-renesas-irqc.c create mode 100644 include/linux/platform_data/irq-renesas-irqc.h (limited to 'include') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 0f5f1c3825b..4a33351c25d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -29,6 +29,10 @@ config RENESAS_INTC_IRQPIN bool select IRQ_DOMAIN +config RENESAS_IRQC + bool + select IRQ_DOMAIN + config VERSATILE_FPGA_IRQ bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 1aaa4073ab6..e41ceb9bec2 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o obj-$(CONFIG_ARM_VIC) += irq-vic.o obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o +obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c new file mode 100644 index 00000000000..95d69bfac98 --- /dev/null +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -0,0 +1,298 @@ +/* + * Renesas IRQC Driver + * + * Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ + +#define IRQC_REQ_STS 0x00 +#define IRQC_EN_STS 0x04 +#define IRQC_EN_SET 0x08 +#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) +#define DETECT_STATUS 0x100 +#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) + +struct irqc_irq { + int hw_irq; + int requested_irq; + int domain_irq; + struct irqc_priv *p; +}; + +struct irqc_priv { + void __iomem *iomem; + void __iomem *cpu_int_base; + struct irqc_irq irq[IRQC_IRQ_MAX]; + struct renesas_irqc_config config; + unsigned int number_of_irqs; + struct platform_device *pdev; + struct irq_chip irq_chip; + struct irq_domain *irq_domain; +}; + +static void irqc_dbg(struct irqc_irq *i, char *str) +{ + dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", + str, i->requested_irq, i->hw_irq, i->domain_irq); +} + +static void irqc_irq_enable(struct irq_data *d) +{ + struct irqc_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + irqc_dbg(&p->irq[hw_irq], "enable"); + iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET); +} + +static void irqc_irq_disable(struct irq_data *d) +{ + struct irqc_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + + irqc_dbg(&p->irq[hw_irq], "disable"); + iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS); +} + +#define INTC_IRQ_SENSE_VALID 0x10 +#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) + +static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = { + [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01), + [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02), + [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */ + [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */ + [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c), /* Synchronous */ +}; + +static int irqc_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct irqc_priv *p = irq_data_get_irq_chip_data(d); + int hw_irq = irqd_to_hwirq(d); + unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK]; + unsigned long tmp; + + irqc_dbg(&p->irq[hw_irq], "sense"); + + if (!(value & INTC_IRQ_SENSE_VALID)) + return -EINVAL; + + tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq)); + tmp &= ~0x3f; + tmp |= value ^ INTC_IRQ_SENSE_VALID; + iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq)); + return 0; +} + +static irqreturn_t irqc_irq_handler(int irq, void *dev_id) +{ + struct irqc_irq *i = dev_id; + struct irqc_priv *p = i->p; + unsigned long bit = BIT(i->hw_irq); + + irqc_dbg(i, "demux1"); + + if (ioread32(p->iomem + DETECT_STATUS) & bit) { + iowrite32(bit, p->iomem + DETECT_STATUS); + irqc_dbg(i, "demux2"); + generic_handle_irq(i->domain_irq); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct irqc_priv *p = h->host_data; + + p->irq[hw].domain_irq = virq; + p->irq[hw].hw_irq = hw; + + irqc_dbg(&p->irq[hw], "map"); + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); + set_irq_flags(virq, IRQF_VALID); /* kill me now */ + return 0; +} + +static struct irq_domain_ops irqc_irq_domain_ops = { + .map = irqc_irq_domain_map, +}; + +static int irqc_probe(struct platform_device *pdev) +{ + struct renesas_irqc_config *pdata = pdev->dev.platform_data; + struct irqc_priv *p; + struct resource *io; + struct resource *irq; + struct irq_chip *irq_chip; + const char *name = dev_name(&pdev->dev); + int ret; + int k; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + ret = -ENOMEM; + goto err0; + } + + /* deal with driver instance configuration */ + if (pdata) + memcpy(&p->config, pdata, sizeof(*pdata)); + + p->pdev = pdev; + platform_set_drvdata(pdev, p); + + /* get hold of manadatory IOMEM */ + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!io) { + dev_err(&pdev->dev, "not enough IOMEM resources\n"); + ret = -EINVAL; + goto err1; + } + + /* allow any number of IRQs between 1 and IRQC_IRQ_MAX */ + for (k = 0; k < IRQC_IRQ_MAX; k++) { + irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); + if (!irq) + break; + + p->irq[k].p = p; + p->irq[k].requested_irq = irq->start; + } + + p->number_of_irqs = k; + if (p->number_of_irqs < 1) { + dev_err(&pdev->dev, "not enough IRQ resources\n"); + ret = -EINVAL; + goto err1; + } + + /* ioremap IOMEM and setup read/write callbacks */ + p->iomem = ioremap_nocache(io->start, resource_size(io)); + if (!p->iomem) { + dev_err(&pdev->dev, "failed to remap IOMEM\n"); + ret = -ENXIO; + goto err2; + } + + p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ + + irq_chip = &p->irq_chip; + irq_chip->name = name; + irq_chip->irq_mask = irqc_irq_disable; + irq_chip->irq_unmask = irqc_irq_enable; + irq_chip->irq_enable = irqc_irq_enable; + irq_chip->irq_disable = irqc_irq_disable; + irq_chip->irq_set_type = irqc_irq_set_type; + irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; + + p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, + p->number_of_irqs, + p->config.irq_base, + &irqc_irq_domain_ops, p); + if (!p->irq_domain) { + ret = -ENXIO; + dev_err(&pdev->dev, "cannot initialize irq domain\n"); + goto err2; + } + + /* request interrupts one by one */ + for (k = 0; k < p->number_of_irqs; k++) { + if (request_irq(p->irq[k].requested_irq, irqc_irq_handler, + 0, name, &p->irq[k])) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + ret = -ENOENT; + goto err3; + } + } + + dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); + + /* warn in case of mismatch if irq base is specified */ + if (p->config.irq_base) { + if (p->config.irq_base != p->irq[0].domain_irq) + dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", + p->config.irq_base, p->irq[0].domain_irq); + } + + return 0; +err3: + for (; k >= 0; k--) + free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); + + irq_domain_remove(p->irq_domain); +err2: + iounmap(p->iomem); +err1: + kfree(p); +err0: + return ret; +} + +static int irqc_remove(struct platform_device *pdev) +{ + struct irqc_priv *p = platform_get_drvdata(pdev); + int k; + + for (k = 0; k < p->number_of_irqs; k++) + free_irq(p->irq[k].requested_irq, &p->irq[k]); + + irq_domain_remove(p->irq_domain); + iounmap(p->iomem); + kfree(p); + return 0; +} + +static struct platform_driver irqc_device_driver = { + .probe = irqc_probe, + .remove = irqc_remove, + .driver = { + .name = "renesas_irqc", + } +}; + +static int __init irqc_init(void) +{ + return platform_driver_register(&irqc_device_driver); +} +postcore_initcall(irqc_init); + +static void __exit irqc_exit(void) +{ + platform_driver_unregister(&irqc_device_driver); +} +module_exit(irqc_exit); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas IRQC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/irq-renesas-irqc.h b/include/linux/platform_data/irq-renesas-irqc.h new file mode 100644 index 00000000000..3ae17b3e00e --- /dev/null +++ b/include/linux/platform_data/irq-renesas-irqc.h @@ -0,0 +1,27 @@ +/* + * Renesas IRQC Driver + * + * Copyright (C) 2013 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __IRQ_RENESAS_IRQC_H__ +#define __IRQ_RENESAS_IRQC_H__ + +struct renesas_irqc_config { + unsigned int irq_base; +}; + +#endif /* __IRQ_RENESAS_IRQC_H__ */ -- cgit v1.2.3-70-g09d2 From af6882be363d3a7bf0f72dd17ac2a639c4da0059 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 8 Mar 2013 10:27:09 +0800 Subject: usb: phy: ab8500-usb: update irq handling code Update irq handling code to notify all possible link status changes of AB8500 and AB8505 to the ux500-musb glue driver. The additional event codes will be used for pm-runtime implementation, and are defined in a separate ux500-specific header. This also modify the irq registration code to use devm_* helpers and drop all non necessary fail path code. Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/musb/ux500.c | 7 +- drivers/usb/phy/phy-ab8500-usb.c | 440 ++++++++++++++++++++++++++++++--------- include/linux/usb/musb-ux500.h | 31 +++ 3 files changed, 382 insertions(+), 96 deletions(-) create mode 100644 include/linux/usb/musb-ux500.h (limited to 'include') diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 0ae9472a68a..88795f53237 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "musb_core.h" @@ -107,15 +108,15 @@ static int musb_otg_notifications(struct notifier_block *nb, event, usb_otg_state_string(musb->xceiv->state)); switch (event) { - case USB_EVENT_ID: + case UX500_MUSB_ID: dev_dbg(musb->controller, "ID GND\n"); ux500_musb_set_vbus(musb, 1); break; - case USB_EVENT_VBUS: + case UX500_MUSB_VBUS: dev_dbg(musb->controller, "VBUS Connect\n"); ux500_musb_set_vbus(musb, 0); break; - case USB_EVENT_NONE: + case UX500_MUSB_NONE: dev_dbg(musb->controller, "VBUS Disconnect\n"); if (is_host_active(musb)) ux500_musb_set_vbus(musb, 0); diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 9f5e0e4ab02..351b0369a61 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -31,9 +31,11 @@ #include #include #include +#include #define AB8500_MAIN_WD_CTRL_REG 0x01 #define AB8500_USB_LINE_STAT_REG 0x80 +#define AB8505_USB_LINE_STAT_REG 0x94 #define AB8500_USB_PHY_CTRL_REG 0x8A #define AB8500_BIT_OTG_STAT_ID (1 << 0) @@ -44,36 +46,76 @@ #define AB8500_WD_KICK_DELAY_US 100 /* usec */ #define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */ +#define AB8500_V20_31952_DISABLE_DELAY_US 100 /* usec */ /* Usb line status register */ enum ab8500_usb_link_status { - USB_LINK_NOT_CONFIGURED = 0, - USB_LINK_STD_HOST_NC, - USB_LINK_STD_HOST_C_NS, - USB_LINK_STD_HOST_C_S, - USB_LINK_HOST_CHG_NM, - USB_LINK_HOST_CHG_HS, - USB_LINK_HOST_CHG_HS_CHIRP, - USB_LINK_DEDICATED_CHG, - USB_LINK_ACA_RID_A, - USB_LINK_ACA_RID_B, - USB_LINK_ACA_RID_C_NM, - USB_LINK_ACA_RID_C_HS, - USB_LINK_ACA_RID_C_HS_CHIRP, - USB_LINK_HM_IDGND, - USB_LINK_RESERVED, - USB_LINK_NOT_VALID_LINK + USB_LINK_NOT_CONFIGURED_8500 = 0, + USB_LINK_STD_HOST_NC_8500, + USB_LINK_STD_HOST_C_NS_8500, + USB_LINK_STD_HOST_C_S_8500, + USB_LINK_HOST_CHG_NM_8500, + USB_LINK_HOST_CHG_HS_8500, + USB_LINK_HOST_CHG_HS_CHIRP_8500, + USB_LINK_DEDICATED_CHG_8500, + USB_LINK_ACA_RID_A_8500, + USB_LINK_ACA_RID_B_8500, + USB_LINK_ACA_RID_C_NM_8500, + USB_LINK_ACA_RID_C_HS_8500, + USB_LINK_ACA_RID_C_HS_CHIRP_8500, + USB_LINK_HM_IDGND_8500, + USB_LINK_RESERVED_8500, + USB_LINK_NOT_VALID_LINK_8500, +}; + +enum ab8505_usb_link_status { + USB_LINK_NOT_CONFIGURED_8505 = 0, + USB_LINK_STD_HOST_NC_8505, + USB_LINK_STD_HOST_C_NS_8505, + USB_LINK_STD_HOST_C_S_8505, + USB_LINK_CDP_8505, + USB_LINK_RESERVED0_8505, + USB_LINK_RESERVED1_8505, + USB_LINK_DEDICATED_CHG_8505, + USB_LINK_ACA_RID_A_8505, + USB_LINK_ACA_RID_B_8505, + USB_LINK_ACA_RID_C_NM_8505, + USB_LINK_RESERVED2_8505, + USB_LINK_RESERVED3_8505, + USB_LINK_HM_IDGND_8505, + USB_LINK_CHARGERPORT_NOT_OK_8505, + USB_LINK_CHARGER_DM_HIGH_8505, + USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8505, + USB_LINK_STD_UPSTREAM_NO_IDGNG_NO_VBUS_8505, + USB_LINK_STD_UPSTREAM_8505, + USB_LINK_CHARGER_SE1_8505, + USB_LINK_CARKIT_CHGR_1_8505, + USB_LINK_CARKIT_CHGR_2_8505, + USB_LINK_ACA_DOCK_CHGR_8505, + USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_8505, + USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_8505, + USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8505, + USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8505, + USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8505, +}; + +enum ab8500_usb_mode { + USB_IDLE = 0, + USB_PERIPHERAL, + USB_HOST, + USB_DEDICATED_CHG }; struct ab8500_usb { struct usb_phy phy; struct device *dev; struct ab8500 *ab8500; - int irq_num_link_status; unsigned vbus_draw; struct delayed_work dwork; struct work_struct phy_dis_work; unsigned long link_status_wait; + enum ab8500_usb_mode mode; + int previous_link_status_state; }; static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x) @@ -104,6 +146,17 @@ static void ab8500_usb_wd_workaround(struct ab8500_usb *ab) 0); } +static void ab8500_usb_wd_linkstatus(struct ab8500_usb *ab, u8 bit) +{ + /* Workaround for v2.0 bug # 31952 */ + if (is_ab8500_2p0(ab->ab8500)) { + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_PHY_CTRL_REG, + bit, bit); + udelay(AB8500_V20_31952_DISABLE_DELAY_US); + } +} + static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host, bool enable) { @@ -139,92 +192,276 @@ static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host, #define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_ctrl(ab, false, true) #define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_ctrl(ab, false, false) -static int ab8500_usb_link_status_update(struct ab8500_usb *ab) +static int ab8505_usb_link_status_update(struct ab8500_usb *ab, + enum ab8505_usb_link_status lsts) { - u8 reg; - enum ab8500_usb_link_status lsts; - void *v = NULL; - enum usb_phy_events event; + enum ux500_musb_vbus_id_status event = 0; - abx500_get_register_interruptible(ab->dev, - AB8500_USB, - AB8500_USB_LINE_STAT_REG, - ®); + dev_dbg(ab->dev, "ab8505_usb_link_status_update %d\n", lsts); - lsts = (reg >> 3) & 0x0F; + /* + * Spurious link_status interrupts are seen at the time of + * disconnection of a device in RIDA state + */ + if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8505 && + (lsts == USB_LINK_STD_HOST_NC_8505)) + return 0; + + ab->previous_link_status_state = lsts; switch (lsts) { - case USB_LINK_NOT_CONFIGURED: - case USB_LINK_RESERVED: - case USB_LINK_NOT_VALID_LINK: - /* TODO: Disable regulators. */ - ab8500_usb_host_phy_dis(ab); - ab8500_usb_peri_phy_dis(ab); - ab->phy.state = OTG_STATE_B_IDLE; + case USB_LINK_ACA_RID_B_8505: + event = UX500_MUSB_RIDB; + case USB_LINK_NOT_CONFIGURED_8505: + case USB_LINK_RESERVED0_8505: + case USB_LINK_RESERVED1_8505: + case USB_LINK_RESERVED2_8505: + case USB_LINK_RESERVED3_8505: + ab->mode = USB_IDLE; ab->phy.otg->default_a = false; ab->vbus_draw = 0; - event = USB_EVENT_NONE; + if (event != UX500_MUSB_RIDB) + event = UX500_MUSB_NONE; + /* + * Fallback to default B_IDLE as nothing + * is connected + */ + ab->phy.state = OTG_STATE_B_IDLE; break; - case USB_LINK_STD_HOST_NC: - case USB_LINK_STD_HOST_C_NS: - case USB_LINK_STD_HOST_C_S: - case USB_LINK_HOST_CHG_NM: - case USB_LINK_HOST_CHG_HS: - case USB_LINK_HOST_CHG_HS_CHIRP: - if (ab->phy.otg->gadget) { - /* TODO: Enable regulators. */ + case USB_LINK_ACA_RID_C_NM_8505: + event = UX500_MUSB_RIDC; + case USB_LINK_STD_HOST_NC_8505: + case USB_LINK_STD_HOST_C_NS_8505: + case USB_LINK_STD_HOST_C_S_8505: + case USB_LINK_CDP_8505: + if (ab->mode == USB_IDLE) { + ab->mode = USB_PERIPHERAL; ab8500_usb_peri_phy_en(ab); - v = ab->phy.otg->gadget; + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); } - event = USB_EVENT_VBUS; + if (event != UX500_MUSB_RIDC) + event = UX500_MUSB_VBUS; break; - case USB_LINK_HM_IDGND: - if (ab->phy.otg->host) { - /* TODO: Enable regulators. */ + case USB_LINK_ACA_RID_A_8505: + case USB_LINK_ACA_DOCK_CHGR_8505: + event = UX500_MUSB_RIDA; + case USB_LINK_HM_IDGND_8505: + if (ab->mode == USB_IDLE) { + ab->mode = USB_HOST; ab8500_usb_host_phy_en(ab); - v = ab->phy.otg->host; + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); } - ab->phy.state = OTG_STATE_A_IDLE; ab->phy.otg->default_a = true; - event = USB_EVENT_ID; + if (event != UX500_MUSB_RIDA) + event = UX500_MUSB_ID; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); break; - case USB_LINK_ACA_RID_A: - case USB_LINK_ACA_RID_B: - /* TODO */ - case USB_LINK_ACA_RID_C_NM: - case USB_LINK_ACA_RID_C_HS: - case USB_LINK_ACA_RID_C_HS_CHIRP: - case USB_LINK_DEDICATED_CHG: - /* TODO: vbus_draw */ - event = USB_EVENT_CHARGER; + case USB_LINK_DEDICATED_CHG_8505: + ab->mode = USB_DEDICATED_CHG; + event = UX500_MUSB_CHARGER; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + break; + + default: break; } - atomic_notifier_call_chain(&ab->phy.notifier, event, v); + return 0; +} + +static int ab8500_usb_link_status_update(struct ab8500_usb *ab, + enum ab8500_usb_link_status lsts) +{ + enum ux500_musb_vbus_id_status event = 0; + + dev_dbg(ab->dev, "ab8500_usb_link_status_update %d\n", lsts); + + /* + * Spurious link_status interrupts are seen in case of a + * disconnection of a device in IDGND and RIDA stage + */ + if (ab->previous_link_status_state == USB_LINK_HM_IDGND_8500 && + (lsts == USB_LINK_STD_HOST_C_NS_8500 || + lsts == USB_LINK_STD_HOST_NC_8500)) + return 0; + + if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8500 && + lsts == USB_LINK_STD_HOST_NC_8500) + return 0; + + ab->previous_link_status_state = lsts; + + switch (lsts) { + case USB_LINK_ACA_RID_B_8500: + event = UX500_MUSB_RIDB; + case USB_LINK_NOT_CONFIGURED_8500: + case USB_LINK_NOT_VALID_LINK_8500: + ab->mode = USB_IDLE; + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + if (event != UX500_MUSB_RIDB) + event = UX500_MUSB_NONE; + /* Fallback to default B_IDLE as nothing is connected */ + ab->phy.state = OTG_STATE_B_IDLE; + break; + + case USB_LINK_ACA_RID_C_NM_8500: + case USB_LINK_ACA_RID_C_HS_8500: + case USB_LINK_ACA_RID_C_HS_CHIRP_8500: + event = UX500_MUSB_RIDC; + case USB_LINK_STD_HOST_NC_8500: + case USB_LINK_STD_HOST_C_NS_8500: + case USB_LINK_STD_HOST_C_S_8500: + case USB_LINK_HOST_CHG_NM_8500: + case USB_LINK_HOST_CHG_HS_8500: + case USB_LINK_HOST_CHG_HS_CHIRP_8500: + if (ab->mode == USB_IDLE) { + ab->mode = USB_PERIPHERAL; + ab8500_usb_peri_phy_en(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); + } + if (event != UX500_MUSB_RIDC) + event = UX500_MUSB_VBUS; + break; + + case USB_LINK_ACA_RID_A_8500: + event = UX500_MUSB_RIDA; + case USB_LINK_HM_IDGND_8500: + if (ab->mode == USB_IDLE) { + ab->mode = USB_HOST; + ab8500_usb_host_phy_en(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); + } + ab->phy.otg->default_a = true; + if (event != UX500_MUSB_RIDA) + event = UX500_MUSB_ID; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + break; + + case USB_LINK_DEDICATED_CHG_8500: + ab->mode = USB_DEDICATED_CHG; + event = UX500_MUSB_CHARGER; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + break; + + case USB_LINK_RESERVED_8500: + break; + } return 0; } -static void ab8500_usb_delayed_work(struct work_struct *work) +/* + * Connection Sequence: + * 1. Link Status Interrupt + * 2. Enable AB clock + * 3. Enable AB regulators + * 4. Enable USB phy + * 5. Reset the musb controller + * 6. Switch the ULPI GPIO pins to fucntion mode + * 7. Enable the musb Peripheral5 clock + * 8. Restore MUSB context + */ +static int abx500_usb_link_status_update(struct ab8500_usb *ab) { - struct ab8500_usb *ab = container_of(work, struct ab8500_usb, - dwork.work); + u8 reg; + int ret = 0; + + if (is_ab8500(ab->ab8500)) { + enum ab8500_usb_link_status lsts; + + abx500_get_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_LINE_STAT_REG, ®); + lsts = (reg >> 3) & 0x0F; + ret = ab8500_usb_link_status_update(ab, lsts); + } else if (is_ab8505(ab->ab8500)) { + enum ab8505_usb_link_status lsts; + + abx500_get_register_interruptible(ab->dev, + AB8500_USB, AB8505_USB_LINE_STAT_REG, ®); + lsts = (reg >> 3) & 0x1F; + ret = ab8505_usb_link_status_update(ab, lsts); + } + + return ret; +} + +/* + * Disconnection Sequence: + * 1. Disconect Interrupt + * 2. Disable regulators + * 3. Disable AB clock + * 4. Disable the Phy + * 5. Link Status Interrupt + * 6. Disable Musb Clock + */ +static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data) +{ + struct ab8500_usb *ab = (struct ab8500_usb *) data; + enum usb_phy_events event = UX500_MUSB_NONE; + + /* Link status will not be updated till phy is disabled. */ + if (ab->mode == USB_HOST) { + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + ab8500_usb_host_phy_dis(ab); + ab->mode = USB_IDLE; + } + + if (ab->mode == USB_PERIPHERAL) { + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + ab8500_usb_peri_phy_dis(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_CLEAN, &ab->vbus_draw); + ab->mode = USB_IDLE; + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + } + + if (is_ab8500_2p0(ab->ab8500)) { + if (ab->mode == USB_DEDICATED_CHG) { + ab8500_usb_wd_linkstatus(ab, + AB8500_BIT_PHY_CTRL_DEVICE_EN); + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_PHY_CTRL_REG, + AB8500_BIT_PHY_CTRL_DEVICE_EN, 0); + } + } - ab8500_usb_link_status_update(ab); + return IRQ_HANDLED; } -static irqreturn_t ab8500_usb_v20_irq(int irq, void *data) +static irqreturn_t ab8500_usb_link_status_irq(int irq, void *data) { struct ab8500_usb *ab = (struct ab8500_usb *) data; - ab8500_usb_link_status_update(ab); + abx500_usb_link_status_update(ab); return IRQ_HANDLED; } +static void ab8500_usb_delayed_work(struct work_struct *work) +{ + struct ab8500_usb *ab = container_of(work, struct ab8500_usb, + dwork.work); + + abx500_usb_link_status_update(ab); +} + static void ab8500_usb_phy_disable_work(struct work_struct *work) { struct ab8500_usb *ab = container_of(work, struct ab8500_usb, @@ -250,7 +487,7 @@ static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA) if (mA) atomic_notifier_call_chain(&ab->phy.notifier, - USB_EVENT_ENUMERATED, ab->phy.otg->gadget); + UX500_MUSB_ENUMERATED, ab->phy.otg->gadget); return 0; } @@ -327,30 +564,48 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } -static void ab8500_usb_irq_free(struct ab8500_usb *ab) -{ - free_irq(ab->irq_num_link_status, ab); -} - -static int ab8500_usb_v2_res_setup(struct platform_device *pdev, - struct ab8500_usb *ab) +static int ab8500_usb_irq_setup(struct platform_device *pdev, + struct ab8500_usb *ab) { int err; + int irq; - ab->irq_num_link_status = platform_get_irq_byname(pdev, - "USB_LINK_STATUS"); - if (ab->irq_num_link_status < 0) { + irq = platform_get_irq_byname(pdev, "USB_LINK_STATUS"); + if (irq < 0) { dev_err(&pdev->dev, "Link status irq not found\n"); - return ab->irq_num_link_status; + return irq; + } + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + ab8500_usb_link_status_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, "usb-link-status", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for link status irq\n"); + return err; } - err = request_threaded_irq(ab->irq_num_link_status, NULL, - ab8500_usb_v20_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, - "usb-link-status", ab); + irq = platform_get_irq_byname(pdev, "ID_WAKEUP_F"); + if (irq < 0) { + dev_err(&pdev->dev, "ID fall irq not found\n"); + return irq; + } + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + ab8500_usb_disconnect_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, "usb-id-fall", ab); if (err < 0) { - dev_err(ab->dev, - "request_irq failed for link status irq\n"); + dev_err(ab->dev, "request_irq failed for ID fall irq\n"); + return err; + } + + irq = platform_get_irq_byname(pdev, "VBUS_DET_F"); + if (irq < 0) { + dev_err(&pdev->dev, "VBUS fall irq not found\n"); + return irq; + } + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + ab8500_usb_disconnect_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, "usb-vbus-fall", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for Vbus fall irq\n"); return err; } @@ -408,22 +663,23 @@ static int ab8500_usb_probe(struct platform_device *pdev) /* all: Disable phy when called from set_host and set_peripheral */ INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); - err = ab8500_usb_v2_res_setup(pdev, ab); + err = ab8500_usb_irq_setup(pdev, ab); if (err < 0) - goto fail0; + goto fail; err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2); if (err) { dev_err(&pdev->dev, "Can't register transceiver\n"); - goto fail1; + goto fail; } + /* Needed to enable ID detection. */ + ab8500_usb_wd_workaround(ab); + dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev); return 0; -fail1: - ab8500_usb_irq_free(ab); -fail0: +fail: kfree(otg); kfree(ab); return err; @@ -433,8 +689,6 @@ static int ab8500_usb_remove(struct platform_device *pdev) { struct ab8500_usb *ab = platform_get_drvdata(pdev); - ab8500_usb_irq_free(ab); - cancel_delayed_work_sync(&ab->dwork); cancel_work_sync(&ab->phy_dis_work); diff --git a/include/linux/usb/musb-ux500.h b/include/linux/usb/musb-ux500.h new file mode 100644 index 00000000000..1e2c7130f6e --- /dev/null +++ b/include/linux/usb/musb-ux500.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2013 ST-Ericsson AB + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MUSB_UX500_H__ +#define __MUSB_UX500_H__ + +enum ux500_musb_vbus_id_status { + UX500_MUSB_NONE = 0, + UX500_MUSB_VBUS, + UX500_MUSB_ID, + UX500_MUSB_CHARGER, + UX500_MUSB_ENUMERATED, + UX500_MUSB_RIDA, + UX500_MUSB_RIDB, + UX500_MUSB_RIDC, + UX500_MUSB_PREPARE, + UX500_MUSB_CLEAN, +}; + +#endif /* __MUSB_UX500_H__ */ -- cgit v1.2.3-70-g09d2 From 3b77d6617a68dbcafc9cc95d80522c3b0c00ad80 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Tue, 12 Mar 2013 13:39:47 +0800 Subject: net: sctp: remove cast for kmalloc/kzalloc return value remove cast for kmalloc/kzalloc return value. Signed-off-by: Zhang Yanfei Acked-by: Neil Horman Cc: Vlad Yasevich Cc: Sridhar Samudrala Cc: Neil Horman Cc: Andrew Morton Cc: linux-sctp@vger.kernel.org Signed-off-by: Jiri Kosina --- include/net/sctp/sctp.h | 2 +- net/sctp/protocol.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index df85a0c0f2d..cd89510eab2 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -576,7 +576,7 @@ for (pos = chunk->subh.fwdtsn_hdr->skip;\ #define WORD_ROUND(s) (((s)+3)&~3) /* Make a new instance of type. */ -#define t_new(type, flags) (type *)kzalloc(sizeof(type), flags) +#define t_new(type, flags) kzalloc(sizeof(type), flags) /* Compare two timevals. */ #define tv_lt(s, t) \ diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 1c2e46cb919..eaee00c6113 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1403,7 +1403,7 @@ SCTP_STATIC __init int sctp_init(void) /* Allocate and initialize the endpoint hash table. */ sctp_ep_hashsize = 64; - sctp_ep_hashtable = (struct sctp_hashbucket *) + sctp_ep_hashtable = kmalloc(64 * sizeof(struct sctp_hashbucket), GFP_KERNEL); if (!sctp_ep_hashtable) { pr_err("Failed endpoint_hash alloc\n"); -- cgit v1.2.3-70-g09d2 From cf2fbdd26f80046725a11a80683a03baf27fae82 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Sat, 16 Mar 2013 20:53:05 +0900 Subject: treewide: Fix typos in printk and comment Signed-off-by: Masanari Iida Signed-off-by: Jiri Kosina --- arch/arc/plat-arcfpga/Kconfig | 2 +- arch/blackfin/include/asm/bfin_sport3.h | 2 +- arch/s390/kernel/irq.c | 2 +- arch/s390/kvm/trace.h | 2 +- drivers/crypto/caam/ctrl.c | 2 +- drivers/gpu/drm/radeon/radeon_irq_kms.c | 2 +- drivers/i2c/busses/i2c-puv3.c | 2 +- drivers/scsi/qla4xxx/ql4_nx.c | 2 +- drivers/usb/misc/uss720.c | 2 +- include/trace/events/timer.h | 10 +++++----- sound/pci/mixart/mixart.c | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/arch/arc/plat-arcfpga/Kconfig b/arch/arc/plat-arcfpga/Kconfig index b41e786cdbc..295cefeb25d 100644 --- a/arch/arc/plat-arcfpga/Kconfig +++ b/arch/arc/plat-arcfpga/Kconfig @@ -53,7 +53,7 @@ menuconfig ARC_HAS_BVCI_LAT_UNIT bool "BVCI Bus Latency Unit" depends on ARC_BOARD_ML509 || ARC_BOARD_ANGEL4 help - IP to add artifical latency to BVCI Bus Based FPGA builds. + IP to add artificial latency to BVCI Bus Based FPGA builds. The default latency (even worst case) for FPGA is non-realistic (~10 SDRAM, ~5 SSRAM). diff --git a/arch/blackfin/include/asm/bfin_sport3.h b/arch/blackfin/include/asm/bfin_sport3.h index 03c00220d69..d82f5fa0ad9 100644 --- a/arch/blackfin/include/asm/bfin_sport3.h +++ b/arch/blackfin/include/asm/bfin_sport3.h @@ -41,7 +41,7 @@ #define SPORT_CTL_LAFS 0x00020000 /* Late Transmit frame select */ #define SPORT_CTL_RJUST 0x00040000 /* Right Justified mode select */ #define SPORT_CTL_FSED 0x00080000 /* External frame sync edge select */ -#define SPORT_CTL_TFIEN 0x00100000 /* Transmit finish interrrupt enable select */ +#define SPORT_CTL_TFIEN 0x00100000 /* Transmit finish interrupt enable select */ #define SPORT_CTL_GCLKEN 0x00200000 /* Gated clock mode select */ #define SPORT_CTL_SPENSEC 0x01000000 /* Enable secondary channel */ #define SPORT_CTL_SPTRAN 0x02000000 /* Data direction control */ diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 1630f439cd2..4f5ef62934a 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -33,7 +33,7 @@ struct irq_class { }; /* - * The list of "main" irq classes on s390. This is the list of interrrupts + * The list of "main" irq classes on s390. This is the list of interrupts * that appear both in /proc/stat ("intr" line) and /proc/interrupts. * Historically only external and I/O interrupts have been part of /proc/stat. * We can't add the split external and I/O sub classes since the first field diff --git a/arch/s390/kvm/trace.h b/arch/s390/kvm/trace.h index 2b29e62351d..74789880535 100644 --- a/arch/s390/kvm/trace.h +++ b/arch/s390/kvm/trace.h @@ -67,7 +67,7 @@ TRACE_EVENT(kvm_s390_sie_fault, #define sie_intercept_code \ {0x04, "Instruction"}, \ {0x08, "Program interruption"}, \ - {0x0C, "Instruction and program interuption"}, \ + {0x0C, "Instruction and program interruption"}, \ {0x10, "External request"}, \ {0x14, "External interruption"}, \ {0x18, "I/O request"}, \ diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 1c56f63524f..8acf00490fd 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -66,7 +66,7 @@ static void build_instantiation_desc(u32 *desc) /* * load 1 to clear written reg: - * resets the done interrrupt and returns the RNG to idle. + * resets the done interrupt and returns the RNG to idle. */ append_load_imm_u32(desc, 1, LDST_SRCDST_WORD_CLRW); diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 90374dd7796..8c8a7f0d982 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -270,7 +270,7 @@ int radeon_irq_kms_init(struct radeon_device *rdev) } /** - * radeon_irq_kms_fini - tear down driver interrrupt info + * radeon_irq_kms_fini - tear down driver interrupt info * * @rdev: radeon device pointer * diff --git a/drivers/i2c/busses/i2c-puv3.c b/drivers/i2c/busses/i2c-puv3.c index 261d7db437e..8acef657abf 100644 --- a/drivers/i2c/busses/i2c-puv3.c +++ b/drivers/i2c/busses/i2c-puv3.c @@ -199,7 +199,7 @@ static int puv3_i2c_probe(struct platform_device *pdev) adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); if (adapter == NULL) { - dev_err(&pdev->dev, "can't allocate inteface!\n"); + dev_err(&pdev->dev, "can't allocate interface!\n"); rc = -ENOMEM; goto fail_nomem; } diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c index 71d3d234f52..9299400d3c9 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.c +++ b/drivers/scsi/qla4xxx/ql4_nx.c @@ -2089,7 +2089,7 @@ static int qla4_8xxx_minidump_process_rdmem(struct scsi_qla_host *ha, if (r_addr & 0xf) { DEBUG2(ql4_printk(KERN_INFO, ha, - "[%s]: Read addr 0x%x not 16 bytes alligned\n", + "[%s]: Read addr 0x%x not 16 bytes aligned\n", __func__, r_addr)); return QLA_ERROR; } diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index 29cad9e0a7a..e129cf66122 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -705,7 +705,7 @@ static int uss720_probe(struct usb_interface *intf, return -ENODEV; } i = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2); - dev_dbg(&intf->dev, "set inteface result %d\n", i); + dev_dbg(&intf->dev, "set interface result %d\n", i); interface = intf->cur_altsetting; diff --git a/include/trace/events/timer.h b/include/trace/events/timer.h index 425bcfe56c6..8d219470624 100644 --- a/include/trace/events/timer.h +++ b/include/trace/events/timer.h @@ -123,7 +123,7 @@ DEFINE_EVENT(timer_class, timer_cancel, /** * hrtimer_init - called when the hrtimer is initialized - * @timer: pointer to struct hrtimer + * @hrtimer: pointer to struct hrtimer * @clockid: the hrtimers clock * @mode: the hrtimers mode */ @@ -155,7 +155,7 @@ TRACE_EVENT(hrtimer_init, /** * hrtimer_start - called when the hrtimer is started - * @timer: pointer to struct hrtimer + * @hrtimer: pointer to struct hrtimer */ TRACE_EVENT(hrtimer_start, @@ -186,8 +186,8 @@ TRACE_EVENT(hrtimer_start, ); /** - * htimmer_expire_entry - called immediately before the hrtimer callback - * @timer: pointer to struct hrtimer + * hrtimer_expire_entry - called immediately before the hrtimer callback + * @hrtimer: pointer to struct hrtimer * @now: pointer to variable which contains current time of the * timers base. * @@ -234,7 +234,7 @@ DECLARE_EVENT_CLASS(hrtimer_class, /** * hrtimer_expire_exit - called immediately after the hrtimer callback returns - * @timer: pointer to struct hrtimer + * @hrtimer: pointer to struct hrtimer * * When used in combination with the hrtimer_expire_entry tracepoint we can * determine the runtime of the callback function. diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 01f7f37a841..934dec98e2c 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1175,7 +1175,7 @@ static void snd_mixart_proc_read(struct snd_info_entry *entry, snd_iprintf(buffer, "\tstreaming : %d\n", streaming); snd_iprintf(buffer, "\tmailbox : %d\n", mailbox); - snd_iprintf(buffer, "\tinterrups handling : %d\n\n", interr); + snd_iprintf(buffer, "\tinterrupts handling : %d\n\n", interr); } } /* endif elf loaded */ } -- cgit v1.2.3-70-g09d2 From 2908fe31cf6b8d3a975efb567347f85e724f4e81 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:56 -0500 Subject: Bluetooth: Remove useless HCI_PENDING_CLASS flag Now that class related operations are tracked through asynchronous HCI requests this flag is no longer needed. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 1 - net/bluetooth/hci_event.c | 3 +-- net/bluetooth/mgmt.c | 4 ---- 3 files changed, 1 insertion(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7f12c25f1fc..1e723c76a8f 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -119,7 +119,6 @@ enum { HCI_CONNECTABLE, HCI_DISCOVERABLE, HCI_LINK_SECURITY, - HCI_PENDING_CLASS, HCI_PERIODIC_INQ, }; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d11b87bc1d1..5f2d008f335 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -194,8 +194,7 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_RESET, &hdev->flags); /* Reset all non-persistent flags */ - hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PENDING_CLASS) | - BIT(HCI_PERIODIC_INQ)); + hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ)); hdev->discovery.state = DISCOVERY_STOPPED; hdev->inq_tx_power = HCI_TX_POWER_INVALID; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8a0bbb914be..3e3cb0102b1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -652,8 +652,6 @@ static void update_class(struct hci_request *req) return; hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); - - set_bit(HCI_PENDING_CLASS, &hdev->dev_flags); } static void service_cache_off(struct work_struct *work) @@ -3762,8 +3760,6 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, struct cmd_lookup match = { NULL, hdev, mgmt_status(status) }; int err = 0; - clear_bit(HCI_PENDING_CLASS, &hdev->dev_flags); - mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match); mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match); mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match); -- cgit v1.2.3-70-g09d2 From 2cc6fb0049bc02ca7a020ba7b4f88b4c35976058 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:57 -0500 Subject: Bluetooth: Add a define for the HCI persistent flags mask We'll need to use this mask also when powering off the HCI device so it's better to have this in a single and visible place. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 5 +++++ net/bluetooth/hci_event.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 1e723c76a8f..1e40222e9dd 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -122,6 +122,11 @@ enum { HCI_PERIODIC_INQ, }; +/* A mask for the flags that are supposed to remain when a reset happens + * or the HCI device is closed. + */ +#define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ)) + /* HCI ioctl defines */ #define HCIDEVUP _IOW('H', 201, int) #define HCIDEVDOWN _IOW('H', 202, int) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 5f2d008f335..ed4ecd930a7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -194,7 +194,7 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_RESET, &hdev->flags); /* Reset all non-persistent flags */ - hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ)); + hdev->dev_flags &= ~HCI_PERSISTENT_MASK; hdev->discovery.state = DISCOVERY_STOPPED; hdev->inq_tx_power = HCI_TX_POWER_INVALID; -- cgit v1.2.3-70-g09d2 From 04b4edcbc9049e100681c0149b572de439be42ab Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:01 -0500 Subject: Bluetooth: Handle AD updating through an async request For proper control of the AD update and the related HCI commands it's best to run the AD update through an async request instead of a standalone HCI command. This patch changes the hci_update_ad() function to take a request pointer and updates its users appropriately. E.g. the function is no longer called after the init sequence but during stage 3 of the init sequence. The TX power is read during the init sequence, so we don't need an explicit update whenever it is read and the AD update based on the local name should be done through the local name mgmt handler. The only other user is the update based on enabling advertising. This part is still kept as there is no mgmt API to enable it. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 4 ++-- net/bluetooth/hci_core.c | 29 ++++++++++------------------- net/bluetooth/hci_event.c | 19 +++++++++---------- 3 files changed, 21 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d6c32561fc0..cb99193c749 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -740,8 +740,6 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, u8 *randomizer); int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); -int hci_update_ad(struct hci_dev *hdev); - void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct sk_buff *skb); @@ -1167,6 +1165,8 @@ struct hci_sec_filter { #define hci_req_lock(d) mutex_lock(&d->req_lock) #define hci_req_unlock(d) mutex_unlock(&d->req_lock) +void hci_update_ad(struct hci_request *req); + void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9e87a91562a..0ffd3587117 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -492,8 +492,10 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) if (hdev->commands[5] & 0x10) hci_setup_link_policy(req); - if (lmp_le_capable(hdev)) + if (lmp_le_capable(hdev)) { hci_set_le_support(req); + hci_update_ad(req); + } } static int __hci_init(struct hci_dev *hdev) @@ -936,39 +938,29 @@ static u8 create_ad(struct hci_dev *hdev, u8 *ptr) return ad_len; } -int hci_update_ad(struct hci_dev *hdev) +void hci_update_ad(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; struct hci_cp_le_set_adv_data cp; u8 len; - int err; - - hci_dev_lock(hdev); - if (!lmp_le_capable(hdev)) { - err = -EINVAL; - goto unlock; - } + if (!lmp_le_capable(hdev)) + return; memset(&cp, 0, sizeof(cp)); len = create_ad(hdev, cp.data); if (hdev->adv_data_len == len && - memcmp(cp.data, hdev->adv_data, len) == 0) { - err = 0; - goto unlock; - } + memcmp(cp.data, hdev->adv_data, len) == 0) + return; memcpy(hdev->adv_data, cp.data, sizeof(cp.data)); hdev->adv_data_len = len; cp.length = len; - err = hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); -unlock: - hci_dev_unlock(hdev); - - return err; + hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); } /* ---- HCI ioctl helpers ---- */ @@ -1025,7 +1017,6 @@ int hci_dev_open(__u16 dev) hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); - hci_update_ad(hdev); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && mgmt_valid_hdev(hdev)) { hci_dev_lock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ed4ecd930a7..84edacbc14a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -223,9 +223,6 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); hci_dev_unlock(hdev); - - if (!status && !test_bit(HCI_INIT, &hdev->flags)) - hci_update_ad(hdev); } static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -776,11 +773,8 @@ static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (!rp->status) { + if (!rp->status) hdev->adv_tx_power = rp->tx_power; - if (!test_bit(HCI_INIT, &hdev->flags)) - hci_update_ad(hdev); - } } static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -877,10 +871,15 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags); } - hci_dev_unlock(hdev); + if (!test_bit(HCI_INIT, &hdev->flags)) { + struct hci_request req; - if (!test_bit(HCI_INIT, &hdev->flags)) - hci_update_ad(hdev); + hci_req_init(&req, hdev); + hci_update_ad(&req); + hci_req_run(&req, NULL); + } + + hci_dev_unlock(hdev); } static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) -- cgit v1.2.3-70-g09d2 From 1a4d3c4b3750885733641216756de4e4d9b2443a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:08 -0500 Subject: Bluetooth: Add proper flag for fast connectable mode In order to be able to represent fast connectable mode in the mgmt settings we need to have a HCI dev flag for it. This patch adds the flag and makes sure its value is changed whenever a mgmt_set_fast_connectable command completes. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 4 +++- net/bluetooth/mgmt.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 1e40222e9dd..b854506c859 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -120,12 +120,14 @@ enum { HCI_DISCOVERABLE, HCI_LINK_SECURITY, HCI_PERIODIC_INQ, + HCI_FAST_CONNECTABLE, }; /* A mask for the flags that are supposed to remain when a reset happens * or the HCI device is closed. */ -#define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ)) +#define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \ + BIT(HCI_FAST_CONNECTABLE)) /* HCI ioctl defines */ #define HCIDEVUP _IOW('H', 201, int) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e89938e0233..b6a33c5e768 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -410,6 +410,9 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_CONNECTABLE; + if (test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) + settings |= MGMT_SETTING_FAST_CONNECTABLE; + if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_DISCOVERABLE; @@ -2913,6 +2916,13 @@ static void fast_connectable_complete(struct hci_dev *hdev, u8 status) cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, mgmt_status(status)); } else { + struct mgmt_mode *cp = cmd->param; + + if (cp->val) + set_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); + else + clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); + send_settings_rsp(cmd->sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev); new_settings(hdev, cmd->sk); } @@ -2959,6 +2969,12 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, goto unlock; } + if (!!cp->val == test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) { + err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE, + hdev); + goto unlock; + } + if (cp->val) { type = PAGE_SCAN_TYPE_INTERLACED; -- cgit v1.2.3-70-g09d2 From f332ec6699980e0563408c7bcf1a8a31b825fee1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:11 -0500 Subject: Bluetooth: Add reading of page scan parameters These parameters are related to the "fast connectable" mode that can be changed through the mgmt interface. Not all controllers properly reset these values with HCI_Reset so they need to be read in order to be able to verify whether the values are correct or not before enabling page scan. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 13 +++++++++++++ include/net/bluetooth/hci_core.h | 4 ++++ net/bluetooth/hci_core.c | 6 ++++++ net/bluetooth/hci_event.c | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index b854506c859..b3308927a0a 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -887,12 +887,25 @@ struct hci_rp_read_data_block_size { __le16 num_blocks; } __packed; +#define HCI_OP_READ_PAGE_SCAN_ACTIVITY 0x0c1b +struct hci_rp_read_page_scan_activity { + __u8 status; + __le16 interval; + __le16 window; +} __packed; + #define HCI_OP_WRITE_PAGE_SCAN_ACTIVITY 0x0c1c struct hci_cp_write_page_scan_activity { __le16 interval; __le16 window; } __packed; +#define HCI_OP_READ_PAGE_SCAN_TYPE 0x0c46 +struct hci_rp_read_page_scan_type { + __u8 status; + __u8 type; +} __packed; + #define HCI_OP_WRITE_PAGE_SCAN_TYPE 0x0c47 #define PAGE_SCAN_TYPE_STANDARD 0x00 #define PAGE_SCAN_TYPE_INTERLACED 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index cb99193c749..358a6983d3b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -165,6 +165,10 @@ struct hci_dev { __u16 voice_setting; __u8 io_capability; __s8 inq_tx_power; + __u16 page_scan_interval; + __u16 page_scan_window; + __u8 page_scan_type; + __u16 devid_source; __u16 devid_vendor; __u16 devid_product; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0ffd3587117..cfcad5423f1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -272,6 +272,12 @@ static void bredr_setup(struct hci_request *req) bacpy(&cp.bdaddr, BDADDR_ANY); cp.delete_all = 0x01; hci_req_add(req, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); + + /* Read page scan parameters */ + if (req->hdev->hci_ver > BLUETOOTH_VER_1_1) { + hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL); + hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL); + } } static void le_setup(struct hci_request *req) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 84edacbc14a..3c6d0a4f78d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -601,6 +601,30 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) bacpy(&hdev->bdaddr, &rp->bdaddr); } +static void hci_cc_read_page_scan_activity(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_page_scan_activity *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (test_bit(HCI_INIT, &hdev->flags) && !rp->status) { + hdev->page_scan_interval = __le16_to_cpu(rp->interval); + hdev->page_scan_window = __le16_to_cpu(rp->window); + } +} + +static void hci_cc_read_page_scan_type(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_page_scan_type *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (test_bit(HCI_INIT, &hdev->flags) && !rp->status) + hdev->page_scan_type = rp->type; +} + static void hci_cc_read_data_block_size(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2204,6 +2228,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_bd_addr(hdev, skb); break; + case HCI_OP_READ_PAGE_SCAN_ACTIVITY: + hci_cc_read_page_scan_activity(hdev, skb); + break; + + case HCI_OP_READ_PAGE_SCAN_TYPE: + hci_cc_read_page_scan_type(hdev, skb); + break; + case HCI_OP_READ_DATA_BLOCK_SIZE: hci_cc_read_data_block_size(hdev, skb); break; -- cgit v1.2.3-70-g09d2 From ddbfe860acc39d4856a86186eb8a292426ea6224 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 8 Mar 2013 14:46:14 +0100 Subject: mac80211: move sdata debugfs dir to vif There is need create driver own per interface debugfs files. This is currently done by drv_{add,remove}_interface_debugfs() callbacks. But it is possible that after we remove interface from the driver (i.e. on suspend) we call drv_remove_interface_debugfs() function. Fixing this problem will require to add call drv_{add,remove}_interface_debugfs() anytime we create and remove interface in mac80211. So it's better to add debugfs dir dentry to vif structure to allow to create/remove custom debugfs driver files on drv_{add,remove}_interface(). Signed-off-by: Stanislaw Gruszka Signed-off-by: Johannes Berg --- include/net/mac80211.h | 7 +++++++ net/mac80211/debugfs_key.c | 10 +++++----- net/mac80211/debugfs_netdev.c | 22 +++++++++++----------- net/mac80211/driver-ops.h | 4 ++-- net/mac80211/ieee80211_i.h | 1 - 5 files changed, 25 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f5db5e97042..8b2c7506f5c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1067,6 +1067,9 @@ enum ieee80211_vif_flags { * path needing to access it; even though the netdev carrier will always * be off when it is %NULL there can still be races and packets could be * processed after it switches back to %NULL. + * @debugfs_dir: debugfs dentry, can be used by drivers to create own per + * interface debug files. Note that it will be NULL for the virtual + * monitor interface (if that is requested.) * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *). */ @@ -1083,6 +1086,10 @@ struct ieee80211_vif { u32 driver_flags; +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *debugfs_dir; +#endif + /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index c3a3082b72e..1521cabad3d 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -295,7 +295,7 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) char buf[50]; struct ieee80211_key *key; - if (!sdata->debugfs.dir) + if (!sdata->vif.debugfs_dir) return; lockdep_assert_held(&sdata->local->key_mtx); @@ -311,7 +311,7 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_unicast_key = debugfs_create_symlink("default_unicast_key", - sdata->debugfs.dir, buf); + sdata->vif.debugfs_dir, buf); } if (sdata->debugfs.default_multicast_key) { @@ -325,7 +325,7 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_multicast_key = debugfs_create_symlink("default_multicast_key", - sdata->debugfs.dir, buf); + sdata->vif.debugfs_dir, buf); } } @@ -334,7 +334,7 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) char buf[50]; struct ieee80211_key *key; - if (!sdata->debugfs.dir) + if (!sdata->vif.debugfs_dir) return; key = key_mtx_dereference(sdata->local, @@ -343,7 +343,7 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_mgmt_key = debugfs_create_symlink("default_mgmt_key", - sdata->debugfs.dir, buf); + sdata->vif.debugfs_dir, buf); } else ieee80211_debugfs_key_remove_mgmt_default(sdata); } diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 059bbb82e84..ddb42686790 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -521,7 +521,7 @@ IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration, #endif #define DEBUGFS_ADD_MODE(name, mode) \ - debugfs_create_file(#name, mode, sdata->debugfs.dir, \ + debugfs_create_file(#name, mode, sdata->vif.debugfs_dir, \ sdata, &name##_ops); #define DEBUGFS_ADD(name) DEBUGFS_ADD_MODE(name, 0400) @@ -577,7 +577,7 @@ static void add_mesh_files(struct ieee80211_sub_if_data *sdata) static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) { struct dentry *dir = debugfs_create_dir("mesh_stats", - sdata->debugfs.dir); + sdata->vif.debugfs_dir); #define MESHSTATS_ADD(name)\ debugfs_create_file(#name, 0400, dir, sdata, &name##_ops); @@ -594,7 +594,7 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) static void add_mesh_config(struct ieee80211_sub_if_data *sdata) { struct dentry *dir = debugfs_create_dir("mesh_config", - sdata->debugfs.dir); + sdata->vif.debugfs_dir); #define MESHPARAMS_ADD(name) \ debugfs_create_file(#name, 0600, dir, sdata, &name##_ops); @@ -631,7 +631,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata) static void add_files(struct ieee80211_sub_if_data *sdata) { - if (!sdata->debugfs.dir) + if (!sdata->vif.debugfs_dir) return; DEBUGFS_ADD(flags); @@ -673,21 +673,21 @@ void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata) char buf[10+IFNAMSIZ]; sprintf(buf, "netdev:%s", sdata->name); - sdata->debugfs.dir = debugfs_create_dir(buf, + sdata->vif.debugfs_dir = debugfs_create_dir(buf, sdata->local->hw.wiphy->debugfsdir); - if (sdata->debugfs.dir) + if (sdata->vif.debugfs_dir) sdata->debugfs.subdir_stations = debugfs_create_dir("stations", - sdata->debugfs.dir); + sdata->vif.debugfs_dir); add_files(sdata); } void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata) { - if (!sdata->debugfs.dir) + if (!sdata->vif.debugfs_dir) return; - debugfs_remove_recursive(sdata->debugfs.dir); - sdata->debugfs.dir = NULL; + debugfs_remove_recursive(sdata->vif.debugfs_dir); + sdata->vif.debugfs_dir = NULL; } void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata) @@ -695,7 +695,7 @@ void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata) struct dentry *dir; char buf[10 + IFNAMSIZ]; - dir = sdata->debugfs.dir; + dir = sdata->vif.debugfs_dir; if (!dir) return; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 025b7592b79..0059f3886d4 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -560,7 +560,7 @@ void drv_add_interface_debugfs(struct ieee80211_local *local, return; local->ops->add_interface_debugfs(&local->hw, &sdata->vif, - sdata->debugfs.dir); + sdata->vif.debugfs_dir); } static inline @@ -575,7 +575,7 @@ void drv_remove_interface_debugfs(struct ieee80211_local *local, return; local->ops->remove_interface_debugfs(&local->hw, &sdata->vif, - sdata->debugfs.dir); + sdata->vif.debugfs_dir); } #else static inline diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 95beb18588f..d6e92060982 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -758,7 +758,6 @@ struct ieee80211_sub_if_data { #ifdef CONFIG_MAC80211_DEBUGFS struct { - struct dentry *dir; struct dentry *subdir_stations; struct dentry *default_unicast_key; struct dentry *default_multicast_key; -- cgit v1.2.3-70-g09d2 From d260ff12e7768444b4da7612b785cfd7cbc1d1c1 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 8 Mar 2013 14:46:15 +0100 Subject: mac80211: remove vif debugfs driver callbacks This basically reverts commit b207cdb07f3f01ec1adaac62e9d0cc918c60a81a. Now is possible to use drv_{add,remove}_interface() and vif->debugfs_dir to create/remove per interface debugfs files. Remove redundant callbacks. Signed-off-by: Stanislaw Gruszka Signed-off-by: Johannes Berg --- include/net/mac80211.h | 18 ------------------ net/mac80211/driver-ops.h | 37 ------------------------------------- net/mac80211/iface.c | 4 ---- 3 files changed, 59 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8b2c7506f5c..0b912d22f82 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2233,18 +2233,6 @@ enum ieee80211_roc_type { * MAC address of the device going away. * Hence, this callback must be implemented. It can sleep. * - * @add_interface_debugfs: Drivers can use this callback to add debugfs files - * when a vif is added to mac80211. This callback and - * @remove_interface_debugfs should be within a CONFIG_MAC80211_DEBUGFS - * conditional. @remove_interface_debugfs must be provided for cleanup. - * This callback can sleep. - * - * @remove_interface_debugfs: Remove the debugfs files which were added using - * @add_interface_debugfs. This callback must remove all debugfs entries - * that were added because mac80211 only removes interface debugfs when the - * interface is destroyed, not when it is removed from the driver. - * This callback can sleep. - * * @config: Handler for configuration requests. IEEE 802.11 code calls this * function to change hardware configuration, e.g., channel. * This function should never fail but returns a negative error code @@ -2665,12 +2653,6 @@ struct ieee80211_ops { struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); - void (*add_interface_debugfs)(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct dentry *dir); - void (*remove_interface_debugfs)(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct dentry *dir); #endif void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd, struct ieee80211_sta *sta); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 0059f3886d4..7b9ff53bd2e 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -547,43 +547,6 @@ static inline void drv_sta_remove_debugfs(struct ieee80211_local *local, local->ops->sta_remove_debugfs(&local->hw, &sdata->vif, sta, dir); } - -static inline -void drv_add_interface_debugfs(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) -{ - might_sleep(); - - check_sdata_in_driver(sdata); - - if (!local->ops->add_interface_debugfs) - return; - - local->ops->add_interface_debugfs(&local->hw, &sdata->vif, - sdata->vif.debugfs_dir); -} - -static inline -void drv_remove_interface_debugfs(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) -{ - might_sleep(); - - check_sdata_in_driver(sdata); - - if (!local->ops->remove_interface_debugfs) - return; - - local->ops->remove_interface_debugfs(&local->hw, &sdata->vif, - sdata->vif.debugfs_dir); -} -#else -static inline -void drv_add_interface_debugfs(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) {} -static inline -void drv_remove_interface_debugfs(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) {} #endif static inline __must_check diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 9875e321c9e..80e838bc875 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -557,8 +557,6 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) goto err_del_interface; } - drv_add_interface_debugfs(local, sdata); - if (sdata->vif.type == NL80211_IFTYPE_AP) { local->fif_pspoll++; local->fif_probe_req++; @@ -846,8 +844,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: skb_queue_purge(&sdata->skb_queue); - drv_remove_interface_debugfs(local, sdata); - if (going_down) drv_remove_interface(local, sdata); } -- cgit v1.2.3-70-g09d2 From 39ecc01d1bbe3de2cf5f01a81e176ea5160d3b95 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Feb 2013 12:11:00 +0100 Subject: mac80211: pass queue bitmap to flush operation There are a number of situations in which mac80211 only really needs to flush queues for one virtual interface, and in fact during this frames might be transmitted on other virtual interfaces. Calculate and pass a queue bitmap to the driver so it knows which queues to flush. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ar5523/ar5523.c | 2 +- drivers/net/wireless/ath/ath9k/main.c | 2 +- drivers/net/wireless/ath/carl9170/main.c | 2 +- .../net/wireless/brcm80211/brcmsmac/mac80211_if.c | 2 +- drivers/net/wireless/iwlegacy/common.c | 3 +-- drivers/net/wireless/iwlegacy/common.h | 2 +- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 2 +- drivers/net/wireless/mac80211_hwsim.c | 2 +- drivers/net/wireless/p54/main.c | 2 +- drivers/net/wireless/rt2x00/rt2x00.h | 2 +- drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +- drivers/net/wireless/rtlwifi/core.c | 2 +- drivers/net/wireless/ti/wlcore/main.c | 2 +- include/net/mac80211.h | 9 +++++--- net/mac80211/driver-ops.h | 7 +++--- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/iface.c | 2 +- net/mac80211/mlme.c | 8 +++---- net/mac80211/offchannel.c | 4 ++-- net/mac80211/pm.c | 2 +- net/mac80211/scan.c | 4 ++-- net/mac80211/trace.h | 11 ++++++---- net/mac80211/util.c | 25 ++++++++++++++++++++++ 23 files changed, 67 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 7157f7d311c..afd1e36d308 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1091,7 +1091,7 @@ static int ar5523_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return ret; } -static void ar5523_flush(struct ieee80211_hw *hw, bool drop) +static void ar5523_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct ar5523 *ar = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 6e66f9c6782..24650fd4169 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1745,7 +1745,7 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) mutex_unlock(&sc->mutex); } -static void ath9k_flush(struct ieee80211_hw *hw, bool drop) +static void ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index f293b3ff475..08b19319994 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1703,7 +1703,7 @@ found: return 0; } -static void carl9170_op_flush(struct ieee80211_hw *hw, bool drop) +static void carl9170_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct ar9170 *ar = hw->priv; unsigned int vid; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index c6451c61407..aa5f43fee5e 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -723,7 +723,7 @@ static bool brcms_tx_flush_completed(struct brcms_info *wl) return result; } -static void brcms_ops_flush(struct ieee80211_hw *hw, bool drop) +static void brcms_ops_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct brcms_info *wl = hw->priv; int ret; diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index e006ea83132..722bfb57cfd 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -4704,8 +4704,7 @@ out: } EXPORT_SYMBOL(il_mac_change_interface); -void -il_mac_flush(struct ieee80211_hw *hw, bool drop) +void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct il_priv *il = hw->priv; unsigned long timeout = jiffies + msecs_to_jiffies(500); diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index 73bd3ef316c..728aa1306ab 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -1720,7 +1720,7 @@ void il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype newtype, bool newp2p); -void il_mac_flush(struct ieee80211_hw *hw, bool drop); +void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop); int il_alloc_txq_mem(struct il_priv *il); void il_free_txq_mem(struct il_priv *il); diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index c7cd2dffa5c..a7294fa4d7e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1100,7 +1100,7 @@ static void iwlagn_configure_filter(struct ieee80211_hw *hw, FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; } -static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) +static void iwlagn_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 7490c4fc717..3466f1a8a8d 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1389,7 +1389,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, return 0; } -static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop) +static void mac80211_hwsim_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { /* Not implemented, queues only on kernel side */ } diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index aadda99989c..ee654a691f3 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -670,7 +670,7 @@ static unsigned int p54_flush_count(struct p54_common *priv) return total; } -static void p54_flush(struct ieee80211_hw *dev, bool drop) +static void p54_flush(struct ieee80211_hw *dev, u32 queues, bool drop) { struct p54_common *priv = dev->priv; unsigned int total, i; diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 086abb403a4..041b392f4f4 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1360,7 +1360,7 @@ int rt2x00mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw); -void rt2x00mac_flush(struct ieee80211_hw *hw, bool drop); +void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop); int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 20c6eccce5a..9161c02d8ff 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -748,7 +748,7 @@ void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw) } EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll); -void rt2x00mac_flush(struct ieee80211_hw *hw, bool drop) +void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index d3ce9fbef00..b5a7a260bf6 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -1166,7 +1166,7 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw) * before switch channle or power save, or tx buffer packet * maybe send after offchannel or rf sleep, this may cause * dis-association by AP */ -static void rtl_op_flush(struct ieee80211_hw *hw, bool drop) +static void rtl_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index d7e306333f6..a9f7041c719 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4946,7 +4946,7 @@ out: mutex_unlock(&wl->mutex); } -static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop) +static void wlcore_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop) { struct wl1271 *wl = hw->priv; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0b912d22f82..4158da74e11 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2438,8 +2438,11 @@ enum ieee80211_roc_type { * @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep. * * @flush: Flush all pending frames from the hardware queue, making sure - * that the hardware queues are empty. If the parameter @drop is set - * to %true, pending frames may be dropped. The callback can sleep. + * that the hardware queues are empty. The @queues parameter is a bitmap + * of queues to flush, which is useful if different virtual interfaces + * use different hardware queues; it may also indicate all queues. + * If the parameter @drop is set to %true, pending frames may be dropped. + * The callback can sleep. * * @channel_switch: Drivers that need (or want) to offload the channel * switch operation for CSAs received from the AP may implement this @@ -2687,7 +2690,7 @@ struct ieee80211_ops { struct netlink_callback *cb, void *data, int len); #endif - void (*flush)(struct ieee80211_hw *hw, bool drop); + void (*flush)(struct ieee80211_hw *hw, u32 queues, bool drop); void (*channel_switch)(struct ieee80211_hw *hw, struct ieee80211_channel_switch *ch_switch); int (*napi_poll)(struct ieee80211_hw *hw, int budget); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 7b9ff53bd2e..169664c122e 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -720,13 +720,14 @@ static inline void drv_rfkill_poll(struct ieee80211_local *local) local->ops->rfkill_poll(&local->hw); } -static inline void drv_flush(struct ieee80211_local *local, bool drop) +static inline void drv_flush(struct ieee80211_local *local, + u32 queues, bool drop) { might_sleep(); - trace_drv_flush(local, drop); + trace_drv_flush(local, queues, drop); if (local->ops->flush) - local->ops->flush(&local->hw, drop); + local->ops->flush(&local->hw, queues, drop); trace_drv_return_void(local); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d6e92060982..b96c0e97775 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1540,6 +1540,8 @@ static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local, { ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL); } +void ieee80211_flush_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata); void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 80e838bc875..d646e12e55a 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -92,7 +92,7 @@ static u32 ieee80211_idle_on(struct ieee80211_local *local) if (local->hw.conf.flags & IEEE80211_CONF_IDLE) return 0; - drv_flush(local, false); + ieee80211_flush_queues(local, NULL); local->hw.conf.flags |= IEEE80211_CONF_IDLE; return IEEE80211_CONF_CHANGE_IDLE; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index fdc06e381c1..65b38e13eb0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1436,7 +1436,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) else { ieee80211_send_nullfunc(local, sdata, 1); /* Flush to get the tx status of nullfunc frame */ - drv_flush(local, false); + ieee80211_flush_queues(local, sdata); } } @@ -1767,7 +1767,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */ if (tx) - drv_flush(local, false); + ieee80211_flush_queues(local, sdata); /* deauthenticate/disassociate now */ if (tx || frame_buf) @@ -1776,7 +1776,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, /* flush out frame */ if (tx) - drv_flush(local, false); + ieee80211_flush_queues(local, sdata); /* clear bssid only after building the needed mgmt frames */ memset(ifmgd->bssid, 0, ETH_ALEN); @@ -1948,7 +1948,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); run_again(ifmgd, ifmgd->probe_timeout); if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) - drv_flush(sdata->local, false); + ieee80211_flush_queues(sdata->local, sdata); } static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index db547fceaeb..d32f514074b 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -120,7 +120,7 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) */ ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL); - drv_flush(local, false); + ieee80211_flush_queues(local, NULL); mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { @@ -373,7 +373,7 @@ void ieee80211_sw_roc_work(struct work_struct *work) ieee80211_roc_notify_destroy(roc); if (started) { - drv_flush(local, false); + ieee80211_flush_queues(local, NULL); local->tmp_channel = NULL; ieee80211_hw_config(local, 0); diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index b471a67f224..497f21a0d11 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -35,7 +35,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* flush out all packets */ synchronize_net(); - drv_flush(local, false); + ieee80211_flush_queues(local, NULL); local->quiescing = true; /* make quiescing visible to timers everywhere */ diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 5dc17c623f7..cb34cbbaa20 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -332,7 +332,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) ieee80211_offchannel_stop_vifs(local); /* ensure nullfunc is transmitted before leaving operating channel */ - drv_flush(local, false); + ieee80211_flush_queues(local, NULL); ieee80211_configure_filter(local); @@ -668,7 +668,7 @@ static void ieee80211_scan_state_resume(struct ieee80211_local *local, ieee80211_offchannel_stop_vifs(local); if (local->ops->flush) { - drv_flush(local, false); + ieee80211_flush_queues(local, NULL); *next_delay = 0; } else *next_delay = HZ / 10; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index d97e4305cf1..c5899797a8d 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -964,23 +964,26 @@ TRACE_EVENT(drv_get_survey, ); TRACE_EVENT(drv_flush, - TP_PROTO(struct ieee80211_local *local, bool drop), + TP_PROTO(struct ieee80211_local *local, + u32 queues, bool drop), - TP_ARGS(local, drop), + TP_ARGS(local, queues, drop), TP_STRUCT__entry( LOCAL_ENTRY __field(bool, drop) + __field(u32, queues) ), TP_fast_assign( LOCAL_ASSIGN; __entry->drop = drop; + __entry->queues = queues; ), TP_printk( - LOCAL_PR_FMT " drop:%d", - LOCAL_PR_ARG, __entry->drop + LOCAL_PR_FMT " queues:0x%x drop:%d", + LOCAL_PR_ARG, __entry->queues, __entry->drop ) ); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index b7a856e3281..f978ddd1bb4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -511,6 +511,31 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_wake_queues); +void ieee80211_flush_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + u32 queues; + + if (!local->ops->flush) + return; + + if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) { + int ac; + + queues = 0; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + queues |= BIT(sdata->vif.hw_queue[ac]); + if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE) + queues |= BIT(sdata->vif.cab_queue); + } else { + /* all queues */ + queues = BIT(local->hw.queues) - 1; + } + + drv_flush(local, queues, false); +} + void ieee80211_iterate_active_interfaces( struct ieee80211_hw *hw, u32 iter_flags, void (*iterator)(void *data, u8 *mac, -- cgit v1.2.3-70-g09d2 From 445ea4e83ec50668cc9ad7e5cf96d242f19165e8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Feb 2013 12:25:28 +0100 Subject: mac80211: stop queues temporarily for flushing Sometimes queues are flushed in the middle of operation, which can lead to driver issues. Stop queues temporarily, while flushing, to avoid transmitting new packets while they are being flushed. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 ++ net/mac80211/ieee80211_i.h | 3 +++ net/mac80211/main.c | 4 ++-- net/mac80211/mlme.c | 4 ++++ net/mac80211/offchannel.c | 4 ++-- net/mac80211/pm.c | 4 +++- net/mac80211/tx.c | 1 + net/mac80211/util.c | 23 ++++++++++++++++------- 8 files changed, 33 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 4158da74e11..dd73b8c6746 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -93,9 +93,11 @@ struct device; * enum ieee80211_max_queues - maximum number of queues * * @IEEE80211_MAX_QUEUES: Maximum number of regular device queues. + * @IEEE80211_MAX_QUEUE_MAP: bitmap with maximum queues set */ enum ieee80211_max_queues { IEEE80211_MAX_QUEUES = 16, + IEEE80211_MAX_QUEUE_MAP = BIT(IEEE80211_MAX_QUEUES) - 1, }; #define IEEE80211_INVAL_HW_QUEUE 0xff diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b96c0e97775..ae2d1754b79 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -809,6 +809,7 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_SUSPEND, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, + IEEE80211_QUEUE_STOP_REASON_FLUSH, }; #ifdef CONFIG_MAC80211_LEDS @@ -1522,8 +1523,10 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr, bool ack); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, + unsigned long queues, enum queue_stop_reason reason); void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, + unsigned long queues, enum queue_stop_reason reason); void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index eee1768e89c..c6f81ecc36a 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -277,8 +277,8 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) "Hardware restart was requested\n"); /* use this reason, ieee80211_reconfig will unblock it */ - ieee80211_stop_queues_by_reason(hw, - IEEE80211_QUEUE_STOP_REASON_SUSPEND); + ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_SUSPEND); /* * Stop all Rx during the reconfig. We don't want state changes diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 65b38e13eb0..4d383a93ea7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1009,6 +1009,7 @@ static void ieee80211_chswitch_work(struct work_struct *work) /* XXX: wait for a beacon first? */ ieee80211_wake_queues_by_reason(&sdata->local->hw, + IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); out: ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; @@ -1108,6 +1109,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (sw_elem->mode) ieee80211_stop_queues_by_reason(&sdata->local->hw, + IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); if (sdata->local->ops->channel_switch) { @@ -1375,6 +1377,7 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work) } ieee80211_wake_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_PS); } @@ -2071,6 +2074,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) true, frame_buf); ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; ieee80211_wake_queues_by_reason(&sdata->local->hw, + IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); mutex_unlock(&ifmgd->mtx); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index d32f514074b..b01eb7314ec 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -118,7 +118,7 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) * Stop queues and transmit all frames queued by the driver * before sending nullfunc to enable powersave at the AP. */ - ieee80211_stop_queues_by_reason(&local->hw, + ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL); ieee80211_flush_queues(local, NULL); @@ -181,7 +181,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local) } mutex_unlock(&local->iflist_mtx); - ieee80211_wake_queues_by_reason(&local->hw, + ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL); } diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 497f21a0d11..3d16f4e6174 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -30,7 +30,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) } ieee80211_stop_queues_by_reason(hw, - IEEE80211_QUEUE_STOP_REASON_SUSPEND); + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_SUSPEND); /* flush out all packets */ synchronize_net(); @@ -68,6 +69,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) mutex_unlock(&local->sta_mtx); } ieee80211_wake_queues_by_reason(hw, + IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_SUSPEND); return err; } else if (err > 0) { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3fcdf211810..2a6ae8030bd 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -233,6 +233,7 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx) if (local->hw.conf.flags & IEEE80211_CONF_PS) { ieee80211_stop_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_PS); ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; ieee80211_queue_work(&local->hw, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index f978ddd1bb4..a7368870c8e 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -453,7 +453,8 @@ void ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, } void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, - enum queue_stop_reason reason) + unsigned long queues, + enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); unsigned long flags; @@ -461,7 +462,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - for (i = 0; i < hw->queues; i++) + for_each_set_bit(i, &queues, hw->queues) __ieee80211_stop_queue(hw, i, reason); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -469,7 +470,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, void ieee80211_stop_queues(struct ieee80211_hw *hw) { - ieee80211_stop_queues_by_reason(hw, + ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_DRIVER); } EXPORT_SYMBOL(ieee80211_stop_queues); @@ -491,6 +492,7 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) EXPORT_SYMBOL(ieee80211_queue_stopped); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, + unsigned long queues, enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); @@ -499,7 +501,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - for (i = 0; i < hw->queues; i++) + for_each_set_bit(i, &queues, hw->queues) __ieee80211_wake_queue(hw, i, reason); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -507,7 +509,8 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, void ieee80211_wake_queues(struct ieee80211_hw *hw) { - ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER); + ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_DRIVER); } EXPORT_SYMBOL(ieee80211_wake_queues); @@ -533,7 +536,13 @@ void ieee80211_flush_queues(struct ieee80211_local *local, queues = BIT(local->hw.queues) - 1; } + ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_FLUSH); + drv_flush(local, queues, false); + + ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_FLUSH); } void ieee80211_iterate_active_interfaces( @@ -1676,8 +1685,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) mutex_unlock(&local->sta_mtx); } - ieee80211_wake_queues_by_reason(hw, - IEEE80211_QUEUE_STOP_REASON_SUSPEND); + ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_SUSPEND); /* * If this is for hw restart things are still running. -- cgit v1.2.3-70-g09d2 From 70bc126471af30bb115e635512dcf6d86fe6e29a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 08:20:52 -0500 Subject: tty: Add safe tty throttle/unthrottle functions The tty driver can become stuck throttled due to race conditions between throttle and unthrottle, when the decision to throttle or unthrottle is conditional. The following example helps to illustrate the race: CPU 0 | CPU 1 | if (condition A) | | | if (!condition A) | unthrottle() throttle() | | Note the converse is also possible; ie., CPU 0 | CPU 1 | | if (!condition A) | if (condition A) | throttle() | | unthrottle() | Add new throttle/unthrottle functions based on the familiar model of task state and schedule/wake. For example, while (1) { tty_set_flow_change(tty, TTY_THROTTLE_SAFE); if (!condition) break; if (!tty_throttle_safe(tty)) break; } __tty_set_flow_change(tty, 0); In this example, if an unthrottle occurs after the condition is evaluated but before tty_throttle_safe(), then tty_throttle_safe() will return non-zero, looping and forcing the re-evaluation of condition. Reported-by: Vincent Pillet Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ioctl.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/tty.h | 18 ++++++++++++++ 2 files changed, 82 insertions(+) (limited to 'include') diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index d58b92cc187..132d452578b 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -106,6 +106,7 @@ void tty_throttle(struct tty_struct *tty) if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && tty->ops->throttle) tty->ops->throttle(tty); + tty->flow_change = 0; mutex_unlock(&tty->termios_mutex); } EXPORT_SYMBOL(tty_throttle); @@ -129,10 +130,73 @@ void tty_unthrottle(struct tty_struct *tty) if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->ops->unthrottle) tty->ops->unthrottle(tty); + tty->flow_change = 0; mutex_unlock(&tty->termios_mutex); } EXPORT_SYMBOL(tty_unthrottle); +/** + * tty_throttle_safe - flow control + * @tty: terminal + * + * Similar to tty_throttle() but will only attempt throttle + * if tty->flow_change is TTY_THROTTLE_SAFE. Prevents an accidental + * throttle due to race conditions when throttling is conditional + * on factors evaluated prior to throttling. + * + * Returns 0 if tty is throttled (or was already throttled) + */ + +int tty_throttle_safe(struct tty_struct *tty) +{ + int ret = 0; + + mutex_lock(&tty->termios_mutex); + if (!test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty->flow_change != TTY_THROTTLE_SAFE) + ret = 1; + else { + __set_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->throttle) + tty->ops->throttle(tty); + } + } + mutex_unlock(&tty->termios_mutex); + + return ret; +} + +/** + * tty_unthrottle_safe - flow control + * @tty: terminal + * + * Similar to tty_unthrottle() but will only attempt unthrottle + * if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental + * unthrottle due to race conditions when unthrottling is conditional + * on factors evaluated prior to unthrottling. + * + * Returns 0 if tty is unthrottled (or was already unthrottled) + */ + +int tty_unthrottle_safe(struct tty_struct *tty) +{ + int ret = 0; + + mutex_lock(&tty->termios_mutex); + if (test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty->flow_change != TTY_UNTHROTTLE_SAFE) + ret = 1; + else { + __clear_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->unthrottle) + tty->ops->unthrottle(tty); + } + } + mutex_unlock(&tty->termios_mutex); + + return ret; +} + /** * tty_wait_until_sent - wait for I/O to finish * @tty: tty we are waiting for diff --git a/include/linux/tty.h b/include/linux/tty.h index c75d886b030..189ca80494d 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -258,6 +258,7 @@ struct tty_struct { unsigned char warned:1; unsigned char ctrl_status; /* ctrl_lock */ unsigned int receive_room; /* Bytes free for queue */ + int flow_change; struct tty_struct *link; struct fasync_struct *fasync; @@ -318,6 +319,21 @@ struct tty_file_private { #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty)) +/* Values for tty->flow_change */ +#define TTY_THROTTLE_SAFE 1 +#define TTY_UNTHROTTLE_SAFE 2 + +static inline void __tty_set_flow_change(struct tty_struct *tty, int val) +{ + tty->flow_change = val; +} + +static inline void tty_set_flow_change(struct tty_struct *tty, int val) +{ + tty->flow_change = val; + smp_mb(); +} + #ifdef CONFIG_TTY extern void console_init(void); extern void tty_kref_put(struct tty_struct *tty); @@ -400,6 +416,8 @@ extern int tty_write_room(struct tty_struct *tty); extern void tty_driver_flush_buffer(struct tty_struct *tty); extern void tty_throttle(struct tty_struct *tty); extern void tty_unthrottle(struct tty_struct *tty); +extern int tty_throttle_safe(struct tty_struct *tty); +extern int tty_unthrottle_safe(struct tty_struct *tty); extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws); extern void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty); -- cgit v1.2.3-70-g09d2 From 6be06e7273c4682a15ca1f4adf1aeae510823530 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 6 Mar 2013 08:38:21 -0500 Subject: tty: Fix checkpatch errors in tty_ldisc.h Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- include/linux/tty_ldisc.h | 132 +++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 66 deletions(-) (limited to 'include') diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 455a0d7bf22..58390c73df8 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -9,89 +9,89 @@ * * int (*open)(struct tty_struct *); * - * This function is called when the line discipline is associated - * with the tty. The line discipline can use this as an - * opportunity to initialize any state needed by the ldisc routines. - * + * This function is called when the line discipline is associated + * with the tty. The line discipline can use this as an + * opportunity to initialize any state needed by the ldisc routines. + * * void (*close)(struct tty_struct *); * * This function is called when the line discipline is being - * shutdown, either because the tty is being closed or because - * the tty is being changed to use a new line discipline - * + * shutdown, either because the tty is being closed or because + * the tty is being changed to use a new line discipline + * * void (*flush_buffer)(struct tty_struct *tty); * - * This function instructs the line discipline to clear its - * buffers of any input characters it may have queued to be - * delivered to the user mode process. - * + * This function instructs the line discipline to clear its + * buffers of any input characters it may have queued to be + * delivered to the user mode process. + * * ssize_t (*chars_in_buffer)(struct tty_struct *tty); * - * This function returns the number of input characters the line + * This function returns the number of input characters the line * discipline may have queued up to be delivered to the user mode * process. - * + * * ssize_t (*read)(struct tty_struct * tty, struct file * file, * unsigned char * buf, size_t nr); * - * This function is called when the user requests to read from - * the tty. The line discipline will return whatever characters - * it has buffered up for the user. If this function is not - * defined, the user will receive an EIO error. - * + * This function is called when the user requests to read from + * the tty. The line discipline will return whatever characters + * it has buffered up for the user. If this function is not + * defined, the user will receive an EIO error. + * * ssize_t (*write)(struct tty_struct * tty, struct file * file, - * const unsigned char * buf, size_t nr); - * - * This function is called when the user requests to write to the - * tty. The line discipline will deliver the characters to the - * low-level tty device for transmission, optionally performing - * some processing on the characters first. If this function is - * not defined, the user will receive an EIO error. - * + * const unsigned char * buf, size_t nr); + * + * This function is called when the user requests to write to the + * tty. The line discipline will deliver the characters to the + * low-level tty device for transmission, optionally performing + * some processing on the characters first. If this function is + * not defined, the user will receive an EIO error. + * * int (*ioctl)(struct tty_struct * tty, struct file * file, - * unsigned int cmd, unsigned long arg); + * unsigned int cmd, unsigned long arg); * * This function is called when the user requests an ioctl which - * is not handled by the tty layer or the low-level tty driver. - * It is intended for ioctls which affect line discpline - * operation. Note that the search order for ioctls is (1) tty - * layer, (2) tty low-level driver, (3) line discpline. So a - * low-level driver can "grab" an ioctl request before the line - * discpline has a chance to see it. - * + * is not handled by the tty layer or the low-level tty driver. + * It is intended for ioctls which affect line discpline + * operation. Note that the search order for ioctls is (1) tty + * layer, (2) tty low-level driver, (3) line discpline. So a + * low-level driver can "grab" an ioctl request before the line + * discpline has a chance to see it. + * * long (*compat_ioctl)(struct tty_struct * tty, struct file * file, - * unsigned int cmd, unsigned long arg); + * unsigned int cmd, unsigned long arg); * - * Process ioctl calls from 32-bit process on 64-bit system + * Process ioctl calls from 32-bit process on 64-bit system * * void (*set_termios)(struct tty_struct *tty, struct ktermios * old); * - * This function notifies the line discpline that a change has - * been made to the termios structure. - * + * This function notifies the line discpline that a change has + * been made to the termios structure. + * * int (*poll)(struct tty_struct * tty, struct file * file, - * poll_table *wait); + * poll_table *wait); * - * This function is called when a user attempts to select/poll on a - * tty device. It is solely the responsibility of the line - * discipline to handle poll requests. + * This function is called when a user attempts to select/poll on a + * tty device. It is solely the responsibility of the line + * discipline to handle poll requests. * * void (*receive_buf)(struct tty_struct *, const unsigned char *cp, - * char *fp, int count); - * - * This function is called by the low-level tty driver to send - * characters received by the hardware to the line discpline for - * processing. is a pointer to the buffer of input - * character received by the device. is a pointer to a - * pointer of flag bytes which indicate whether a character was - * received with a parity error, etc. - * + * char *fp, int count); + * + * This function is called by the low-level tty driver to send + * characters received by the hardware to the line discpline for + * processing. is a pointer to the buffer of input + * character received by the device. is a pointer to a + * pointer of flag bytes which indicate whether a character was + * received with a parity error, etc. + * * void (*write_wakeup)(struct tty_struct *); * - * This function is called by the low-level tty driver to signal - * that line discpline should try to send more characters to the - * low-level driver for transmission. If the line discpline does - * not have any more data to send, it can just return. + * This function is called by the low-level tty driver to signal + * that line discpline should try to send more characters to the + * low-level driver for transmission. If the line discpline does + * not have any more data to send, it can just return. * * int (*hangup)(struct tty_struct *) * @@ -115,7 +115,7 @@ struct tty_ldisc_ops { char *name; int num; int flags; - + /* * The following routines are called from above. */ @@ -123,19 +123,19 @@ struct tty_ldisc_ops { void (*close)(struct tty_struct *); void (*flush_buffer)(struct tty_struct *tty); ssize_t (*chars_in_buffer)(struct tty_struct *tty); - ssize_t (*read)(struct tty_struct * tty, struct file * file, - unsigned char __user * buf, size_t nr); - ssize_t (*write)(struct tty_struct * tty, struct file * file, - const unsigned char * buf, size_t nr); - int (*ioctl)(struct tty_struct * tty, struct file * file, + ssize_t (*read)(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr); + ssize_t (*write)(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr); + int (*ioctl)(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); - long (*compat_ioctl)(struct tty_struct * tty, struct file * file, + long (*compat_ioctl)(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); - void (*set_termios)(struct tty_struct *tty, struct ktermios * old); + void (*set_termios)(struct tty_struct *tty, struct ktermios *old); unsigned int (*poll)(struct tty_struct *, struct file *, struct poll_table_struct *); int (*hangup)(struct tty_struct *tty); - + /* * The following routines are called from below. */ @@ -145,7 +145,7 @@ struct tty_ldisc_ops { void (*dcd_change)(struct tty_struct *, unsigned int); struct module *owner; - + int refcount; }; -- cgit v1.2.3-70-g09d2 From 6865ff222ccab371c04afce17aec1f7d70b17dbc Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:27 +0100 Subject: TTY: do not warn about setting speed via SPD_* The warning is there since 2.1.69 and we have not seen anybody reporting it in the past decade. Remove the warning now. tty_get_baud_rate can now be inline. This gives us one less EXPORT_SYMBOL. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ioctl.c | 28 ---------------------------- include/linux/tty.h | 18 ++++++++++++++++-- 2 files changed, 16 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 132d452578b..28715e48b2f 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -478,34 +478,6 @@ void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) } EXPORT_SYMBOL_GPL(tty_encode_baud_rate); -/** - * tty_get_baud_rate - get tty bit rates - * @tty: tty to query - * - * Returns the baud rate as an integer for this terminal. The - * termios lock must be held by the caller and the terminal bit - * flags may be updated. - * - * Locking: none - */ - -speed_t tty_get_baud_rate(struct tty_struct *tty) -{ - speed_t baud = tty_termios_baud_rate(&tty->termios); - - if (baud == 38400 && tty->alt_speed) { - if (!tty->warned) { - printk(KERN_WARNING "Use of setserial/setrocket to " - "set SPD_* flags is deprecated\n"); - tty->warned = 1; - } - baud = tty->alt_speed; - } - - return baud; -} -EXPORT_SYMBOL(tty_get_baud_rate); - /** * tty_termios_copy_hw - copy hardware settings * @new: New termios diff --git a/include/linux/tty.h b/include/linux/tty.h index 189ca80494d..63b62865c8e 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -255,7 +255,6 @@ struct tty_struct { int count; struct winsize winsize; /* termios mutex */ unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; - unsigned char warned:1; unsigned char ctrl_status; /* ctrl_lock */ unsigned int receive_room; /* Bytes free for queue */ int flow_change; @@ -437,13 +436,28 @@ extern void tty_flush_to_ldisc(struct tty_struct *tty); extern void tty_buffer_free_all(struct tty_port *port); extern void tty_buffer_flush(struct tty_struct *tty); extern void tty_buffer_init(struct tty_port *port); -extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); extern void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud); extern void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud); + +/** + * tty_get_baud_rate - get tty bit rates + * @tty: tty to query + * + * Returns the baud rate as an integer for this terminal. The + * termios lock must be held by the caller and the terminal bit + * flags may be updated. + * + * Locking: none + */ +static inline speed_t tty_get_baud_rate(struct tty_struct *tty) +{ + return tty_termios_baud_rate(&tty->termios); +} + extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old); extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b); extern int tty_set_termios(struct tty_struct *tty, struct ktermios *kt); -- cgit v1.2.3-70-g09d2 From 6aad04f21374633bd8cecf25024553d1e11a9522 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:29 +0100 Subject: TTY: add tty_port_tty_wakeup helper It allows for cleaning up on a considerable amount of places. They did port_get, wakeup, kref_put. Now the only thing needed is to call tty_port_tty_wakeup which does exactly that. One exception is ifx6x60 where tty_wakeup was open-coded. We now call tty_wakeup properly there. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- arch/um/drivers/line.c | 8 +------- drivers/isdn/capi/capi.c | 7 +------ drivers/isdn/gigaset/interface.c | 6 +----- drivers/net/usb/hso.c | 13 ++----------- drivers/s390/char/sclp_tty.c | 9 ++------- drivers/s390/char/sclp_vt220.c | 8 +------- drivers/staging/fwserial/fwserial.c | 10 ++-------- drivers/staging/serqt_usb2/serqt_usb2.c | 7 +------ drivers/tty/ehv_bytechan.c | 6 +----- drivers/tty/hvc/hvsi.c | 7 +------ drivers/tty/nozomi.c | 6 +----- drivers/tty/serial/ifx6x60.c | 33 ++------------------------------- drivers/tty/tty_port.c | 16 ++++++++++++++++ drivers/usb/class/cdc-acm.c | 7 +------ drivers/usb/serial/digi_acceleport.c | 17 +++-------------- drivers/usb/serial/io_edgeport.c | 28 +++++----------------------- drivers/usb/serial/keyspan_pda.c | 6 ++---- drivers/usb/serial/mos7720.c | 8 ++------ drivers/usb/serial/mos7840.c | 7 ++----- drivers/usb/serial/ti_usb_3410_5052.c | 7 ++----- drivers/usb/serial/usb-serial.c | 10 +--------- include/linux/tty.h | 1 + 22 files changed, 51 insertions(+), 176 deletions(-) (limited to 'include') diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index f1b38571f94..cc206eda245 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -248,7 +248,6 @@ static irqreturn_t line_write_interrupt(int irq, void *data) { struct chan *chan = data; struct line *line = chan->line; - struct tty_struct *tty; int err; /* @@ -267,12 +266,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data) } spin_unlock(&line->lock); - tty = tty_port_tty_get(&line->port); - if (tty == NULL) - return IRQ_NONE; - - tty_wakeup(tty); - tty_kref_put(tty); + tty_port_tty_wakeup(&line->port); return IRQ_HANDLED; } diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 89562a845f6..ac6f72b455d 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -569,7 +569,6 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) { struct capidev *cdev = ap->private; #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - struct tty_struct *tty; struct capiminor *mp; u16 datahandle; struct capincci *np; @@ -627,11 +626,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 2)); kfree_skb(skb); capiminor_del_ack(mp, datahandle); - tty = tty_port_tty_get(&mp->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&mp->port); handle_minor_send(mp); } else { diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index e2b539675b6..600c79b030c 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -487,12 +487,8 @@ static const struct tty_operations if_ops = { static void if_wake(unsigned long data) { struct cardstate *cs = (struct cardstate *)data; - struct tty_struct *tty = tty_port_tty_get(&cs->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&cs->port); } /*** interface to common ***/ diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index e2dd3249b6b..a7714b4f29a 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -1925,7 +1925,6 @@ static void hso_std_serial_write_bulk_callback(struct urb *urb) { struct hso_serial *serial = urb->context; int status = urb->status; - struct tty_struct *tty; /* sanity check */ if (!serial) { @@ -1941,11 +1940,7 @@ static void hso_std_serial_write_bulk_callback(struct urb *urb) return; } hso_put_activity(serial->parent); - tty = tty_port_tty_get(&serial->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&serial->port); hso_kick_transmit(serial); D1(" "); @@ -2008,12 +2003,8 @@ static void ctrl_callback(struct urb *urb) put_rxbuf_data_and_resubmit_ctrl_urb(serial); spin_unlock(&serial->serial_lock); } else { - struct tty_struct *tty = tty_port_tty_get(&serial->port); hso_put_activity(serial->parent); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&serial->port); /* response to a write command */ hso_kick_transmit(serial); } diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 14b4cb8abcc..7ed7a598781 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -107,7 +107,6 @@ sclp_tty_write_room (struct tty_struct *tty) static void sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) { - struct tty_struct *tty; unsigned long flags; void *page; @@ -125,12 +124,8 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) struct sclp_buffer, list); spin_unlock_irqrestore(&sclp_tty_lock, flags); } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); - /* check if the tty needs a wake up call */ - tty = tty_port_tty_get(&sclp_port); - if (tty != NULL) { - tty_wakeup(tty); - tty_kref_put(tty); - } + + tty_port_tty_wakeup(&sclp_port); } static inline void diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 6c92f62623b..5aaaa2ec8df 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -114,7 +114,6 @@ static struct sclp_register sclp_vt220_register = { static void sclp_vt220_process_queue(struct sclp_vt220_request *request) { - struct tty_struct *tty; unsigned long flags; void *page; @@ -139,12 +138,7 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request) } while (__sclp_vt220_emit(request)); if (request == NULL && sclp_vt220_flush_later) sclp_vt220_emit_current(); - /* Check if the tty needs a wake up call */ - tty = tty_port_tty_get(&sclp_vt220_port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&sclp_vt220_port); } #define SCLP_BUFFER_MAX_RETRY 1 diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index 5a6fb44f38a..5c64e3a35b2 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -744,7 +744,6 @@ static void fwtty_tx_complete(struct fw_card *card, int rcode, struct fwtty_transaction *txn) { struct fwtty_port *port = txn->port; - struct tty_struct *tty; int len; fwtty_dbg(port, "rcode: %d", rcode); @@ -769,13 +768,8 @@ static void fwtty_tx_complete(struct fw_card *card, int rcode, port->stats.dropped += txn->dma_pended.len; } - if (len < WAKEUP_CHARS) { - tty = tty_port_tty_get(&port->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } - } + if (len < WAKEUP_CHARS) + tty_port_tty_wakeup(&port->port); } static int fwtty_tx(struct fwtty_port *port, bool drain) diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c index b1bb1a6abe8..8a6e5ea476e 100644 --- a/drivers/staging/serqt_usb2/serqt_usb2.c +++ b/drivers/staging/serqt_usb2/serqt_usb2.c @@ -264,7 +264,6 @@ static void ProcessRxChar(struct usb_serial_port *port, unsigned char data) static void qt_write_bulk_callback(struct urb *urb) { - struct tty_struct *tty; int status; struct quatech_port *quatech_port; @@ -278,11 +277,7 @@ static void qt_write_bulk_callback(struct urb *urb) quatech_port = urb->context; - tty = tty_port_tty_get(&quatech_port->port->port); - - if (tty) - tty_wakeup(tty); - tty_kref_put(tty); + tty_port_tty_wakeup(&quatech_port->port->port); } static void qt_interrupt_callback(struct urb *urb) diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index ed92622b894..6d0c27cd03d 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -472,13 +472,9 @@ static void ehv_bc_tx_dequeue(struct ehv_bc_data *bc) static irqreturn_t ehv_bc_tty_tx_isr(int irq, void *data) { struct ehv_bc_data *bc = data; - struct tty_struct *ttys = tty_port_tty_get(&bc->port); ehv_bc_tx_dequeue(bc); - if (ttys) { - tty_wakeup(ttys); - tty_kref_put(ttys); - } + tty_port_tty_wakeup(&bc->port); return IRQ_HANDLED; } diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index ef95a154854..41901997c0d 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -861,7 +861,6 @@ static void hvsi_write_worker(struct work_struct *work) { struct hvsi_struct *hp = container_of(work, struct hvsi_struct, writer.work); - struct tty_struct *tty; unsigned long flags; #ifdef DEBUG static long start_j = 0; @@ -895,11 +894,7 @@ static void hvsi_write_worker(struct work_struct *work) start_j = 0; #endif /* DEBUG */ wake_up_all(&hp->emptyq); - tty = tty_port_tty_get(&hp->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&hp->port); } out: diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 2dff1979615..2e5bbdc09e1 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -791,7 +791,6 @@ static int send_data(enum port_type index, struct nozomi *dc) const u8 toggle = port->toggle_ul; void __iomem *addr = port->ul_addr[toggle]; const u32 ul_size = port->ul_size[toggle]; - struct tty_struct *tty = tty_port_tty_get(&port->port); /* Get data from tty and place in buf for now */ size = kfifo_out(&port->fifo_ul, dc->send_buf, @@ -799,7 +798,6 @@ static int send_data(enum port_type index, struct nozomi *dc) if (size == 0) { DBG4("No more data to send, disable link:"); - tty_kref_put(tty); return 0; } @@ -809,10 +807,8 @@ static int send_data(enum port_type index, struct nozomi *dc) write_mem32(addr, (u32 *) &size, 4); write_mem32(addr + 4, (u32 *) dc->send_buf, size); - if (tty) - tty_wakeup(tty); + tty_port_tty_wakeup(&port->port); - tty_kref_put(tty); return 1; } diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 68d7ce997ed..d723d4193b9 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -442,25 +442,6 @@ static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count, txbuffer[1] |= (more << IFX_SPI_MORE_BIT) & IFX_SPI_MORE_MASK; } -/** - * ifx_spi_wakeup_serial - SPI space made - * @port_data: our SPI device - * - * We have emptied the FIFO enough that we want to get more data - * queued into it. Poke the line discipline via tty_wakeup so that - * it will feed us more bits - */ -static void ifx_spi_wakeup_serial(struct ifx_spi_device *ifx_dev) -{ - struct tty_struct *tty; - - tty = tty_port_tty_get(&ifx_dev->tty_port); - if (!tty) - return; - tty_wakeup(tty); - tty_kref_put(tty); -} - /** * ifx_spi_prepare_tx_buffer - prepare transmit frame * @ifx_dev: our SPI device @@ -506,7 +487,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) tx_count += temp_count; if (temp_count == queue_length) /* poke port to get more data */ - ifx_spi_wakeup_serial(ifx_dev); + tty_port_tty_wakeup(&ifx_dev->tty_port); else /* more data in port, use next SPI message */ ifx_dev->spi_more = 1; } @@ -683,8 +664,6 @@ static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev, static void ifx_spi_complete(void *ctx) { struct ifx_spi_device *ifx_dev = ctx; - struct tty_struct *tty; - struct tty_ldisc *ldisc = NULL; int length; int actual_length; unsigned char more; @@ -762,15 +741,7 @@ complete_exit: */ ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_DATA_PENDING); - tty = tty_port_tty_get(&ifx_dev->tty_port); - if (tty) { - ldisc = tty_ldisc_ref(tty); - if (ldisc) { - ldisc->ops->write_wakeup(tty); - tty_ldisc_deref(ldisc); - } - tty_kref_put(tty); - } + tty_port_tty_wakeup(&ifx_dev->tty_port); } } } diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index b7ff59d3db8..8bb757c62ee 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -232,6 +232,22 @@ void tty_port_hangup(struct tty_port *port) } EXPORT_SYMBOL(tty_port_hangup); +/** + * tty_port_tty_wakeup - helper to wake up a tty + * + * @port: tty port + */ +void tty_port_tty_wakeup(struct tty_port *port) +{ + struct tty_struct *tty = tty_port_tty_get(port); + + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } +} +EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); + /** * tty_port_carrier_raised - carrier raised check * @port: tty port diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 8ac25adf31b..755766e4b75 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -475,15 +475,10 @@ static void acm_write_bulk(struct urb *urb) static void acm_softint(struct work_struct *work) { struct acm *acm = container_of(work, struct acm, work); - struct tty_struct *tty; dev_vdbg(&acm->data->dev, "%s\n", __func__); - tty = tty_port_tty_get(&acm->port); - if (!tty) - return; - tty_wakeup(tty); - tty_kref_put(tty); + tty_port_tty_wakeup(&acm->port); } /* diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index ebe45fa0ed5..31191581060 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -210,7 +210,6 @@ struct digi_port { /* Local Function Declarations */ -static void digi_wakeup_write(struct usb_serial_port *port); static void digi_wakeup_write_lock(struct work_struct *work); static int digi_write_oob_command(struct usb_serial_port *port, unsigned char *buf, int count, int interruptible); @@ -374,20 +373,10 @@ static void digi_wakeup_write_lock(struct work_struct *work) unsigned long flags; spin_lock_irqsave(&priv->dp_port_lock, flags); - digi_wakeup_write(port); + tty_port_tty_wakeup(&port->port); spin_unlock_irqrestore(&priv->dp_port_lock, flags); } -static void digi_wakeup_write(struct usb_serial_port *port) -{ - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } -} - - /* * Digi Write OOB Command * @@ -1044,7 +1033,7 @@ static void digi_write_bulk_callback(struct urb *urb) } } /* wake up processes sleeping on writes immediately */ - digi_wakeup_write(port); + tty_port_tty_wakeup(&port->port); /* also queue up a wakeup at scheduler time, in case we */ /* lost the race in write_chan(). */ schedule_work(&priv->dp_wakeup_work); @@ -1522,7 +1511,7 @@ static int digi_read_oob_callback(struct urb *urb) /* port must be open to use tty struct */ if (rts) { tty->hw_stopped = 0; - digi_wakeup_write(port); + tty_port_tty_wakeup(&port->port); } } else { priv->dp_modem_signals &= ~TIOCM_CTS; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index b00e5cbf741..44e5208f7c6 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -565,7 +565,6 @@ static void edge_interrupt_callback(struct urb *urb) struct device *dev; struct edgeport_port *edge_port; struct usb_serial_port *port; - struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int length = urb->actual_length; int bytes_avail; @@ -644,12 +643,7 @@ static void edge_interrupt_callback(struct urb *urb) /* tell the tty driver that something has changed */ - tty = tty_port_tty_get( - &edge_port->port->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&edge_port->port->port); /* Since we have more credit, check if more data can be sent */ send_more_port_data(edge_serial, @@ -738,7 +732,6 @@ static void edge_bulk_in_callback(struct urb *urb) static void edge_bulk_out_data_callback(struct urb *urb) { struct edgeport_port *edge_port = urb->context; - struct tty_struct *tty; int status = urb->status; if (status) { @@ -747,14 +740,8 @@ static void edge_bulk_out_data_callback(struct urb *urb) __func__, status); } - tty = tty_port_tty_get(&edge_port->port->port); - - if (tty && edge_port->open) { - /* let the tty driver wakeup if it has a special - write_wakeup function */ - tty_wakeup(tty); - } - tty_kref_put(tty); + if (edge_port->open) + tty_port_tty_wakeup(&edge_port->port->port); /* Release the Write URB */ edge_port->write_in_progress = false; @@ -773,7 +760,6 @@ static void edge_bulk_out_data_callback(struct urb *urb) static void edge_bulk_out_cmd_callback(struct urb *urb) { struct edgeport_port *edge_port = urb->context; - struct tty_struct *tty; int status = urb->status; atomic_dec(&CmdUrbs); @@ -794,13 +780,9 @@ static void edge_bulk_out_cmd_callback(struct urb *urb) return; } - /* Get pointer to tty */ - tty = tty_port_tty_get(&edge_port->port->port); - /* tell the tty driver that something has changed */ - if (tty && edge_port->open) - tty_wakeup(tty); - tty_kref_put(tty); + if (edge_port->open) + tty_port_tty_wakeup(&edge_port->port->port); /* we have completed the command */ edge_port->commandPending = false; diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 3b17d5d13dc..2230223978c 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -104,10 +104,8 @@ static void keyspan_pda_wakeup_write(struct work_struct *work) struct keyspan_pda_private *priv = container_of(work, struct keyspan_pda_private, wakeup_work); struct usb_serial_port *port = priv->port; - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty) - tty_wakeup(tty); - tty_kref_put(tty); + + tty_port_tty_wakeup(&port->port); } static void keyspan_pda_request_unthrottle(struct work_struct *work) diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index e0ebec3b5d6..e956eae198f 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -932,7 +932,6 @@ static void mos7720_bulk_in_callback(struct urb *urb) static void mos7720_bulk_out_data_callback(struct urb *urb) { struct moschip_port *mos7720_port; - struct tty_struct *tty; int status = urb->status; if (status) { @@ -946,11 +945,8 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) return ; } - tty = tty_port_tty_get(&mos7720_port->port->port); - - if (tty && mos7720_port->open) - tty_wakeup(tty); - tty_kref_put(tty); + if (mos7720_port->open) + tty_port_tty_wakeup(&mos7720_port->port->port); } /* diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 809fb329eca..08284d28e84 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -814,7 +814,6 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) { struct moschip_port *mos7840_port; struct usb_serial_port *port; - struct tty_struct *tty; int status = urb->status; int i; @@ -837,10 +836,8 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) if (mos7840_port_paranoia_check(port, __func__)) return; - tty = tty_port_tty_get(&port->port); - if (tty && mos7840_port->open) - tty_wakeup(tty); - tty_kref_put(tty); + if (mos7840_port->open) + tty_port_tty_wakeup(&port->port); } diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 39cb9b807c3..437f2d579cd 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -1227,7 +1227,6 @@ static void ti_send(struct ti_port *tport) { int count, result; struct usb_serial_port *port = tport->tp_port; - struct tty_struct *tty = tty_port_tty_get(&port->port); /* FIXME */ unsigned long flags; spin_lock_irqsave(&tport->tp_lock, flags); @@ -1268,14 +1267,12 @@ static void ti_send(struct ti_port *tport) } /* more room in the buffer for new writes, wakeup */ - if (tty) - tty_wakeup(tty); - tty_kref_put(tty); + tty_port_tty_wakeup(&port->port); + wake_up_interruptible(&tport->tp_write_wait); return; unlock: spin_unlock_irqrestore(&tport->tp_lock, flags); - tty_kref_put(tty); return; } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a19ed74d770..2df84845baf 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -541,16 +541,8 @@ static void usb_serial_port_work(struct work_struct *work) { struct usb_serial_port *port = container_of(work, struct usb_serial_port, work); - struct tty_struct *tty; - tty = tty_port_tty_get(&port->port); - if (!tty) - return; - - dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number); - - tty_wakeup(tty); - tty_kref_put(tty); + tty_port_tty_wakeup(&port->port); } static void kill_traffic(struct usb_serial_port *port) diff --git a/include/linux/tty.h b/include/linux/tty.h index 63b62865c8e..b6e890a87eb 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -534,6 +534,7 @@ extern int tty_port_carrier_raised(struct tty_port *port); extern void tty_port_raise_dtr_rts(struct tty_port *port); extern void tty_port_lower_dtr_rts(struct tty_port *port); extern void tty_port_hangup(struct tty_port *port); +extern void tty_port_tty_wakeup(struct tty_port *port); extern int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty, struct file *filp); extern int tty_port_close_start(struct tty_port *port, -- cgit v1.2.3-70-g09d2 From aa27a094e2c2e0cc59914e56113b860f524f4479 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 7 Mar 2013 13:12:30 +0100 Subject: TTY: add tty_port_tty_hangup helper It allows for cleaning up on a considerable amount of places. They did port_get, hangup, kref_put. Now the only thing needed is to call tty_port_tty_hangup which does exactly that. And they can also decide whether to consider CLOCAL or completely ignore that. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- arch/um/drivers/chan_kern.c | 6 +----- drivers/mmc/card/sdio_uart.c | 13 ++--------- drivers/net/usb/hso.c | 7 +----- drivers/tty/cyclades.c | 10 ++------- drivers/tty/moxa.c | 19 ++++++---------- drivers/tty/n_gsm.c | 6 +----- drivers/tty/nozomi.c | 9 +++----- drivers/tty/rocket.c | 7 +----- drivers/tty/serial/ifx6x60.c | 21 ++---------------- drivers/tty/tty_port.c | 17 +++++++++++++++ drivers/usb/class/cdc-acm.c | 24 ++++++--------------- drivers/usb/serial/keyspan.c | 43 +++++++++---------------------------- drivers/usb/serial/option.c | 9 ++------ drivers/usb/serial/sierra.c | 8 ++----- include/linux/tty.h | 1 + net/irda/ircomm/ircomm_tty_attach.c | 6 +----- 16 files changed, 58 insertions(+), 148 deletions(-) (limited to 'include') diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c index 15c553c239a..bf42825ba54 100644 --- a/arch/um/drivers/chan_kern.c +++ b/arch/um/drivers/chan_kern.c @@ -568,11 +568,7 @@ void chan_interrupt(struct line *line, int irq) reactivate_fd(chan->fd, irq); if (err == -EIO) { if (chan->primary) { - struct tty_struct *tty = tty_port_tty_get(&line->port); - if (tty != NULL) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&line->port, false); if (line->chan_out != chan) close_one_chan(line->chan_out, 1); } diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index c931dfe6a59..f093cea0d06 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -134,7 +134,6 @@ static void sdio_uart_port_put(struct sdio_uart_port *port) static void sdio_uart_port_remove(struct sdio_uart_port *port) { struct sdio_func *func; - struct tty_struct *tty; BUG_ON(sdio_uart_table[port->index] != port); @@ -155,12 +154,8 @@ static void sdio_uart_port_remove(struct sdio_uart_port *port) sdio_claim_host(func); port->func = NULL; mutex_unlock(&port->func_lock); - tty = tty_port_tty_get(&port->port); /* tty_hangup is async so is this safe as is ?? */ - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&port->port, false); mutex_unlock(&port->port.mutex); sdio_release_irq(func); sdio_disable_func(func); @@ -492,11 +487,7 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port) wake_up_interruptible(&port->port.open_wait); else { /* DCD drop - hang up if tty attached */ - tty = tty_port_tty_get(&port->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&port->port, false); } } if (status & UART_MSR_DCTS) { diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index a7714b4f29a..cba1d46e672 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -3124,18 +3124,13 @@ static void hso_serial_ref_free(struct kref *ref) static void hso_free_interface(struct usb_interface *interface) { struct hso_serial *hso_dev; - struct tty_struct *tty; int i; for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { if (serial_table[i] && (serial_table[i]->interface == interface)) { hso_dev = dev2ser(serial_table[i]); - tty = tty_port_tty_get(&hso_dev->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&hso_dev->port, false); mutex_lock(&hso_dev->parent->mutex); hso_dev->parent->usb_gone = 1; mutex_unlock(&hso_dev->parent->mutex); diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 345bd0e0884..33f83fee9fa 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -1124,14 +1124,8 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) readl(&info->u.cyz.ch_ctrl->rs_status); if (dcd & C_RS_DCD) wake_up_interruptible(&info->port.open_wait); - else { - struct tty_struct *tty; - tty = tty_port_tty_get(&info->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } + else + tty_port_tty_hangup(&info->port, false); } break; case C_CM_MCTS: diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index adeac255e52..1deaca4674e 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -913,16 +913,12 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) /* pci hot-un-plug support */ for (a = 0; a < brd->numPorts; a++) - if (brd->ports[a].port.flags & ASYNC_INITIALIZED) { - struct tty_struct *tty = tty_port_tty_get( - &brd->ports[a].port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } + if (brd->ports[a].port.flags & ASYNC_INITIALIZED) + tty_port_tty_hangup(&brd->ports[a].port, false); + for (a = 0; a < MAX_PORTS_PER_BOARD; a++) tty_port_destroy(&brd->ports[a].port); + while (1) { opened = 0; for (a = 0; a < brd->numPorts; a++) @@ -1365,7 +1361,6 @@ static void moxa_hangup(struct tty_struct *tty) static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) { - struct tty_struct *tty; unsigned long flags; dcd = !!dcd; @@ -1373,10 +1368,8 @@ static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) if (dcd != p->DCDState) { p->DCDState = dcd; spin_unlock_irqrestore(&p->port.lock, flags); - tty = tty_port_tty_get(&p->port); - if (tty && !C_CLOCAL(tty) && !dcd) - tty_hangup(tty); - tty_kref_put(tty); + if (!dcd) + tty_port_tty_hangup(&p->port, true); } else spin_unlock_irqrestore(&p->port.lock, flags); diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 4a43ef5d796..74d9a0258d7 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1418,11 +1418,7 @@ static void gsm_dlci_close(struct gsm_dlci *dlci) pr_debug("DLCI %d goes closed.\n", dlci->addr); dlci->state = DLCI_CLOSED; if (dlci->addr != 0) { - struct tty_struct *tty = tty_port_tty_get(&dlci->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&dlci->port, false); kfifo_reset(dlci->fifo); } else dlci->gsm->dead = 1; diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index 2e5bbdc09e1..d6080c3831e 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -1501,12 +1501,9 @@ static void tty_exit(struct nozomi *dc) DBG1(" "); - for (i = 0; i < MAX_PORT; ++i) { - struct tty_struct *tty = tty_port_tty_get(&dc->port[i].port); - if (tty && list_empty(&tty->hangup_work.entry)) - tty_hangup(tty); - tty_kref_put(tty); - } + for (i = 0; i < MAX_PORT; ++i) + tty_port_tty_hangup(&dc->port[i].port, false); + /* Racy below - surely should wait for scheduled work to be done or complete off a hangup method ? */ while (dc->open_ttys) diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 1d270034bfc..bbffd7a431e 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -521,15 +521,10 @@ static void rp_handle_port(struct r_port *info) (ChanStatus & CD_ACT) ? "on" : "off"); #endif if (!(ChanStatus & CD_ACT) && info->cd_status) { - struct tty_struct *tty; #ifdef ROCKET_DEBUG_HANGUP printk(KERN_INFO "CD drop, calling hangup.\n"); #endif - tty = tty_port_tty_get(&info->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&info->port, false); } info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0; wake_up_interruptible(&info->port.open_wait); diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index d723d4193b9..2c77fed31a7 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -269,23 +269,6 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev) mrdy_set_high(ifx_dev); } -/** - * ifx_spi_hangup - hang up an IFX device - * @ifx_dev: our SPI device - * - * Hang up the tty attached to the IFX device if one is currently - * open. If not take no action - */ -static void ifx_spi_ttyhangup(struct ifx_spi_device *ifx_dev) -{ - struct tty_port *pport = &ifx_dev->tty_port; - struct tty_struct *tty = tty_port_tty_get(pport); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } -} - /** * ifx_spi_timeout - SPI timeout * @arg: our SPI device @@ -298,7 +281,7 @@ static void ifx_spi_timeout(unsigned long arg) struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)arg; dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***"); - ifx_spi_ttyhangup(ifx_dev); + tty_port_tty_hangup(&ifx_dev->tty_port, false); mrdy_set_low(ifx_dev); clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); } @@ -933,7 +916,7 @@ static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev) set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); if (!solreset) { /* unsolicited reset */ - ifx_spi_ttyhangup(ifx_dev); + tty_port_tty_hangup(&ifx_dev->tty_port, false); } } else { /* exited reset */ diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 8bb757c62ee..7f38eeaafac 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -232,6 +232,23 @@ void tty_port_hangup(struct tty_port *port) } EXPORT_SYMBOL(tty_port_hangup); +/** + * tty_port_tty_hangup - helper to hang up a tty + * + * @port: tty port + * @check_clocal: hang only ttys with CLOCAL unset? + */ +void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) +{ + struct tty_struct *tty = tty_port_tty_get(port); + + if (tty && (!check_clocal || !C_CLOCAL(tty))) { + tty_hangup(tty); + tty_kref_put(tty); + } +} +EXPORT_SYMBOL_GPL(tty_port_tty_hangup); + /** * tty_port_tty_wakeup - helper to wake up a tty * diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 755766e4b75..27a18743275 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -292,7 +292,6 @@ static void acm_ctrl_irq(struct urb *urb) { struct acm *acm = urb->context; struct usb_cdc_notification *dr = urb->transfer_buffer; - struct tty_struct *tty; unsigned char *data; int newctrl; int retval; @@ -327,17 +326,12 @@ static void acm_ctrl_irq(struct urb *urb) break; case USB_CDC_NOTIFY_SERIAL_STATE: - tty = tty_port_tty_get(&acm->port); newctrl = get_unaligned_le16(data); - if (tty) { - if (!acm->clocal && - (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { - dev_dbg(&acm->control->dev, - "%s - calling hangup\n", __func__); - tty_hangup(tty); - } - tty_kref_put(tty); + if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { + dev_dbg(&acm->control->dev, "%s - calling hangup\n", + __func__); + tty_port_tty_hangup(&acm->port, false); } acm->ctrlin = newctrl; @@ -1498,15 +1492,9 @@ err_out: static int acm_reset_resume(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); - struct tty_struct *tty; - if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) { - tty = tty_port_tty_get(&acm->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } + if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) + tty_port_tty_hangup(&acm->port, false); return acm_resume(intf); } diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 1fd1935c831..b011478d2e5 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -378,7 +378,6 @@ static void usa26_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; - struct tty_struct *tty; int old_dcd_state, err; int status = urb->status; @@ -421,12 +420,8 @@ static void usa26_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (old_dcd_state != p_priv->dcd_state) { - tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state != p_priv->dcd_state) + tty_port_tty_hangup(&port->port, true); /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -510,7 +505,6 @@ static void usa28_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; - struct tty_struct *tty; int old_dcd_state; int status = urb->status; @@ -551,12 +545,8 @@ static void usa28_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { - tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) + tty_port_tty_hangup(&port->port, true); /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -642,12 +632,8 @@ static void usa49_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) + tty_port_tty_hangup(&port->port, true); /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -851,7 +837,6 @@ static void usa90_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; - struct tty_struct *tty; int old_dcd_state, err; int status = urb->status; @@ -880,12 +865,8 @@ static void usa90_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { - tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) + tty_port_tty_hangup(&port->port, true); /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); @@ -953,12 +934,8 @@ static void usa67_instat_callback(struct urb *urb) p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); - if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { - struct tty_struct *tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) + tty_port_tty_hangup(&port->port, true); /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index f7d339d8187..602d1f389a3 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1532,13 +1532,8 @@ static void option_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (old_dcd_state && !portdata->dcd_state) { - struct tty_struct *tty = - tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) - tty_hangup(tty); - tty_kref_put(tty); - } + if (old_dcd_state && !portdata->dcd_state) + tty_port_tty_hangup(&port->port, true); } else { dev_dbg(dev, "%s: type %x req %x\n", __func__, req_pkt->bRequestType, req_pkt->bRequest); diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index c13f6e74774..d66148a17fe 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -628,7 +628,6 @@ static void sierra_instat_callback(struct urb *urb) unsigned char signals = *((unsigned char *) urb->transfer_buffer + sizeof(struct usb_ctrlrequest)); - struct tty_struct *tty; dev_dbg(&port->dev, "%s: signal x%x\n", __func__, signals); @@ -639,11 +638,8 @@ static void sierra_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty) && - old_dcd_state && !portdata->dcd_state) - tty_hangup(tty); - tty_kref_put(tty); + if (old_dcd_state && !portdata->dcd_state) + tty_port_tty_hangup(&port->port, true); } else { dev_dbg(&port->dev, "%s: type %x req %x\n", __func__, req_pkt->bRequestType, diff --git a/include/linux/tty.h b/include/linux/tty.h index b6e890a87eb..d3548f87196 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -534,6 +534,7 @@ extern int tty_port_carrier_raised(struct tty_port *port); extern void tty_port_raise_dtr_rts(struct tty_port *port); extern void tty_port_lower_dtr_rts(struct tty_port *port); extern void tty_port_hangup(struct tty_port *port); +extern void tty_port_tty_hangup(struct tty_port *port, bool check_clocal); extern void tty_port_tty_wakeup(struct tty_port *port); extern int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty, struct file *filp); diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index edab393e0c8..a2a508f5f26 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -997,12 +997,8 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, self->settings.dce = IRCOMM_DELTA_CD; ircomm_tty_check_modem_status(self); } else { - struct tty_struct *tty = tty_port_tty_get(&self->port); IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ ); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&self->port, false); } break; default: -- cgit v1.2.3-70-g09d2 From 21622939fc452c7fb739464b8e49368c3ceaa0ee Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:21 -0400 Subject: tty: Add diagnostic for halted line discipline Flip buffer work must not be scheduled by the line discipline after the line discipline has been halted; issue warning. Note: drivers can still schedule flip buffer work. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 8 ++++++++ drivers/tty/tty_ldisc.c | 7 ++++++- include/linux/tty.h | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 68865d9af8a..16793eccc6a 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -153,6 +153,12 @@ static void n_tty_set_room(struct tty_struct *tty) if (left && !old_left) { WARN_RATELIMIT(tty->port->itty == NULL, "scheduling with invalid itty\n"); + /* see if ldisc has been killed - if so, this means that + * even though the ldisc has been halted and ->buf.work + * cancelled, ->buf.work is about to be rescheduled + */ + WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags), + "scheduling buffer work for halted ldisc\n"); schedule_work(&tty->port->buf.work); } } @@ -1624,6 +1630,8 @@ static int n_tty_open(struct tty_struct *tty) goto err_free_bufs; tty->disc_data = ldata; + /* indicate buffer work may resume */ + clear_bit(TTY_LDISC_HALTED, &tty->flags); reset_buffer_flags(tty); tty_unthrottle(tty); ldata->column = 0; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index d794087c327..c641321b940 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -375,6 +375,7 @@ static inline void tty_ldisc_put(struct tty_ldisc *ld) void tty_ldisc_enable(struct tty_struct *tty) { + clear_bit(TTY_LDISC_HALTED, &tty->flags); set_bit(TTY_LDISC, &tty->flags); clear_bit(TTY_LDISC_CHANGING, &tty->flags); wake_up(&tty_ldisc_wait); @@ -513,8 +514,11 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) static int tty_ldisc_halt(struct tty_struct *tty) { + int scheduled; clear_bit(TTY_LDISC, &tty->flags); - return cancel_work_sync(&tty->port->buf.work); + scheduled = cancel_work_sync(&tty->port->buf.work); + set_bit(TTY_LDISC_HALTED, &tty->flags); + return scheduled; } /** @@ -820,6 +824,7 @@ void tty_ldisc_hangup(struct tty_struct *tty) clear_bit(TTY_LDISC, &tty->flags); tty_unlock(tty); cancel_work_sync(&tty->port->buf.work); + set_bit(TTY_LDISC_HALTED, &tty->flags); mutex_unlock(&tty->ldisc_mutex); retry: tty_lock(tty); diff --git a/include/linux/tty.h b/include/linux/tty.h index d3548f87196..66ae020e8a9 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -315,6 +315,7 @@ struct tty_file_private { #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ #define TTY_HUPPED 18 /* Post driver->hangup() */ #define TTY_HUPPING 21 /* ->hangup() in progress */ +#define TTY_LDISC_HALTED 22 /* Line discipline is halted */ #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty)) -- cgit v1.2.3-70-g09d2 From d912156605b0eb3b3070dc7eabc43db6379aa43b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 11 Mar 2013 16:44:33 -0400 Subject: tty: Don't reenable already enabled ldisc tty_ldisc_hangup() guarantees the ldisc is enabled (or that there is no ldisc). Since __tty_hangup() was the only user, re-define tty_ldisc_enable() in file-scope. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 1 - drivers/tty/tty_ldisc.c | 2 +- include/linux/tty.h | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d3ddb31e363..e6ee0f459a2 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -693,7 +693,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) */ set_bit(TTY_HUPPED, &tty->flags); clear_bit(TTY_HUPPING, &tty->flags); - tty_ldisc_enable(tty); tty_unlock(tty); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 37671fcc7e4..9c727da59fa 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -373,7 +373,7 @@ static inline void tty_ldisc_put(struct tty_ldisc *ld) * Clearing directly is allowed. */ -void tty_ldisc_enable(struct tty_struct *tty) +static void tty_ldisc_enable(struct tty_struct *tty) { clear_bit(TTY_LDISC_HALTED, &tty->flags); set_bit(TTY_LDISC, &tty->flags); diff --git a/include/linux/tty.h b/include/linux/tty.h index 66ae020e8a9..367a9dfc4ea 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -561,8 +561,6 @@ extern void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty); extern void tty_ldisc_init(struct tty_struct *tty); extern void tty_ldisc_deinit(struct tty_struct *tty); extern void tty_ldisc_begin(void); -/* This last one is just for the tty layer internals and shouldn't be used elsewhere */ -extern void tty_ldisc_enable(struct tty_struct *tty); /* n_tty.c */ -- cgit v1.2.3-70-g09d2 From c6c1d50b51e76b57fbf0651d38a7ae0c3fb9d5cc Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 22 Jan 2013 12:27:55 -0300 Subject: [media] media: Add 64--32 bit compat ioctl handler Provide an ioctl handler for 32-bit binaries on 64-bit systems. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Tested-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-devnode.c | 31 ++++++++++++++++++++++++++++--- include/media/media-devnode.h | 1 + 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c index 023b2a1cbb9..fb0f0469fad 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/media-devnode.c @@ -116,19 +116,41 @@ static unsigned int media_poll(struct file *filp, return mdev->fops->poll(filp, poll); } -static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +static long +__media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg, + long (*ioctl_func)(struct file *filp, unsigned int cmd, + unsigned long arg)) { struct media_devnode *mdev = media_devnode_data(filp); - if (!mdev->fops->ioctl) + if (!ioctl_func) return -ENOTTY; if (!media_devnode_is_registered(mdev)) return -EIO; - return mdev->fops->ioctl(filp, cmd, arg); + return ioctl_func(filp, cmd, arg); +} + +static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct media_devnode *mdev = media_devnode_data(filp); + + return __media_ioctl(filp, cmd, arg, mdev->fops->ioctl); } +#ifdef CONFIG_COMPAT + +static long media_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct media_devnode *mdev = media_devnode_data(filp); + + return __media_ioctl(filp, cmd, arg, mdev->fops->compat_ioctl); +} + +#endif /* CONFIG_COMPAT */ + /* Override for the open function */ static int media_open(struct inode *inode, struct file *filp) { @@ -188,6 +210,9 @@ static const struct file_operations media_devnode_fops = { .write = media_write, .open = media_open, .unlocked_ioctl = media_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = media_compat_ioctl, +#endif /* CONFIG_COMPAT */ .release = media_release, .poll = media_poll, .llseek = no_llseek, diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index f6caafc874c..3446af279fc 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -46,6 +46,7 @@ struct media_file_operations { ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*ioctl) (struct file *, unsigned int, unsigned long); + long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*open) (struct file *); int (*release) (struct file *); }; -- cgit v1.2.3-70-g09d2 From 6f8da5df8c451103e0043f73a00c90676da6be9e Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Tue, 12 Mar 2013 18:08:09 -0400 Subject: power_supply: Add support for tps65090-charger This patch adds support for the tps65090 charger driver. This driver is responsible for controlling the charger aspect of the tps65090 mfd. Currently, this mainly consists of turning on and off the charger, but some features of the charger can be supported through this driver including: - Enable Auto Recharge based on Battery voltage - Fast Charge Safety Timer - Maximum battery discharge current - Maximum battery adapter current - Enable External Charge - Disable charging termination based on low charger current (supported) Once the driver is accepted, later patches can add support for the features above which are not yet supported. Based on work by: Syed Rafiuddin Laxman Dewangan Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 7 + drivers/power/Makefile | 1 + drivers/power/tps65090-charger.c | 315 +++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tps65090.h | 5 + 4 files changed, 328 insertions(+) create mode 100644 drivers/power/tps65090-charger.c (limited to 'include') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 07e1a8f8d03..339f802b91c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -340,6 +340,13 @@ config CHARGER_SMB347 Say Y to include support for Summit Microelectronics SMB347 Battery Charger. +config CHARGER_TPS65090 + tristate "TPS65090 battery charger driver" + depends on MFD_TPS65090 + help + Say Y here to enable support for battery charging with TPS65090 + PMIC chips. + config AB8500_BM bool "AB8500 Battery Management Driver" depends on AB8500_CORE && AB8500_GPADC diff --git a/drivers/power/Makefile b/drivers/power/Makefile index eb520ea7497..653bf6ceff3 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -52,4 +52,5 @@ obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o +obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o obj-$(CONFIG_POWER_RESET) += reset/ diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c new file mode 100644 index 00000000000..0c66c6656b1 --- /dev/null +++ b/drivers/power/tps65090-charger.c @@ -0,0 +1,315 @@ +/* + * Battery charger driver for TI's tps65090 + * + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TPS65090_REG_INTR_STS 0x00 +#define TPS65090_REG_CG_CTRL0 0x04 +#define TPS65090_REG_CG_CTRL1 0x05 +#define TPS65090_REG_CG_CTRL2 0x06 +#define TPS65090_REG_CG_CTRL3 0x07 +#define TPS65090_REG_CG_CTRL4 0x08 +#define TPS65090_REG_CG_CTRL5 0x09 +#define TPS65090_REG_CG_STATUS1 0x0a +#define TPS65090_REG_CG_STATUS2 0x0b + +#define TPS65090_CHARGER_ENABLE BIT(0) +#define TPS65090_VACG BIT(1) +#define TPS65090_NOITERM BIT(5) + +struct tps65090_charger { + struct device *dev; + int ac_online; + int prev_ac_online; + int irq; + struct power_supply ac; + struct tps65090_platform_data *pdata; +}; + +static enum power_supply_property tps65090_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static int tps65090_low_chrg_current(struct tps65090_charger *charger) +{ + int ret; + + ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL5, + TPS65090_NOITERM); + if (ret < 0) { + dev_err(charger->dev, "%s(): error reading in register 0x%x\n", + __func__, TPS65090_REG_CG_CTRL5); + return ret; + } + return 0; +} + +static int tps65090_enable_charging(struct tps65090_charger *charger, + uint8_t enable) +{ + int ret; + uint8_t ctrl0 = 0; + + ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_CTRL0, + &ctrl0); + if (ret < 0) { + dev_err(charger->dev, "%s(): error reading in register 0x%x\n", + __func__, TPS65090_REG_CG_CTRL0); + return ret; + } + + ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL0, + (ctrl0 | TPS65090_CHARGER_ENABLE)); + if (ret < 0) { + dev_err(charger->dev, "%s(): error reading in register 0x%x\n", + __func__, TPS65090_REG_CG_CTRL0); + return ret; + } + return 0; +} + +static int tps65090_config_charger(struct tps65090_charger *charger) +{ + int ret; + + if (charger->pdata->enable_low_current_chrg) { + ret = tps65090_low_chrg_current(charger); + if (ret < 0) { + dev_err(charger->dev, + "error configuring low charge current\n"); + return ret; + } + } + + return 0; +} + +static int tps65090_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct tps65090_charger *charger = container_of(psy, + struct tps65090_charger, ac); + + if (psp == POWER_SUPPLY_PROP_ONLINE) { + val->intval = charger->ac_online; + charger->prev_ac_online = charger->ac_online; + return 0; + } + return -EINVAL; +} + +static irqreturn_t tps65090_charger_isr(int irq, void *dev_id) +{ + struct tps65090_charger *charger = dev_id; + int ret; + uint8_t status1 = 0; + uint8_t intrsts = 0; + + ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_STATUS1, + &status1); + if (ret < 0) { + dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n", + __func__, TPS65090_REG_CG_STATUS1); + return IRQ_HANDLED; + } + msleep(75); + ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_STS, + &intrsts); + if (ret < 0) { + dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n", + __func__, TPS65090_REG_INTR_STS); + return IRQ_HANDLED; + } + + if (intrsts & TPS65090_VACG) { + ret = tps65090_enable_charging(charger, 1); + if (ret < 0) + return IRQ_HANDLED; + charger->ac_online = 1; + } else { + charger->ac_online = 0; + } + + if (charger->prev_ac_online != charger->ac_online) + power_supply_changed(&charger->ac); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_OF) + +#include + +static struct tps65090_platform_data * + tps65090_parse_dt_charger_data(struct platform_device *pdev) +{ + struct tps65090_platform_data *pdata; + struct device_node *np = pdev->dev.parent->of_node; + unsigned int prop; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Memory alloc for tps65090_pdata failed\n"); + return NULL; + } + + prop = of_property_read_bool(np, "ti,enable-low-current-chrg"); + pdata->enable_low_current_chrg = prop; + + pdata->irq_base = -1; + + return pdata; + +} +#else +static struct tps65090_platform_data * + tps65090_parse_dt_charger_data(struct platform_device *pdev) +{ + return NULL; +} +#endif + +static int tps65090_charger_probe(struct platform_device *pdev) +{ + struct tps65090 *tps65090_mfd = dev_get_drvdata(pdev->dev.parent); + struct tps65090_charger *cdata; + struct tps65090_platform_data *pdata; + uint8_t status1 = 0; + int ret; + int irq; + + pdata = dev_get_platdata(pdev->dev.parent); + + if (!pdata && tps65090_mfd->dev->of_node) + pdata = tps65090_parse_dt_charger_data(pdev); + + if (!pdata) { + dev_err(&pdev->dev, "%s():no platform data available\n", + __func__); + return -ENODEV; + } + + cdata = devm_kzalloc(&pdev->dev, sizeof(*cdata), GFP_KERNEL); + if (!cdata) { + dev_err(&pdev->dev, "failed to allocate memory status\n"); + return -ENOMEM; + } + + dev_set_drvdata(&pdev->dev, cdata); + + cdata->dev = &pdev->dev; + cdata->pdata = pdata; + + cdata->ac.name = "tps65090-ac"; + cdata->ac.type = POWER_SUPPLY_TYPE_MAINS; + cdata->ac.get_property = tps65090_ac_get_property; + cdata->ac.properties = tps65090_ac_props; + cdata->ac.num_properties = ARRAY_SIZE(tps65090_ac_props); + cdata->ac.supplied_to = pdata->supplied_to; + cdata->ac.num_supplicants = pdata->num_supplicants; + + ret = power_supply_register(&pdev->dev, &cdata->ac); + if (ret) { + dev_err(&pdev->dev, "failed: power supply register\n"); + return ret; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_warn(&pdev->dev, "Unable to get charger irq = %d\n", irq); + ret = irq; + goto fail_unregister_supply; + } + + cdata->irq = irq; + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + tps65090_charger_isr, 0, "tps65090-charger", cdata); + if (ret) { + dev_err(cdata->dev, "Unable to register irq %d err %d\n", irq, + ret); + goto fail_free_irq; + } + + ret = tps65090_config_charger(cdata); + if (ret < 0) { + dev_err(&pdev->dev, "charger config failed, err %d\n", ret); + goto fail_free_irq; + } + + /* Check for charger presence */ + ret = tps65090_read(cdata->dev->parent, TPS65090_REG_CG_STATUS1, + &status1); + if (ret < 0) { + dev_err(cdata->dev, "%s(): Error in reading reg 0x%x", __func__, + TPS65090_REG_CG_STATUS1); + goto fail_free_irq; + } + + if (status1 != 0) { + ret = tps65090_enable_charging(cdata, 1); + if (ret < 0) { + dev_err(cdata->dev, "error enabling charger\n"); + goto fail_free_irq; + } + cdata->ac_online = 1; + power_supply_changed(&cdata->ac); + } + + return 0; + +fail_free_irq: + devm_free_irq(cdata->dev, irq, cdata); +fail_unregister_supply: + power_supply_unregister(&cdata->ac); + + return ret; +} + +static int tps65090_charger_remove(struct platform_device *pdev) +{ + struct tps65090_charger *cdata = dev_get_drvdata(&pdev->dev); + + devm_free_irq(cdata->dev, cdata->irq, cdata); + power_supply_unregister(&cdata->ac); + + return 0; +} + +static struct platform_driver tps65090_charger_driver = { + .driver = { + .name = "tps65090-charger", + .owner = THIS_MODULE, + }, + .probe = tps65090_charger_probe, + .remove = tps65090_charger_remove, +}; +module_platform_driver(tps65090_charger_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Syed Rafiuddin "); +MODULE_DESCRIPTION("tps65090 battery charger driver"); diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 6694cf43e8b..998628a2b08 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -86,6 +86,11 @@ struct tps65090_regulator_plat_data { struct tps65090_platform_data { int irq_base; + + char **supplied_to; + size_t num_supplicants; + int enable_low_current_chrg; + struct tps65090_regulator_plat_data *reg_pdata[TPS65090_REGULATOR_MAX]; }; -- cgit v1.2.3-70-g09d2 From b962abdc6531c8de837504ebc98139587162f223 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Sat, 9 Mar 2013 23:25:08 +0200 Subject: ipvs: fix some sparse warnings Add missing __percpu annotations and make ip_vs_net_id static. Signed-off-by: Julian Anastasov Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 +- net/netfilter/ipvs/ip_vs_core.c | 8 +------- net/netfilter/ipvs/ip_vs_est.c | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 68c69d54d39..29bc0557756 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -459,7 +459,7 @@ struct ip_vs_estimator { struct ip_vs_stats { struct ip_vs_stats_user ustats; /* statistics */ struct ip_vs_estimator est; /* estimator */ - struct ip_vs_cpu_stats *cpustats; /* per cpu counters */ + struct ip_vs_cpu_stats __percpu *cpustats; /* per cpu counters */ spinlock_t lock; /* spin lock */ struct ip_vs_stats_user ustats0; /* reset values */ }; diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 47edf5a40a5..3e5e80b809f 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -69,10 +69,7 @@ EXPORT_SYMBOL(ip_vs_conn_put); EXPORT_SYMBOL(ip_vs_get_debug_level); #endif -int ip_vs_net_id __read_mostly; -#ifdef IP_VS_GENERIC_NETNS -EXPORT_SYMBOL(ip_vs_net_id); -#endif +static int ip_vs_net_id __read_mostly; /* netns cnt used for uniqueness */ static atomic_t ipvs_netns_cnt = ATOMIC_INIT(0); @@ -1181,9 +1178,6 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) iph.len)))) { #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) { - struct net *net = - dev_net(skb_dst(skb)->dev); - if (!skb->dev) skb->dev = net->loopback_dev; icmpv6_send(skb, diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 0fac6017b6f..6bee6d0c73a 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -56,7 +56,7 @@ * Make a summary from each cpu */ static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum, - struct ip_vs_cpu_stats *stats) + struct ip_vs_cpu_stats __percpu *stats) { int i; -- cgit v1.2.3-70-g09d2 From dece40e848f6e022f960dc9de54be518928460c3 Mon Sep 17 00:00:00 2001 From: Vladimir Davydov Date: Wed, 13 Mar 2013 23:40:14 +0000 Subject: netfilter: nf_conntrack: speed up module removal path if netns in use The patch introduces nf_conntrack_cleanup_net_list(), which cleanups nf_conntrack for a list of netns and calls synchronize_net() only once for them all. This should reduce netns destruction time. I've measured cleanup time for 1k dummy net ns. Here are the results: # modprobe nf_conntrack # time modprobe -r nf_conntrack real 0m10.337s user 0m0.000s sys 0m0.376s # modprobe nf_conntrack # time modprobe -r nf_conntrack real 0m5.661s user 0m0.000s sys 0m0.216s Signed-off-by: Vladimir Davydov Cc: Patrick McHardy Cc: "David S. Miller" Cc: "Eric W. Biederman" Acked-by: Gao feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_core.h | 1 + net/netfilter/nf_conntrack_core.c | 46 +++++++++++++++++++++---------- net/netfilter/nf_conntrack_standalone.c | 16 +++++++---- 3 files changed, 43 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index 930275fa2ea..fb2b6234e93 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -27,6 +27,7 @@ extern unsigned int nf_conntrack_in(struct net *net, extern int nf_conntrack_init_net(struct net *net); extern void nf_conntrack_cleanup_net(struct net *net); +extern void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list); extern int nf_conntrack_proto_pernet_init(struct net *net); extern void nf_conntrack_proto_pernet_fini(struct net *net); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 1068deb97c8..007e8c43d19 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1365,30 +1365,48 @@ void nf_conntrack_cleanup_end(void) */ void nf_conntrack_cleanup_net(struct net *net) { + LIST_HEAD(single); + + list_add(&net->exit_list, &single); + nf_conntrack_cleanup_net_list(&single); +} + +void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list) +{ + int busy; + struct net *net; + /* * This makes sure all current packets have passed through * netfilter framework. Roll on, two-stage module * delete... */ synchronize_net(); - i_see_dead_people: - nf_ct_iterate_cleanup(net, kill_all, NULL); - nf_ct_release_dying_list(net); - if (atomic_read(&net->ct.count) != 0) { +i_see_dead_people: + busy = 0; + list_for_each_entry(net, net_exit_list, exit_list) { + nf_ct_iterate_cleanup(net, kill_all, NULL); + nf_ct_release_dying_list(net); + if (atomic_read(&net->ct.count) != 0) + busy = 1; + } + if (busy) { schedule(); goto i_see_dead_people; } - nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); - nf_conntrack_proto_pernet_fini(net); - nf_conntrack_helper_pernet_fini(net); - nf_conntrack_ecache_pernet_fini(net); - nf_conntrack_tstamp_pernet_fini(net); - nf_conntrack_acct_pernet_fini(net); - nf_conntrack_expect_pernet_fini(net); - kmem_cache_destroy(net->ct.nf_conntrack_cachep); - kfree(net->ct.slabname); - free_percpu(net->ct.stat); + list_for_each_entry(net, net_exit_list, exit_list) { + nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); + nf_conntrack_proto_pernet_fini(net); + nf_conntrack_helper_pernet_fini(net); + nf_conntrack_ecache_pernet_fini(net); + nf_conntrack_tstamp_pernet_fini(net); + nf_conntrack_acct_pernet_fini(net); + nf_conntrack_expect_pernet_fini(net); + kmem_cache_destroy(net->ct.nf_conntrack_cachep); + kfree(net->ct.slabname); + free_percpu(net->ct.stat); + } } void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 6bcce401fd1..6c69fbdb836 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -545,16 +545,20 @@ out_init: return ret; } -static void nf_conntrack_pernet_exit(struct net *net) +static void nf_conntrack_pernet_exit(struct list_head *net_exit_list) { - nf_conntrack_standalone_fini_sysctl(net); - nf_conntrack_standalone_fini_proc(net); - nf_conntrack_cleanup_net(net); + struct net *net; + + list_for_each_entry(net, net_exit_list, exit_list) { + nf_conntrack_standalone_fini_sysctl(net); + nf_conntrack_standalone_fini_proc(net); + } + nf_conntrack_cleanup_net_list(net_exit_list); } static struct pernet_operations nf_conntrack_net_ops = { - .init = nf_conntrack_pernet_init, - .exit = nf_conntrack_pernet_exit, + .init = nf_conntrack_pernet_init, + .exit_batch = nf_conntrack_pernet_exit, }; static int __init nf_conntrack_standalone_init(void) -- cgit v1.2.3-70-g09d2 From 2a1486981c1317dc4f4aad568f2cc6e49dfb8c82 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 19 Mar 2013 14:00:53 +0000 Subject: Fix breakage in MIPS siginfo handling MIPS's siginfo handling has been broken since this commit: commit 574c4866e33d648520a8bd5bf6f573ea6e554e88 Author: Al Viro Date: Sun Nov 25 22:24:19 2012 -0500 consolidate kernel-side struct sigaction declarations for 64-bit BE MIPS CPUs. The UAPI variant looks like this: struct sigaction { unsigned int sa_flags; __sighandler_t sa_handler; sigset_t sa_mask; }; but the core kernel's variant looks like this: struct sigaction { #ifndef __ARCH_HAS_ODD_SIGACTION __sighandler_t sa_handler; unsigned long sa_flags; #else unsigned long sa_flags; __sighandler_t sa_handler; #endif #ifdef __ARCH_HAS_SA_RESTORER __sigrestore_t sa_restorer; #endif sigset_t sa_mask; }; The problem is that sa_flags has been changed from an unsigned int to an unsigned long. Fix this by making sa_flags unsigned int if __ARCH_HAS_ODD_SIGACTION is defined. Whilst we're at it, rename __ARCH_HAS_ODD_SIGACTION to __ARCH_HAS_IRIX_SIGACTION. Signed-off-by: David Howells Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org Acked-by: Al Viro Signed-off-by: Ralf Baechle --- arch/mips/include/asm/signal.h | 2 +- include/linux/compat.h | 4 ++-- include/linux/signal.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/mips/include/asm/signal.h b/arch/mips/include/asm/signal.h index 197f6367c20..8efe5a9e2c3 100644 --- a/arch/mips/include/asm/signal.h +++ b/arch/mips/include/asm/signal.h @@ -21,6 +21,6 @@ #include #include -#define __ARCH_HAS_ODD_SIGACTION +#define __ARCH_HAS_IRIX_SIGACTION #endif /* _ASM_SIGNAL_H */ diff --git a/include/linux/compat.h b/include/linux/compat.h index 76a87fb57ac..377cd8c3395 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -141,11 +141,11 @@ typedef struct { } compat_sigset_t; struct compat_sigaction { -#ifndef __ARCH_HAS_ODD_SIGACTION +#ifndef __ARCH_HAS_IRIX_SIGACTION compat_uptr_t sa_handler; compat_ulong_t sa_flags; #else - compat_ulong_t sa_flags; + compat_uint_t sa_flags; compat_uptr_t sa_handler; #endif #ifdef __ARCH_HAS_SA_RESTORER diff --git a/include/linux/signal.h b/include/linux/signal.h index a2dcb94ea49..9475c5cb28b 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -250,11 +250,11 @@ extern int show_unhandled_signals; extern int sigsuspend(sigset_t *); struct sigaction { -#ifndef __ARCH_HAS_ODD_SIGACTION +#ifndef __ARCH_HAS_IRIX_SIGACTION __sighandler_t sa_handler; unsigned long sa_flags; #else - unsigned long sa_flags; + unsigned int sa_flags; __sighandler_t sa_handler; #endif #ifdef __ARCH_HAS_SA_RESTORER -- cgit v1.2.3-70-g09d2 From ef2d41b19b8100ce63eabba9ee87953aa685921a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 15 Feb 2013 15:06:28 -0300 Subject: [media] davinci: remove VPBE_ENC_DV_PRESET and rename VPBE_ENC_CUSTOM_TIMINGS Remove VPBE_ENC_DV_PRESET (the DV_PRESET API is no longer supported) and VPBE_ENC_CUSTOM_TIMINGS is renamed to VPBE_ENC_DV_TIMINGS since the old "CUSTOM_TIMINGS" name is deprecated in favor of "DV_TIMINGS". Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Acked-by: Sekhar Nori Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-davinci/board-dm644x-evm.c | 4 ++-- arch/arm/mach-davinci/dm644x.c | 2 +- drivers/media/platform/davinci/vpbe.c | 8 ++++---- drivers/media/platform/davinci/vpbe_display.c | 2 +- drivers/media/platform/davinci/vpbe_venc.c | 8 ++++---- include/media/davinci/vpbe_types.h | 3 +-- 6 files changed, 13 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 71735e7797c..a02180052a7 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -649,7 +649,7 @@ static struct vpbe_enc_mode_info dm644xevm_enc_std_timing[] = { static struct vpbe_enc_mode_info dm644xevm_enc_preset_timing[] = { { .name = "480p59_94", - .timings_type = VPBE_ENC_CUSTOM_TIMINGS, + .timings_type = VPBE_ENC_DV_TIMINGS, .dv_timings = V4L2_DV_BT_CEA_720X480P59_94, .interlaced = 0, .xres = 720, @@ -661,7 +661,7 @@ static struct vpbe_enc_mode_info dm644xevm_enc_preset_timing[] = { }, { .name = "576p50", - .timings_type = VPBE_ENC_CUSTOM_TIMINGS, + .timings_type = VPBE_ENC_DV_TIMINGS, .dv_timings = V4L2_DV_BT_CEA_720X576P50, .interlaced = 0, .xres = 720, diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index db1dd92e00a..ee0e994748e 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -706,7 +706,7 @@ static int dm644x_venc_setup_clock(enum vpbe_enc_timings_type type, v |= DM644X_VPSS_DACCLKEN; writel(v, DAVINCI_SYSMOD_VIRT(SYSMOD_VPSS_CLKCTL)); break; - case VPBE_ENC_CUSTOM_TIMINGS: + case VPBE_ENC_DV_TIMINGS: if (pclock <= 27000000) { v |= DM644X_VPSS_DACCLKEN; writel(v, DAVINCI_SYSMOD_VIRT(SYSMOD_VPSS_CLKCTL)); diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index 4ca0f9a2ad8..2a49f00c454 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -344,7 +344,7 @@ static int vpbe_s_dv_timings(struct vpbe_device *vpbe_dev, return -EINVAL; for (i = 0; i < output->num_modes; i++) { - if (output->modes[i].timings_type == VPBE_ENC_CUSTOM_TIMINGS && + if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS && !memcmp(&output->modes[i].dv_timings, dv_timings, sizeof(*dv_timings))) break; @@ -385,7 +385,7 @@ static int vpbe_g_dv_timings(struct vpbe_device *vpbe_dev, struct v4l2_dv_timings *dv_timings) { if (vpbe_dev->current_timings.timings_type & - VPBE_ENC_CUSTOM_TIMINGS) { + VPBE_ENC_DV_TIMINGS) { *dv_timings = vpbe_dev->current_timings.dv_timings; return 0; } @@ -412,7 +412,7 @@ static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev, return -EINVAL; for (i = 0; i < output->num_modes; i++) { - if (output->modes[i].timings_type == VPBE_ENC_CUSTOM_TIMINGS) { + if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS) { if (j == timings->index) break; j++; @@ -515,7 +515,7 @@ static int vpbe_set_mode(struct vpbe_device *vpbe_dev, return vpbe_s_std(vpbe_dev, &preset_mode->std_id); if (preset_mode->timings_type & - VPBE_ENC_CUSTOM_TIMINGS) { + VPBE_ENC_DV_TIMINGS) { dv_timings = preset_mode->dv_timings; return vpbe_s_dv_timings(vpbe_dev, &dv_timings); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 9f9f2c1a073..8290fddbd47 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1202,7 +1202,7 @@ vpbe_display_g_dv_timings(struct file *file, void *priv, /* Get the given standard in the encoder */ if (vpbe_dev->current_timings.timings_type & - VPBE_ENC_CUSTOM_TIMINGS) { + VPBE_ENC_DV_TIMINGS) { *dv_timings = vpbe_dev->current_timings.dv_timings; } else { return -EINVAL; diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index bdbebd59df9..9546d268e2d 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -313,7 +313,7 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0) return -EINVAL; venc_enabledigitaloutput(sd, 0); @@ -360,7 +360,7 @@ static int venc_set_576p50(struct v4l2_subdev *sd) venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0) return -EINVAL; venc_enabledigitaloutput(sd, 0); @@ -400,7 +400,7 @@ static int venc_set_720p60_internal(struct v4l2_subdev *sd) struct venc_state *venc = to_state(sd); struct venc_platform_data *pdata = venc->pdata; - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 74250000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0) return -EINVAL; venc_enabledigitaloutput(sd, 0); @@ -428,7 +428,7 @@ static int venc_set_1080i30_internal(struct v4l2_subdev *sd) struct venc_state *venc = to_state(sd); struct venc_platform_data *pdata = venc->pdata; - if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 74250000) < 0) + if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0) return -EINVAL; venc_enabledigitaloutput(sd, 0); diff --git a/include/media/davinci/vpbe_types.h b/include/media/davinci/vpbe_types.h index 9b85396514b..05dbe0ba514 100644 --- a/include/media/davinci/vpbe_types.h +++ b/include/media/davinci/vpbe_types.h @@ -26,8 +26,7 @@ enum vpbe_version { /* vpbe_timing_type - Timing types used in vpbe device */ enum vpbe_enc_timings_type { VPBE_ENC_STD = 0x1, - VPBE_ENC_DV_PRESET = 0x2, - VPBE_ENC_CUSTOM_TIMINGS = 0x4, + VPBE_ENC_DV_TIMINGS = 0x4, /* Used when set timings through FB device interface */ VPBE_ENC_TIMINGS_INVALID = 0x8, }; -- cgit v1.2.3-70-g09d2 From 1de1951930d4450d1645a0a907b710f268af42c3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 4 Mar 2013 07:18:38 -0300 Subject: [media] davinci/dm644x_ccdc: fix compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/platform/davinci/dm644x_ccdc.c: In function ‘validate_ccdc_param’: drivers/media/platform/davinci/dm644x_ccdc.c:233:32: warning: comparison between ‘enum ccdc_gama_width’ and ‘enum ccdc_data_size’ [-Wenum-compare] It took a bit of work, see this thread of an earlier attempt to fix this: https://patchwork.kernel.org/patch/1923091/ I've chosen not to follow the suggestions in that thread since gamma_width is really a different property from data_size. What you really want is to know if gamma_width fits inside data_size and for that you need to translate each enum into a maximum bit number so you can safely compare the two. So I put in two static inline translation functions instead, keeping the rest of the code the same (except for fixing the 'gama' typo). Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/dm644x_ccdc.c | 13 +++++++----- drivers/media/platform/davinci/dm644x_ccdc_regs.h | 2 +- include/media/davinci/dm644x_ccdc.h | 24 +++++++++++++++++------ 3 files changed, 27 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c index 318e8051299..971d639b667 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc.c +++ b/drivers/media/platform/davinci/dm644x_ccdc.c @@ -228,9 +228,12 @@ static void ccdc_readregs(void) static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) { if (ccdcparam->alaw.enable) { - if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) || - (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) || - (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) { + u8 max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd); + u8 max_data = ccdc_data_size_max_bit(ccdcparam->data_sz); + + if ((ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) || + (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_15_6) || + (max_gamma > max_data)) { dev_dbg(ccdc_cfg.dev, "\nInvalid data line select"); return -1; } @@ -560,8 +563,8 @@ void ccdc_config_raw(void) /* Enable and configure aLaw register if needed */ if (config_params->alaw.enable) { - val = ((config_params->alaw.gama_wd & - CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE); + val = ((config_params->alaw.gamma_wd & + CCDC_ALAW_GAMMA_WD_MASK) | CCDC_ALAW_ENABLE); regw(val, CCDC_ALAW); dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val); } diff --git a/drivers/media/platform/davinci/dm644x_ccdc_regs.h b/drivers/media/platform/davinci/dm644x_ccdc_regs.h index 90370e414e2..2b0aca5383f 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc_regs.h +++ b/drivers/media/platform/davinci/dm644x_ccdc_regs.h @@ -84,7 +84,7 @@ #define CCDC_VDHDEN_ENABLE (1 << 16) #define CCDC_LPF_ENABLE (1 << 14) #define CCDC_ALAW_ENABLE (1 << 3) -#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_ALAW_GAMMA_WD_MASK 7 #define CCDC_BLK_CLAMP_ENABLE (1 << 31) #define CCDC_BLK_SGAIN_MASK 0x1F #define CCDC_BLK_ST_PXL_MASK 0x7FFF diff --git a/include/media/davinci/dm644x_ccdc.h b/include/media/davinci/dm644x_ccdc.h index 3e178eb52fb..852e96c4bb4 100644 --- a/include/media/davinci/dm644x_ccdc.h +++ b/include/media/davinci/dm644x_ccdc.h @@ -38,17 +38,23 @@ enum ccdc_sample_line { CCDC_SAMPLE_16LINES }; -/* enum for Alaw gama width */ -enum ccdc_gama_width { - CCDC_GAMMA_BITS_15_6, +/* enum for Alaw gamma width */ +enum ccdc_gamma_width { + CCDC_GAMMA_BITS_15_6, /* use bits 15-6 for gamma */ CCDC_GAMMA_BITS_14_5, CCDC_GAMMA_BITS_13_4, CCDC_GAMMA_BITS_12_3, CCDC_GAMMA_BITS_11_2, CCDC_GAMMA_BITS_10_1, - CCDC_GAMMA_BITS_09_0 + CCDC_GAMMA_BITS_09_0 /* use bits 9-0 for gamma */ }; +/* returns the highest bit used for the gamma */ +static inline u8 ccdc_gamma_width_max_bit(enum ccdc_gamma_width width) +{ + return 15 - width; +} + enum ccdc_data_size { CCDC_DATA_16BITS, CCDC_DATA_15BITS, @@ -60,12 +66,18 @@ enum ccdc_data_size { CCDC_DATA_8BITS }; +/* returns the highest bit used for this data size */ +static inline u8 ccdc_data_size_max_bit(enum ccdc_data_size sz) +{ + return sz == CCDC_DATA_8BITS ? 7 : 15 - sz; +} + /* structure for ALaw */ struct ccdc_a_law { /* Enable/disable A-Law */ unsigned char enable; - /* Gama Width Input */ - enum ccdc_gama_width gama_wd; + /* Gamma Width Input */ + enum ccdc_gamma_width gamma_wd; }; /* structure for Black Clamping */ -- cgit v1.2.3-70-g09d2 From db242f62bd18f6c689c0e04f3df14be2deb89462 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 4 Mar 2013 07:24:07 -0300 Subject: [media] davinci: more gama -> gamma typo fixes Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/dm355_ccdc.c | 10 +++++----- drivers/media/platform/davinci/dm355_ccdc_regs.h | 2 +- drivers/media/platform/davinci/isif.c | 2 +- drivers/media/platform/davinci/isif_regs.h | 4 ++-- include/media/davinci/dm355_ccdc.h | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c index 4277e4ad810..2364dbab804 100644 --- a/drivers/media/platform/davinci/dm355_ccdc.c +++ b/drivers/media/platform/davinci/dm355_ccdc.c @@ -85,7 +85,7 @@ static struct ccdc_oper_config { .mfilt1 = CCDC_NO_MEDIAN_FILTER1, .mfilt2 = CCDC_NO_MEDIAN_FILTER2, .alaw = { - .gama_wd = 2, + .gamma_wd = 2, }, .blk_clamp = { .sample_pixel = 1, @@ -303,8 +303,8 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) } if (ccdcparam->alaw.enable) { - if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 || - ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) { + if (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_13_4 || + ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) { dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n"); return -EINVAL; } @@ -680,8 +680,8 @@ static int ccdc_config_raw(void) /* Enable and configure aLaw register if needed */ if (config_params->alaw.enable) { val |= (CCDC_ALAW_ENABLE | - ((config_params->alaw.gama_wd & - CCDC_ALAW_GAMA_WD_MASK) << + ((config_params->alaw.gamma_wd & + CCDC_ALAW_GAMMA_WD_MASK) << CCDC_GAMMAWD_INPUT_SHIFT)); } diff --git a/drivers/media/platform/davinci/dm355_ccdc_regs.h b/drivers/media/platform/davinci/dm355_ccdc_regs.h index d6d2ef0533b..2e1946e0b99 100644 --- a/drivers/media/platform/davinci/dm355_ccdc_regs.h +++ b/drivers/media/platform/davinci/dm355_ccdc_regs.h @@ -153,7 +153,7 @@ #define CCDC_VDHDEN_ENABLE (1 << 16) #define CCDC_LPF_ENABLE (1 << 14) #define CCDC_ALAW_ENABLE 1 -#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_ALAW_GAMMA_WD_MASK 7 #define CCDC_REC656IF_BT656_EN 3 #define CCDC_FMTCFG_FMTMODE_MASK 3 diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c index 5050f9265f4..abc3ae36645 100644 --- a/drivers/media/platform/davinci/isif.c +++ b/drivers/media/platform/davinci/isif.c @@ -604,7 +604,7 @@ static int isif_config_raw(void) if (module_params->compress.alg == ISIF_ALAW) val |= ISIF_ALAW_ENABLE; - val |= (params->data_msb << ISIF_ALAW_GAMA_WD_SHIFT); + val |= (params->data_msb << ISIF_ALAW_GAMMA_WD_SHIFT); regw(val, CGAMMAWD); /* Configure DPCM compression settings */ diff --git a/drivers/media/platform/davinci/isif_regs.h b/drivers/media/platform/davinci/isif_regs.h index aa69a463c12..3993aece821 100644 --- a/drivers/media/platform/davinci/isif_regs.h +++ b/drivers/media/platform/davinci/isif_regs.h @@ -203,8 +203,8 @@ #define ISIF_LPF_MASK 1 /* GAMMAWD registers */ -#define ISIF_ALAW_GAMA_WD_MASK 0xF -#define ISIF_ALAW_GAMA_WD_SHIFT 1 +#define ISIF_ALAW_GAMMA_WD_MASK 0xF +#define ISIF_ALAW_GAMMA_WD_SHIFT 1 #define ISIF_ALAW_ENABLE 1 #define ISIF_GAMMAWD_CFA_SHIFT 5 diff --git a/include/media/davinci/dm355_ccdc.h b/include/media/davinci/dm355_ccdc.h index adf2fe4bf0b..c669a9fb75e 100644 --- a/include/media/davinci/dm355_ccdc.h +++ b/include/media/davinci/dm355_ccdc.h @@ -38,7 +38,7 @@ enum ccdc_sample_line { CCDC_SAMPLE_16LINES }; -/* enum for Alaw gama width */ +/* enum for Alaw gamma width */ enum ccdc_gamma_width { CCDC_GAMMA_BITS_13_4, CCDC_GAMMA_BITS_12_3, @@ -97,8 +97,8 @@ enum ccdc_mfilt2 { struct ccdc_a_law { /* Enable/disable A-Law */ unsigned char enable; - /* Gama Width Input */ - enum ccdc_gamma_width gama_wd; + /* Gamma Width Input */ + enum ccdc_gamma_width gamma_wd; }; /* structure for Black Clamping */ -- cgit v1.2.3-70-g09d2 From 3d6ee287a3e341c88eafd0b4620b12d640b3736b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 12 Mar 2013 20:26:02 +0100 Subject: clk: Introduce optional is_prepared callback To reflect whether a clk_hw is prepared the clk_hw may implement the optional is_prepared callback. If not implemented we fall back to use the software prepare counter. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 21 +++++++++++++++++++++ include/linux/clk-provider.h | 6 ++++++ 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ed87b240580..7571b5054f3 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -451,6 +451,27 @@ unsigned long __clk_get_flags(struct clk *clk) return !clk ? 0 : clk->flags; } +bool __clk_is_prepared(struct clk *clk) +{ + int ret; + + if (!clk) + return false; + + /* + * .is_prepared is optional for clocks that can prepare + * fall back to software usage counter if it is missing + */ + if (!clk->ops->is_prepared) { + ret = clk->prepare_count ? 1 : 0; + goto out; + } + + ret = clk->ops->is_prepared(clk->hw); +out: + return !!ret; +} + bool __clk_is_enabled(struct clk *clk) { int ret; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 7f197d7addb..ee946862e05 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -45,6 +45,10 @@ struct clk_hw; * undo any work done in the @prepare callback. Called with * prepare_lock held. * + * @is_prepared: Queries the hardware to determine if the clock is prepared. + * This function is allowed to sleep. Optional, if this op is not + * set then the prepare count will be used. + * * @enable: Enable the clock atomically. This must not return until the * clock is generating a valid clock signal, usable by consumer * devices. Called with enable_lock held. This function must not @@ -108,6 +112,7 @@ struct clk_hw; struct clk_ops { int (*prepare)(struct clk_hw *hw); void (*unprepare)(struct clk_hw *hw); + int (*is_prepared)(struct clk_hw *hw); int (*enable)(struct clk_hw *hw); void (*disable)(struct clk_hw *hw); int (*is_enabled)(struct clk_hw *hw); @@ -351,6 +356,7 @@ unsigned int __clk_get_enable_count(struct clk *clk); unsigned int __clk_get_prepare_count(struct clk *clk); unsigned long __clk_get_rate(struct clk *clk); unsigned long __clk_get_flags(struct clk *clk); +bool __clk_is_prepared(struct clk *clk); bool __clk_is_enabled(struct clk *clk); struct clk *__clk_lookup(const char *name); -- cgit v1.2.3-70-g09d2 From 3cc8247f1dce79511de8bf0f69ab02a46cc315b7 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 12 Mar 2013 20:26:04 +0100 Subject: clk: Introduce optional unprepare_unused callback An unprepare_unused callback is introduced due to the same reasons to why the disable_unused callback was added. During the clk_disable_unused sequence, those clk_hw that needs specific treatment with regards to being unprepared, shall implement the unprepare_unused callback. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 7 +++++-- include/linux/clk-provider.h | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c0141f3e110..253792a46c0 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -352,9 +352,12 @@ static void clk_unprepare_unused_subtree(struct clk *clk) if (clk->flags & CLK_IGNORE_UNUSED) return; - if (__clk_is_prepared(clk)) - if (clk->ops->unprepare) + if (__clk_is_prepared(clk)) { + if (clk->ops->unprepare_unused) + clk->ops->unprepare_unused(clk->hw); + else if (clk->ops->unprepare) clk->ops->unprepare(clk->hw); + } } /* caller must hold prepare_lock */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index ee946862e05..56e6cc12c79 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -49,6 +49,10 @@ struct clk_hw; * This function is allowed to sleep. Optional, if this op is not * set then the prepare count will be used. * + * @unprepare_unused: Unprepare the clock atomically. Only called from + * clk_disable_unused for prepare clocks with special needs. + * Called with prepare mutex held. This function may sleep. + * * @enable: Enable the clock atomically. This must not return until the * clock is generating a valid clock signal, usable by consumer * devices. Called with enable_lock held. This function must not @@ -113,6 +117,7 @@ struct clk_ops { int (*prepare)(struct clk_hw *hw); void (*unprepare)(struct clk_hw *hw); int (*is_prepared)(struct clk_hw *hw); + void (*unprepare_unused)(struct clk_hw *hw); int (*enable)(struct clk_hw *hw); void (*disable)(struct clk_hw *hw); int (*is_enabled)(struct clk_hw *hw); -- cgit v1.2.3-70-g09d2 From 14a40ffccd6163bbcd1d6f32b28a88ffe6149fc6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 19 Mar 2013 13:45:20 -0700 Subject: sched: replace PF_THREAD_BOUND with PF_NO_SETAFFINITY PF_THREAD_BOUND was originally used to mark kernel threads which were bound to a specific CPU using kthread_bind() and a task with the flag set allows cpus_allowed modifications only to itself. Workqueue is currently abusing it to prevent userland from meddling with cpus_allowed of workqueue workers. What we need is a flag to prevent userland from messing with cpus_allowed of certain kernel tasks. In kernel, anyone can (incorrectly) squash the flag, and, for worker-type usages, restricting cpus_allowed modification to the task itself doesn't provide meaningful extra proection as other tasks can inject work items to the task anyway. This patch replaces PF_THREAD_BOUND with PF_NO_SETAFFINITY. sched_setaffinity() checks the flag and return -EINVAL if set. set_cpus_allowed_ptr() is no longer affected by the flag. This will allow simplifying workqueue worker CPU affinity management. Signed-off-by: Tejun Heo Acked-by: Ingo Molnar Reviewed-by: Lai Jiangshan Cc: Peter Zijlstra Cc: Thomas Gleixner --- include/linux/sched.h | 2 +- kernel/cgroup.c | 4 ++-- kernel/cpuset.c | 16 ++++++++-------- kernel/kthread.c | 2 +- kernel/sched/core.c | 9 ++++----- kernel/workqueue.c | 10 +++------- 6 files changed, 19 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index d35d2b6ddbf..e5c64f7b8c1 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1793,7 +1793,7 @@ extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, #define PF_SWAPWRITE 0x00800000 /* Allowed to write to swap */ #define PF_SPREAD_PAGE 0x01000000 /* Spread page cache over cpuset */ #define PF_SPREAD_SLAB 0x02000000 /* Spread some slab caches over cpuset */ -#define PF_THREAD_BOUND 0x04000000 /* Thread bound to specific cpu */ +#define PF_NO_SETAFFINITY 0x04000000 /* Userland is not allowed to meddle with cpus_allowed */ #define PF_MCE_EARLY 0x08000000 /* Early kill for mce process policy */ #define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */ #define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index a32f9432666..3852d926322 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2224,11 +2224,11 @@ retry_find_task: tsk = tsk->group_leader; /* - * Workqueue threads may acquire PF_THREAD_BOUND and become + * Workqueue threads may acquire PF_NO_SETAFFINITY and become * trapped in a cpuset, or RT worker may be born in a cgroup * with no rt_runtime allocated. Just say no. */ - if (tsk == kthreadd_task || (tsk->flags & PF_THREAD_BOUND)) { + if (tsk == kthreadd_task || (tsk->flags & PF_NO_SETAFFINITY)) { ret = -EINVAL; rcu_read_unlock(); goto out_unlock_cgroup; diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 4f9dfe43ecb..f22e9479270 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1388,16 +1388,16 @@ static int cpuset_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) cgroup_taskset_for_each(task, cgrp, tset) { /* - * Kthreads bound to specific cpus cannot be moved to a new - * cpuset; we cannot change their cpu affinity and - * isolating such threads by their set of allowed nodes is - * unnecessary. Thus, cpusets are not applicable for such - * threads. This prevents checking for success of - * set_cpus_allowed_ptr() on all attached tasks before - * cpus_allowed may be changed. + * Kthreads which disallow setaffinity shouldn't be moved + * to a new cpuset; we don't want to change their cpu + * affinity and isolating such threads by their set of + * allowed nodes is unnecessary. Thus, cpusets are not + * applicable for such threads. This prevents checking for + * success of set_cpus_allowed_ptr() on all attached tasks + * before cpus_allowed may be changed. */ ret = -EINVAL; - if (task->flags & PF_THREAD_BOUND) + if (task->flags & PF_NO_SETAFFINITY) goto out_unlock; ret = security_task_setscheduler(task); if (ret) diff --git a/kernel/kthread.c b/kernel/kthread.c index 691dc2ef9ba..a2fbbb782ba 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -260,7 +260,7 @@ static void __kthread_bind(struct task_struct *p, unsigned int cpu) { /* It's safe because the task is inactive. */ do_set_cpus_allowed(p, cpumask_of(cpu)); - p->flags |= PF_THREAD_BOUND; + p->flags |= PF_NO_SETAFFINITY; } /** diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 7f12624a393..23606ee961b 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4126,6 +4126,10 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask) get_task_struct(p); rcu_read_unlock(); + if (p->flags & PF_NO_SETAFFINITY) { + retval = -EINVAL; + goto out_put_task; + } if (!alloc_cpumask_var(&cpus_allowed, GFP_KERNEL)) { retval = -ENOMEM; goto out_put_task; @@ -4773,11 +4777,6 @@ int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask) goto out; } - if (unlikely((p->flags & PF_THREAD_BOUND) && p != current)) { - ret = -EINVAL; - goto out; - } - do_set_cpus_allowed(p, new_mask); /* Can the task run on the task's current CPU? If so, we're done */ diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 969be0b7207..39a591f65b0 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1757,12 +1757,8 @@ static struct worker *create_worker(struct worker_pool *pool) set_user_nice(worker->task, pool->attrs->nice); set_cpus_allowed_ptr(worker->task, pool->attrs->cpumask); - /* - * %PF_THREAD_BOUND is used to prevent userland from meddling with - * cpumask of workqueue workers. This is an abuse. We need - * %PF_NO_SETAFFINITY. - */ - worker->task->flags |= PF_THREAD_BOUND; + /* prevent userland from meddling with cpumask of workqueue workers */ + worker->task->flags |= PF_NO_SETAFFINITY; /* * The caller is responsible for ensuring %POOL_DISASSOCIATED @@ -3876,7 +3872,7 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt, } wq->rescuer = rescuer; - rescuer->task->flags |= PF_THREAD_BOUND; + rescuer->task->flags |= PF_NO_SETAFFINITY; wake_up_process(rescuer->task); } -- cgit v1.2.3-70-g09d2 From 77f65ebdca506870d99bfabe52bde222511022ec Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 19 Mar 2013 10:18:11 +0000 Subject: packet: packet fanout rollover during socket overload Changes: v3->v2: rebase (no other changes) passes selftest v2->v1: read f->num_members only once fix bug: test rollover mode + flag Minimize packet drop in a fanout group. If one socket is full, roll over packets to another from the group. Maintain flow affinity during normal load using an rxhash fanout policy, while dispersing unexpected traffic storms that hit a single cpu, such as spoofed-source DoS flows. Rollover breaks affinity for flows arriving at saturated sockets during those conditions. The patch adds a fanout policy ROLLOVER that rotates between sockets, filling each socket before moving to the next. It also adds a fanout flag ROLLOVER. If passed along with any other fanout policy, the primary policy is applied until the chosen socket is full. Then, rollover selects another socket, to delay packet drop until the entire system is saturated. Probing sockets is not free. Selecting the last used socket, as rollover does, is a greedy approach that maximizes chance of success, at the cost of extreme load imbalance. In practice, with sufficiently long queues to absorb bursts, sockets are drained in parallel and load balance looks uniform in `top`. To avoid contention, scales counters with number of sockets and accesses them lockfree. Values are bounds checked to ensure correctness. Tested using an application with 9 threads pinned to CPUs, one socket per thread and sufficient busywork per packet operation to limits each thread to handling 32 Kpps. When sent 500 Kpps single UDP stream packets, a FANOUT_CPU setup processes 32 Kpps in total without this patch, 270 Kpps with the patch. Tested with read() and with a packet ring (V1). Also, passes psock_fanout.c unit test added to selftests. Signed-off-by: Willem de Bruijn Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- include/uapi/linux/if_packet.h | 2 + net/packet/af_packet.c | 109 +++++-- net/packet/internal.h | 3 +- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net-afpacket/Makefile | 18 ++ .../testing/selftests/net-afpacket/psock_fanout.c | 326 +++++++++++++++++++++ .../selftests/net-afpacket/run_afpackettests | 16 + 7 files changed, 451 insertions(+), 24 deletions(-) create mode 100644 tools/testing/selftests/net-afpacket/Makefile create mode 100644 tools/testing/selftests/net-afpacket/psock_fanout.c create mode 100644 tools/testing/selftests/net-afpacket/run_afpackettests (limited to 'include') diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h index f9a60375f0d..8136658ea47 100644 --- a/include/uapi/linux/if_packet.h +++ b/include/uapi/linux/if_packet.h @@ -55,6 +55,8 @@ struct sockaddr_ll { #define PACKET_FANOUT_HASH 0 #define PACKET_FANOUT_LB 1 #define PACKET_FANOUT_CPU 2 +#define PACKET_FANOUT_ROLLOVER 3 +#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 #define PACKET_FANOUT_FLAG_DEFRAG 0x8000 struct tpacket_stats { diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 1d6793dbfba..bd0d14c97d4 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -181,6 +181,8 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, struct packet_sock; static int tpacket_snd(struct packet_sock *po, struct msghdr *msg); +static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev); static void *packet_previous_frame(struct packet_sock *po, struct packet_ring_buffer *rb, @@ -973,11 +975,11 @@ static void *packet_current_rx_frame(struct packet_sock *po, static void *prb_lookup_block(struct packet_sock *po, struct packet_ring_buffer *rb, - unsigned int previous, + unsigned int idx, int status) { struct tpacket_kbdq_core *pkc = GET_PBDQC_FROM_RB(rb); - struct tpacket_block_desc *pbd = GET_PBLOCK_DESC(pkc, previous); + struct tpacket_block_desc *pbd = GET_PBLOCK_DESC(pkc, idx); if (status != BLOCK_STATUS(pbd)) return NULL; @@ -1041,6 +1043,29 @@ static void packet_increment_head(struct packet_ring_buffer *buff) buff->head = buff->head != buff->frame_max ? buff->head+1 : 0; } +static bool packet_rcv_has_room(struct packet_sock *po, struct sk_buff *skb) +{ + struct sock *sk = &po->sk; + bool has_room; + + if (po->prot_hook.func != tpacket_rcv) + return (atomic_read(&sk->sk_rmem_alloc) + skb->truesize) + <= sk->sk_rcvbuf; + + spin_lock(&sk->sk_receive_queue.lock); + if (po->tp_version == TPACKET_V3) + has_room = prb_lookup_block(po, &po->rx_ring, + po->rx_ring.prb_bdqc.kactive_blk_num, + TP_STATUS_KERNEL); + else + has_room = packet_lookup_frame(po, &po->rx_ring, + po->rx_ring.head, + TP_STATUS_KERNEL); + spin_unlock(&sk->sk_receive_queue.lock); + + return has_room; +} + static void packet_sock_destruct(struct sock *sk) { skb_queue_purge(&sk->sk_error_queue); @@ -1066,16 +1091,16 @@ static int fanout_rr_next(struct packet_fanout *f, unsigned int num) return x; } -static struct sock *fanout_demux_hash(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) +static unsigned int fanout_demux_hash(struct packet_fanout *f, + struct sk_buff *skb, + unsigned int num) { - u32 idx, hash = skb->rxhash; - - idx = ((u64)hash * num) >> 32; - - return f->arr[idx]; + return (((u64)skb->rxhash) * num) >> 32; } -static struct sock *fanout_demux_lb(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) +static unsigned int fanout_demux_lb(struct packet_fanout *f, + struct sk_buff *skb, + unsigned int num) { int cur, old; @@ -1083,14 +1108,40 @@ static struct sock *fanout_demux_lb(struct packet_fanout *f, struct sk_buff *skb while ((old = atomic_cmpxchg(&f->rr_cur, cur, fanout_rr_next(f, num))) != cur) cur = old; - return f->arr[cur]; + return cur; +} + +static unsigned int fanout_demux_cpu(struct packet_fanout *f, + struct sk_buff *skb, + unsigned int num) +{ + return smp_processor_id() % num; } -static struct sock *fanout_demux_cpu(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) +static unsigned int fanout_demux_rollover(struct packet_fanout *f, + struct sk_buff *skb, + unsigned int idx, unsigned int skip, + unsigned int num) { - unsigned int cpu = smp_processor_id(); + unsigned int i, j; - return f->arr[cpu % num]; + i = j = min_t(int, f->next[idx], num - 1); + do { + if (i != skip && packet_rcv_has_room(pkt_sk(f->arr[i]), skb)) { + if (i != j) + f->next[idx] = i; + return i; + } + if (++i == num) + i = 0; + } while (i != j); + + return idx; +} + +static bool fanout_has_flag(struct packet_fanout *f, u16 flag) +{ + return f->flags & (flag >> 8); } static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, @@ -1099,7 +1150,7 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, struct packet_fanout *f = pt->af_packet_priv; unsigned int num = f->num_members; struct packet_sock *po; - struct sock *sk; + unsigned int idx; if (!net_eq(dev_net(dev), read_pnet(&f->net)) || !num) { @@ -1110,23 +1161,31 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, switch (f->type) { case PACKET_FANOUT_HASH: default: - if (f->defrag) { + if (fanout_has_flag(f, PACKET_FANOUT_FLAG_DEFRAG)) { skb = ip_check_defrag(skb, IP_DEFRAG_AF_PACKET); if (!skb) return 0; } skb_get_rxhash(skb); - sk = fanout_demux_hash(f, skb, num); + idx = fanout_demux_hash(f, skb, num); break; case PACKET_FANOUT_LB: - sk = fanout_demux_lb(f, skb, num); + idx = fanout_demux_lb(f, skb, num); break; case PACKET_FANOUT_CPU: - sk = fanout_demux_cpu(f, skb, num); + idx = fanout_demux_cpu(f, skb, num); + break; + case PACKET_FANOUT_ROLLOVER: + idx = fanout_demux_rollover(f, skb, 0, (unsigned int) -1, num); break; } - po = pkt_sk(sk); + po = pkt_sk(f->arr[idx]); + if (fanout_has_flag(f, PACKET_FANOUT_FLAG_ROLLOVER) && + unlikely(!packet_rcv_has_room(po, skb))) { + idx = fanout_demux_rollover(f, skb, idx, idx, num); + po = pkt_sk(f->arr[idx]); + } return po->prot_hook.func(skb, dev, &po->prot_hook, orig_dev); } @@ -1175,10 +1234,13 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) struct packet_sock *po = pkt_sk(sk); struct packet_fanout *f, *match; u8 type = type_flags & 0xff; - u8 defrag = (type_flags & PACKET_FANOUT_FLAG_DEFRAG) ? 1 : 0; + u8 flags = type_flags >> 8; int err; switch (type) { + case PACKET_FANOUT_ROLLOVER: + if (type_flags & PACKET_FANOUT_FLAG_ROLLOVER) + return -EINVAL; case PACKET_FANOUT_HASH: case PACKET_FANOUT_LB: case PACKET_FANOUT_CPU: @@ -1203,7 +1265,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) } } err = -EINVAL; - if (match && match->defrag != defrag) + if (match && match->flags != flags) goto out; if (!match) { err = -ENOMEM; @@ -1213,7 +1275,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) write_pnet(&match->net, sock_net(sk)); match->id = id; match->type = type; - match->defrag = defrag; + match->flags = flags; atomic_set(&match->rr_cur, 0); INIT_LIST_HEAD(&match->list); spin_lock_init(&match->lock); @@ -3240,7 +3302,8 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, case PACKET_FANOUT: val = (po->fanout ? ((u32)po->fanout->id | - ((u32)po->fanout->type << 16)) : + ((u32)po->fanout->type << 16) | + ((u32)po->fanout->flags << 24)) : 0); break; case PACKET_TX_HAS_OFF: diff --git a/net/packet/internal.h b/net/packet/internal.h index e84cab8cb7a..e891f025a1b 100644 --- a/net/packet/internal.h +++ b/net/packet/internal.h @@ -77,10 +77,11 @@ struct packet_fanout { unsigned int num_members; u16 id; u8 type; - u8 defrag; + u8 flags; atomic_t rr_cur; struct list_head list; struct sock *arr[PACKET_FANOUT_MAX]; + int next[PACKET_FANOUT_MAX]; spinlock_t lock; atomic_t sk_ref; struct packet_type prot_hook ____cacheline_aligned_in_smp; diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7c6280f1cf4..7f50078d0e8 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -6,6 +6,7 @@ TARGETS += cpu-hotplug TARGETS += memory-hotplug TARGETS += efivarfs TARGETS += net-socket +TARGETS += net-afpacket all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/net-afpacket/Makefile b/tools/testing/selftests/net-afpacket/Makefile new file mode 100644 index 00000000000..45f2ffb7fda --- /dev/null +++ b/tools/testing/selftests/net-afpacket/Makefile @@ -0,0 +1,18 @@ +# Makefile for net-socket selftests + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall + +CFLAGS += -I../../../../usr/include/ + +AF_PACKET_PROGS = psock_fanout + +all: $(AF_PACKET_PROGS) +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +run_tests: all + @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]" + +clean: + $(RM) $(AF_PACKET_PROGS) diff --git a/tools/testing/selftests/net-afpacket/psock_fanout.c b/tools/testing/selftests/net-afpacket/psock_fanout.c new file mode 100644 index 00000000000..09dbf93c53d --- /dev/null +++ b/tools/testing/selftests/net-afpacket/psock_fanout.c @@ -0,0 +1,326 @@ +/* + * Copyright 2013 Google Inc. + * Author: Willem de Bruijn (willemb@google.com) + * + * A basic test of packet socket fanout behavior. + * + * Control: + * - create fanout fails as expected with illegal flag combinations + * - join fanout fails as expected with diverging types or flags + * + * Datapath: + * Open a pair of packet sockets and a pair of INET sockets, send a known + * number of packets across the two INET sockets and count the number of + * packets enqueued onto the two packet sockets. + * + * The test currently runs for + * - PACKET_FANOUT_HASH + * - PACKET_FANOUT_HASH with PACKET_FANOUT_FLAG_ROLLOVER + * - PACKET_FANOUT_ROLLOVER + * + * Todo: + * - datapath: PACKET_FANOUT_LB + * - datapath: PACKET_FANOUT_CPU + * - functionality: PACKET_FANOUT_FLAG_DEFRAG + * + * License (GPLv2): + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Hack: build even if local includes are old */ +#ifndef PACKET_FANOUT +#define PACKET_FANOUT 18 +#define PACKET_FANOUT_HASH 0 +#define PACKET_FANOUT_LB 1 +#define PACKET_FANOUT_CPU 2 +#define PACKET_FANOUT_FLAG_DEFRAG 0x8000 + +#ifndef PACKET_FANOUT_ROLLOVER +#define PACKET_FANOUT_ROLLOVER 3 +#endif + +#ifndef PACKET_FANOUT_FLAG_ROLLOVER +#define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 +#endif + +#endif + +#define DATA_LEN 100 +#define DATA_CHAR 'a' + +static void pair_udp_open(int fds[], uint16_t port) +{ + struct sockaddr_in saddr, daddr; + + fds[0] = socket(PF_INET, SOCK_DGRAM, 0); + fds[1] = socket(PF_INET, SOCK_DGRAM, 0); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: socket dgram\n"); + exit(1); + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + memset(&daddr, 0, sizeof(daddr)); + daddr.sin_family = AF_INET; + daddr.sin_port = htons(port + 1); + daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + /* must bind both to get consistent hash result */ + if (bind(fds[1], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } + if (bind(fds[0], (void *) &saddr, sizeof(saddr))) { + perror("bind"); + exit(1); + } + if (connect(fds[0], (void *) &daddr, sizeof(daddr))) { + perror("bind"); + exit(1); + } +} + +static void pair_udp_send(int fds[], int num) +{ + char buf[DATA_LEN], rbuf[DATA_LEN]; + + memset(buf, DATA_CHAR, sizeof(buf)); + while (num--) { + /* Should really handle EINTR and EAGAIN */ + if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) { + fprintf(stderr, "ERROR: send failed left=%d\n", num); + exit(1); + } + if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) { + fprintf(stderr, "ERROR: recv failed left=%d\n", num); + exit(1); + } + if (memcmp(buf, rbuf, sizeof(buf))) { + fprintf(stderr, "ERROR: data failed left=%d\n", num); + exit(1); + } + } +} + +static void sock_fanout_setfilter(int fd) +{ + struct sock_filter bpf_filter[] = { + { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ + { 0x35, 0, 5, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ + { 0x15, 0, 3, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x30, 0, 0, 0x00000051 }, /* LD ip[81] */ + { 0x15, 0, 1, DATA_CHAR }, /* JEQ DATA_CHAR [f goto nomatch]*/ + { 0x6, 0, 0, 0x00000060 }, /* RET match */ +/* nomatch */ { 0x6, 0, 0, 0x00000000 }, /* RET no match */ + }; + struct sock_fprog bpf_prog; + + bpf_prog.filter = bpf_filter; + bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter); + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog, + sizeof(bpf_prog))) { + perror("setsockopt SO_ATTACH_FILTER"); + exit(1); + } +} + +/* Open a socket in a given fanout mode. + * @return -1 if mode is bad, a valid socket otherwise */ +static int sock_fanout_open(uint16_t typeflags, int num_packets) +{ + int fd, val; + + fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + if (fd < 0) { + perror("socket packet"); + exit(1); + } + + /* fanout group ID is always 0: tests whether old groups are deleted */ + val = ((int) typeflags) << 16; + if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { + if (close(fd)) { + perror("close packet"); + exit(1); + } + return -1; + } + + val = sizeof(struct iphdr) + sizeof(struct udphdr) + DATA_LEN; + val *= num_packets; + /* hack: apparently, the above calculation is too small (TODO: fix) */ + val *= 3; + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val))) { + perror("setsockopt SO_RCVBUF"); + exit(1); + } + + sock_fanout_setfilter(fd); + return fd; +} + +static void sock_fanout_read(int fds[], const int expect[]) +{ + struct tpacket_stats stats; + socklen_t ssize; + int ret[2]; + + ssize = sizeof(stats); + if (getsockopt(fds[0], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { + perror("getsockopt statistics 0"); + exit(1); + } + ret[0] = stats.tp_packets - stats.tp_drops; + ssize = sizeof(stats); + if (getsockopt(fds[1], SOL_PACKET, PACKET_STATISTICS, &stats, &ssize)) { + perror("getsockopt statistics 1"); + exit(1); + } + ret[1] = stats.tp_packets - stats.tp_drops; + + fprintf(stderr, "info: count=%d,%d, expect=%d,%d\n", + ret[0], ret[1], expect[0], expect[1]); + + if ((!(ret[0] == expect[0] && ret[1] == expect[1])) && + (!(ret[0] == expect[1] && ret[1] == expect[0]))) { + fprintf(stderr, "ERROR: incorrect queue lengths\n"); + exit(1); + } +} + +/* Test illegal mode + flag combination */ +static void test_control_single(void) +{ + fprintf(stderr, "test: control single socket\n"); + + if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | + PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { + fprintf(stderr, "ERROR: opened socket with dual rollover\n"); + exit(1); + } +} + +/* Test illegal group with different modes or flags */ +static void test_control_group(void) +{ + int fds[2]; + + fprintf(stderr, "test: control multiple sockets\n"); + + fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[0] == -1) { + fprintf(stderr, "ERROR: failed to open HASH socket\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); + exit(1); + } + if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { + fprintf(stderr, "ERROR: joined group with wrong mode\n"); + exit(1); + } + fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + if (fds[1] == -1) { + fprintf(stderr, "ERROR: failed to join group\n"); + exit(1); + } + if (close(fds[1]) || close(fds[0])) { + fprintf(stderr, "ERROR: closing sockets\n"); + exit(1); + } +} + +static void test_datapath(uint16_t typeflags, + const int expect1[], const int expect2[]) +{ + const int expect0[] = { 0, 0 }; + int fds[2], fds_udp[2][2]; + + fprintf(stderr, "test: datapath 0x%hx\n", typeflags); + + fds[0] = sock_fanout_open(typeflags, 20); + fds[1] = sock_fanout_open(typeflags, 20); + if (fds[0] == -1 || fds[1] == -1) { + fprintf(stderr, "ERROR: failed open\n"); + exit(1); + } + pair_udp_open(fds_udp[0], 8000); + pair_udp_open(fds_udp[1], 8002); + sock_fanout_read(fds, expect0); + + /* Send data, but not enough to overflow a queue */ + pair_udp_send(fds_udp[0], 15); + pair_udp_send(fds_udp[1], 5); + sock_fanout_read(fds, expect1); + + /* Send more data, overflow the queue */ + pair_udp_send(fds_udp[0], 15); + /* TODO: ensure consistent order between expect1 and expect2 */ + sock_fanout_read(fds, expect2); + + if (close(fds_udp[1][1]) || close(fds_udp[1][0]) || + close(fds_udp[0][1]) || close(fds_udp[0][0]) || + close(fds[1]) || close(fds[0])) { + fprintf(stderr, "close datapath\n"); + exit(1); + } +} + +int main(int argc, char **argv) +{ + const int expect_hash[2][2] = { { 15, 5 }, { 5, 0 } }; + const int expect_hash_rb[2][2] = { { 15, 5 }, { 5, 10 } }; + const int expect_rb[2][2] = { { 20, 0 }, { 0, 15 } }; + + test_control_single(); + test_control_group(); + + test_datapath(PACKET_FANOUT_HASH, expect_hash[0], expect_hash[1]); + test_datapath(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_ROLLOVER, + expect_hash_rb[0], expect_hash_rb[1]); + test_datapath(PACKET_FANOUT_ROLLOVER, expect_rb[0], expect_rb[1]); + + printf("OK. All tests passed\n"); + return 0; +} diff --git a/tools/testing/selftests/net-afpacket/run_afpackettests b/tools/testing/selftests/net-afpacket/run_afpackettests new file mode 100644 index 00000000000..7907824c635 --- /dev/null +++ b/tools/testing/selftests/net-afpacket/run_afpackettests @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ $(id -u) != 0 ]; then + echo $msg must be run as root >&2 + exit 0 +fi + +echo "--------------------" +echo "running psock_fanout test" +echo "--------------------" +./psock_fanout +if [ $? -ne 0 ]; then + echo "[FAIL]" +else + echo "[PASS]" +fi -- cgit v1.2.3-70-g09d2 From cee7e443344a3845e5b9111614b41e0b1afb60ce Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 6 Nov 2012 10:17:49 +0200 Subject: smack: SMACK_MAGIC to include/uapi/linux/magic.h SMACK_MAGIC moved to a proper place for easy user space access (i.e. libsmack). Signed-off-by: Jarkko Sakkinen --- include/uapi/linux/magic.h | 1 + security/smack/smack.h | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index 873e086ce3a..249df3720be 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -11,6 +11,7 @@ #define DEBUGFS_MAGIC 0x64626720 #define SECURITYFS_MAGIC 0x73636673 #define SELINUX_MAGIC 0xf97cff8c +#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ #define RAMFS_MAGIC 0x858458f6 /* some random number */ #define TMPFS_MAGIC 0x01021994 #define HUGETLBFS_MAGIC 0x958458f6 /* some random number */ diff --git a/security/smack/smack.h b/security/smack/smack.h index 99b36124f71..8ad30955e15 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -148,11 +148,6 @@ struct smack_known { #define SMACK_UNLABELED_SOCKET 0 #define SMACK_CIPSO_SOCKET 1 -/* - * smackfs magic number - */ -#define SMACK_MAGIC 0x43415d53 /* "SMAC" */ - /* * CIPSO defaults. */ -- cgit v1.2.3-70-g09d2 From 4d10f054f7df600ec8a388091c93b2d976920de0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 19 Mar 2013 15:38:50 +0100 Subject: clocksource: make CLOCKSOURCE_OF_DECLARE type safe This ensures that a function pointer passed into CLOCKSOURCE_OF_DECLARE takes the same arguments that we use for calling that function later. Also fix the extraneous semicolon at end of the CLOCKSOURCE_OF_DECLARE definition. Signed-off-by: Arnd Bergmann Acked-by: Rob Herring --- drivers/clocksource/clksrc-of.c | 3 ++- drivers/clocksource/vt8500_timer.c | 2 +- include/linux/clocksource.h | 11 +++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c index 3ef11fba781..37f5325bec9 100644 --- a/drivers/clocksource/clksrc-of.c +++ b/drivers/clocksource/clksrc-of.c @@ -16,6 +16,7 @@ #include #include +#include extern struct of_device_id __clksrc_of_table[]; @@ -26,7 +27,7 @@ void __init clocksource_of_init(void) { struct device_node *np; const struct of_device_id *match; - void (*init_func)(struct device_node *); + clocksource_of_init_fn init_func; for_each_matching_node_and_match(np, __clksrc_of_table, &match) { init_func = match->data; diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c index 24225528559..64f553f04fa 100644 --- a/drivers/clocksource/vt8500_timer.c +++ b/drivers/clocksource/vt8500_timer.c @@ -165,4 +165,4 @@ static void __init vt8500_timer_init(struct device_node *np) 4, 0xf0000000); } -CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init) +CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init); diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 08ed5e19d8c..ac33184b14f 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -332,16 +332,23 @@ extern int clocksource_mmio_init(void __iomem *, const char *, extern int clocksource_i8253_init(void); +struct device_node; +typedef void(*clocksource_of_init_fn)(struct device_node *); #ifdef CONFIG_CLKSRC_OF extern void clocksource_of_init(void); #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ static const struct of_device_id __clksrc_of_table_##name \ __used __section(__clksrc_of_table) \ - = { .compatible = compat, .data = fn }; + = { .compatible = compat, \ + .data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn } #else static inline void clocksource_of_init(void) {} -#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) +#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ + static const struct of_device_id __clksrc_of_table_##name \ + __unused __section(__clksrc_of_table) \ + = { .compatible = compat, \ + .data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn } #endif #endif /* _LINUX_CLOCKSOURCE_H */ -- cgit v1.2.3-70-g09d2 From a9a0fef779074838230e04a322fd2bdc921f4f4f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Mar 2013 13:22:19 +1030 Subject: virtio_ring: expose virtio barriers for use in vringh. The host side of ring needs this logic too. Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 33 +++++-------------------- include/linux/virtio_ring.h | 57 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index ffd7e7da5d3..245177c286a 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -24,27 +24,6 @@ #include #include -/* virtio guest is communicating with a virtual "device" that actually runs on - * a host processor. Memory barriers are used to control SMP effects. */ -#ifdef CONFIG_SMP -/* Where possible, use SMP barriers which are more lightweight than mandatory - * barriers, because mandatory barriers control MMIO effects on accesses - * through relaxed memory I/O windows (which virtio-pci does not use). */ -#define virtio_mb(vq) \ - do { if ((vq)->weak_barriers) smp_mb(); else mb(); } while(0) -#define virtio_rmb(vq) \ - do { if ((vq)->weak_barriers) smp_rmb(); else rmb(); } while(0) -#define virtio_wmb(vq) \ - do { if ((vq)->weak_barriers) smp_wmb(); else wmb(); } while(0) -#else -/* We must force memory ordering even if guest is UP since host could be - * running on another CPU, but SMP barriers are defined to barrier() in that - * configuration. So fall back to mandatory barriers instead. */ -#define virtio_mb(vq) mb() -#define virtio_rmb(vq) rmb() -#define virtio_wmb(vq) wmb() -#endif - #ifdef DEBUG /* For development, we want to crash whenever the ring is screwed. */ #define BAD_RING(_vq, fmt, args...) \ @@ -276,7 +255,7 @@ add_head: /* Descriptors and available array need to be set before we expose the * new available array entries. */ - virtio_wmb(vq); + virtio_wmb(vq->weak_barriers); vq->vring.avail->idx++; vq->num_added++; @@ -312,7 +291,7 @@ bool virtqueue_kick_prepare(struct virtqueue *_vq) START_USE(vq); /* We need to expose available array entries before checking avail * event. */ - virtio_mb(vq); + virtio_mb(vq->weak_barriers); old = vq->vring.avail->idx - vq->num_added; new = vq->vring.avail->idx; @@ -436,7 +415,7 @@ void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len) } /* Only get used array entries after they have been exposed by host. */ - virtio_rmb(vq); + virtio_rmb(vq->weak_barriers); last_used = (vq->last_used_idx & (vq->vring.num - 1)); i = vq->vring.used->ring[last_used].id; @@ -460,7 +439,7 @@ void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len) * the read in the next get_buf call. */ if (!(vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT)) { vring_used_event(&vq->vring) = vq->last_used_idx; - virtio_mb(vq); + virtio_mb(vq->weak_barriers); } #ifdef DEBUG @@ -513,7 +492,7 @@ bool virtqueue_enable_cb(struct virtqueue *_vq) * entry. Always do both to keep code simple. */ vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; vring_used_event(&vq->vring) = vq->last_used_idx; - virtio_mb(vq); + virtio_mb(vq->weak_barriers); if (unlikely(more_used(vq))) { END_USE(vq); return false; @@ -553,7 +532,7 @@ bool virtqueue_enable_cb_delayed(struct virtqueue *_vq) /* TODO: tune this threshold */ bufs = (u16)(vq->vring.avail->idx - vq->last_used_idx) * 3 / 4; vring_used_event(&vq->vring) = vq->last_used_idx + bufs; - virtio_mb(vq); + virtio_mb(vq->weak_barriers); if (unlikely((u16)(vq->vring.used->idx - vq->last_used_idx) > bufs)) { END_USE(vq); return false; diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h index 63c6ea19951..ca3ad41c2c8 100644 --- a/include/linux/virtio_ring.h +++ b/include/linux/virtio_ring.h @@ -4,6 +4,63 @@ #include #include +/* + * Barriers in virtio are tricky. Non-SMP virtio guests can't assume + * they're not on an SMP host system, so they need to assume real + * barriers. Non-SMP virtio hosts could skip the barriers, but does + * anyone care? + * + * For virtio_pci on SMP, we don't need to order with respect to MMIO + * accesses through relaxed memory I/O windows, so smp_mb() et al are + * sufficient. + * + * For using virtio to talk to real devices (eg. other heterogeneous + * CPUs) we do need real barriers. In theory, we could be using both + * kinds of virtio, so it's a runtime decision, and the branch is + * actually quite cheap. + */ + +#ifdef CONFIG_SMP +static inline void virtio_mb(bool weak_barriers) +{ + if (weak_barriers) + smp_mb(); + else + mb(); +} + +static inline void virtio_rmb(bool weak_barriers) +{ + if (weak_barriers) + smp_rmb(); + else + rmb(); +} + +static inline void virtio_wmb(bool weak_barriers) +{ + if (weak_barriers) + smp_wmb(); + else + wmb(); +} +#else +static inline void virtio_mb(bool weak_barriers) +{ + mb(); +} + +static inline void virtio_rmb(bool weak_barriers) +{ + rmb(); +} + +static inline void virtio_wmb(bool weak_barriers) +{ + wmb(); +} +#endif + struct virtio_device; struct virtqueue; -- cgit v1.2.3-70-g09d2 From f87d0fbb579818fed3eeb0923cc253163ab93039 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Mar 2013 13:50:14 +1030 Subject: vringh: host-side implementation of virtio rings. Getting use of virtio rings correct is tricky, and a recent patch saw an implementation of in-kernel rings (as separate from userspace). This abstracts the business of dealing with the virtio ring layout from the access (userspace or direct); to do this, we use function pointers, which gcc inlines correctly. Signed-off-by: Rusty Russell Acked-by: Michael S. Tsirkin --- drivers/Makefile | 2 +- drivers/vhost/Kconfig | 8 + drivers/vhost/Kconfig.tcm | 1 + drivers/vhost/Makefile | 2 + drivers/vhost/vringh.c | 1007 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/vringh.h | 196 +++++++++ 6 files changed, 1215 insertions(+), 1 deletion(-) create mode 100644 drivers/vhost/vringh.c create mode 100644 include/linux/vringh.h (limited to 'include') diff --git a/drivers/Makefile b/drivers/Makefile index dce39a95fa7..72d28d34ee2 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -123,7 +123,7 @@ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ obj-$(CONFIG_BCMA) += bcma/ -obj-$(CONFIG_VHOST_NET) += vhost/ +obj-$(CONFIG_VHOST_RING) += vhost/ obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_STAGING) += staging/ obj-y += platform/ diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig index bf243177ffe..85b773a93a5 100644 --- a/drivers/vhost/Kconfig +++ b/drivers/vhost/Kconfig @@ -1,6 +1,7 @@ config VHOST_NET tristate "Host kernel accelerator for virtio net" depends on NET && EVENTFD && (TUN || !TUN) && (MACVTAP || !MACVTAP) + select VHOST_RING ---help--- This kernel module can be loaded in host kernel to accelerate guest networking with virtio_net. Not to be confused with virtio_net @@ -12,3 +13,10 @@ config VHOST_NET if STAGING source "drivers/vhost/Kconfig.tcm" endif + +config VHOST_RING + tristate + ---help--- + This option is selected by any driver which needs to access + the host side of a virtio ring. + diff --git a/drivers/vhost/Kconfig.tcm b/drivers/vhost/Kconfig.tcm index 7e3aa28d999..c3a8cfa1de7 100644 --- a/drivers/vhost/Kconfig.tcm +++ b/drivers/vhost/Kconfig.tcm @@ -1,6 +1,7 @@ config TCM_VHOST tristate "TCM_VHOST fabric module" depends on TARGET_CORE && EVENTFD && m + select VHOST_RING default n ---help--- Say M here to enable the TCM_VHOST fabric module for use with virtio-scsi guests diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile index a27b053bc9a..1d37f5e12be 100644 --- a/drivers/vhost/Makefile +++ b/drivers/vhost/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_VHOST_NET) += vhost_net.o vhost_net-y := vhost.o net.o obj-$(CONFIG_TCM_VHOST) += tcm_vhost.o + +obj-$(CONFIG_VHOST_RING) += vringh.o diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c new file mode 100644 index 00000000000..bff0775e258 --- /dev/null +++ b/drivers/vhost/vringh.c @@ -0,0 +1,1007 @@ +/* + * Helpers for the host side of a virtio ring. + * + * Since these may be in userspace, we use (inline) accessors. + */ +#include +#include +#include +#include +#include +#include +#include + +static __printf(1,2) __cold void vringh_bad(const char *fmt, ...) +{ + static DEFINE_RATELIMIT_STATE(vringh_rs, + DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + if (__ratelimit(&vringh_rs)) { + va_list ap; + va_start(ap, fmt); + printk(KERN_NOTICE "vringh:"); + vprintk(fmt, ap); + va_end(ap); + } +} + +/* Returns vring->num if empty, -ve on error. */ +static inline int __vringh_get_head(const struct vringh *vrh, + int (*getu16)(u16 *val, const u16 *p), + u16 *last_avail_idx) +{ + u16 avail_idx, i, head; + int err; + + err = getu16(&avail_idx, &vrh->vring.avail->idx); + if (err) { + vringh_bad("Failed to access avail idx at %p", + &vrh->vring.avail->idx); + return err; + } + + if (*last_avail_idx == avail_idx) + return vrh->vring.num; + + /* Only get avail ring entries after they have been exposed by guest. */ + virtio_rmb(vrh->weak_barriers); + + i = *last_avail_idx & (vrh->vring.num - 1); + + err = getu16(&head, &vrh->vring.avail->ring[i]); + if (err) { + vringh_bad("Failed to read head: idx %d address %p", + *last_avail_idx, &vrh->vring.avail->ring[i]); + return err; + } + + if (head >= vrh->vring.num) { + vringh_bad("Guest says index %u > %u is available", + head, vrh->vring.num); + return -EINVAL; + } + + (*last_avail_idx)++; + return head; +} + +/* Copy some bytes to/from the iovec. Returns num copied. */ +static inline ssize_t vringh_iov_xfer(struct vringh_kiov *iov, + void *ptr, size_t len, + int (*xfer)(void *addr, void *ptr, + size_t len)) +{ + int err, done = 0; + + while (len && iov->i < iov->used) { + size_t partlen; + + partlen = min(iov->iov[iov->i].iov_len, len); + err = xfer(iov->iov[iov->i].iov_base, ptr, partlen); + if (err) + return err; + done += partlen; + len -= partlen; + ptr += partlen; + iov->consumed += partlen; + iov->iov[iov->i].iov_len -= partlen; + iov->iov[iov->i].iov_base += partlen; + + if (!iov->iov[iov->i].iov_len) { + /* Fix up old iov element then increment. */ + iov->iov[iov->i].iov_len = iov->consumed; + iov->iov[iov->i].iov_base -= iov->consumed; + + iov->consumed = 0; + iov->i++; + } + } + return done; +} + +/* May reduce *len if range is shorter. */ +static inline bool range_check(struct vringh *vrh, u64 addr, size_t *len, + struct vringh_range *range, + bool (*getrange)(struct vringh *, + u64, struct vringh_range *)) +{ + if (addr < range->start || addr > range->end_incl) { + if (!getrange(vrh, addr, range)) + return false; + } + BUG_ON(addr < range->start || addr > range->end_incl); + + /* To end of memory? */ + if (unlikely(addr + *len == 0)) { + if (range->end_incl == -1ULL) + return true; + goto truncate; + } + + /* Otherwise, don't wrap. */ + if (addr + *len < addr) { + vringh_bad("Wrapping descriptor %zu@0x%llx", + *len, (unsigned long long)addr); + return false; + } + + if (unlikely(addr + *len - 1 > range->end_incl)) + goto truncate; + return true; + +truncate: + *len = range->end_incl + 1 - addr; + return true; +} + +static inline bool no_range_check(struct vringh *vrh, u64 addr, size_t *len, + struct vringh_range *range, + bool (*getrange)(struct vringh *, + u64, struct vringh_range *)) +{ + return true; +} + +/* No reason for this code to be inline. */ +static int move_to_indirect(int *up_next, u16 *i, void *addr, + const struct vring_desc *desc, + struct vring_desc **descs, int *desc_max) +{ + /* Indirect tables can't have indirect. */ + if (*up_next != -1) { + vringh_bad("Multilevel indirect %u->%u", *up_next, *i); + return -EINVAL; + } + + if (unlikely(desc->len % sizeof(struct vring_desc))) { + vringh_bad("Strange indirect len %u", desc->len); + return -EINVAL; + } + + /* We will check this when we follow it! */ + if (desc->flags & VRING_DESC_F_NEXT) + *up_next = desc->next; + else + *up_next = -2; + *descs = addr; + *desc_max = desc->len / sizeof(struct vring_desc); + + /* Now, start at the first indirect. */ + *i = 0; + return 0; +} + +static int resize_iovec(struct vringh_kiov *iov, gfp_t gfp) +{ + struct kvec *new; + unsigned int flag, new_num = (iov->max_num & ~VRINGH_IOV_ALLOCATED) * 2; + + if (new_num < 8) + new_num = 8; + + flag = (iov->max_num & VRINGH_IOV_ALLOCATED); + if (flag) + new = krealloc(iov->iov, new_num * sizeof(struct iovec), gfp); + else { + new = kmalloc(new_num * sizeof(struct iovec), gfp); + if (new) { + memcpy(new, iov->iov, + iov->max_num * sizeof(struct iovec)); + flag = VRINGH_IOV_ALLOCATED; + } + } + if (!new) + return -ENOMEM; + iov->iov = new; + iov->max_num = (new_num | flag); + return 0; +} + +static u16 __cold return_from_indirect(const struct vringh *vrh, int *up_next, + struct vring_desc **descs, int *desc_max) +{ + u16 i = *up_next; + + *up_next = -1; + *descs = vrh->vring.desc; + *desc_max = vrh->vring.num; + return i; +} + +static int slow_copy(struct vringh *vrh, void *dst, const void *src, + bool (*rcheck)(struct vringh *vrh, u64 addr, size_t *len, + struct vringh_range *range, + bool (*getrange)(struct vringh *vrh, + u64, + struct vringh_range *)), + bool (*getrange)(struct vringh *vrh, + u64 addr, + struct vringh_range *r), + struct vringh_range *range, + int (*copy)(void *dst, const void *src, size_t len)) +{ + size_t part, len = sizeof(struct vring_desc); + + do { + u64 addr; + int err; + + part = len; + addr = (u64)(unsigned long)src - range->offset; + + if (!rcheck(vrh, addr, &part, range, getrange)) + return -EINVAL; + + err = copy(dst, src, part); + if (err) + return err; + + dst += part; + src += part; + len -= part; + } while (len); + return 0; +} + +static inline int +__vringh_iov(struct vringh *vrh, u16 i, + struct vringh_kiov *riov, + struct vringh_kiov *wiov, + bool (*rcheck)(struct vringh *vrh, u64 addr, size_t *len, + struct vringh_range *range, + bool (*getrange)(struct vringh *, u64, + struct vringh_range *)), + bool (*getrange)(struct vringh *, u64, struct vringh_range *), + gfp_t gfp, + int (*copy)(void *dst, const void *src, size_t len)) +{ + int err, count = 0, up_next, desc_max; + struct vring_desc desc, *descs; + struct vringh_range range = { -1ULL, 0 }, slowrange; + bool slow = false; + + /* We start traversing vring's descriptor table. */ + descs = vrh->vring.desc; + desc_max = vrh->vring.num; + up_next = -1; + + if (riov) + riov->i = riov->used = 0; + else if (wiov) + wiov->i = wiov->used = 0; + else + /* You must want something! */ + BUG(); + + for (;;) { + void *addr; + struct vringh_kiov *iov; + size_t len; + + if (unlikely(slow)) + err = slow_copy(vrh, &desc, &descs[i], rcheck, getrange, + &slowrange, copy); + else + err = copy(&desc, &descs[i], sizeof(desc)); + if (unlikely(err)) + goto fail; + + if (unlikely(desc.flags & VRING_DESC_F_INDIRECT)) { + /* Make sure it's OK, and get offset. */ + len = desc.len; + if (!rcheck(vrh, desc.addr, &len, &range, getrange)) { + err = -EINVAL; + goto fail; + } + + if (unlikely(len != desc.len)) { + slow = true; + /* We need to save this range to use offset */ + slowrange = range; + } + + addr = (void *)(long)(desc.addr + range.offset); + err = move_to_indirect(&up_next, &i, addr, &desc, + &descs, &desc_max); + if (err) + goto fail; + continue; + } + + if (count++ == vrh->vring.num) { + vringh_bad("Descriptor loop in %p", descs); + err = -ELOOP; + goto fail; + } + + if (desc.flags & VRING_DESC_F_WRITE) + iov = wiov; + else { + iov = riov; + if (unlikely(wiov && wiov->i)) { + vringh_bad("Readable desc %p after writable", + &descs[i]); + err = -EINVAL; + goto fail; + } + } + + if (!iov) { + vringh_bad("Unexpected %s desc", + !wiov ? "writable" : "readable"); + err = -EPROTO; + goto fail; + } + + again: + /* Make sure it's OK, and get offset. */ + len = desc.len; + if (!rcheck(vrh, desc.addr, &len, &range, getrange)) { + err = -EINVAL; + goto fail; + } + addr = (void *)(unsigned long)(desc.addr + range.offset); + + if (unlikely(iov->used == (iov->max_num & ~VRINGH_IOV_ALLOCATED))) { + err = resize_iovec(iov, gfp); + if (err) + goto fail; + } + + iov->iov[iov->used].iov_base = addr; + iov->iov[iov->used].iov_len = len; + iov->used++; + + if (unlikely(len != desc.len)) { + desc.len -= len; + desc.addr += len; + goto again; + } + + if (desc.flags & VRING_DESC_F_NEXT) { + i = desc.next; + } else { + /* Just in case we need to finish traversing above. */ + if (unlikely(up_next > 0)) { + i = return_from_indirect(vrh, &up_next, + &descs, &desc_max); + slow = false; + } else + break; + } + + if (i >= desc_max) { + vringh_bad("Chained index %u > %u", i, desc_max); + err = -EINVAL; + goto fail; + } + } + + return 0; + +fail: + return err; +} + +static inline int __vringh_complete(struct vringh *vrh, + const struct vring_used_elem *used, + unsigned int num_used, + int (*putu16)(u16 *p, u16 val), + int (*putused)(struct vring_used_elem *dst, + const struct vring_used_elem + *src, unsigned num)) +{ + struct vring_used *used_ring; + int err; + u16 used_idx, off; + + used_ring = vrh->vring.used; + used_idx = vrh->last_used_idx + vrh->completed; + + off = used_idx % vrh->vring.num; + + /* Compiler knows num_used == 1 sometimes, hence extra check */ + if (num_used > 1 && unlikely(off + num_used >= vrh->vring.num)) { + u16 part = vrh->vring.num - off; + err = putused(&used_ring->ring[off], used, part); + if (!err) + err = putused(&used_ring->ring[0], used + part, + num_used - part); + } else + err = putused(&used_ring->ring[off], used, num_used); + + if (err) { + vringh_bad("Failed to write %u used entries %u at %p", + num_used, off, &used_ring->ring[off]); + return err; + } + + /* Make sure buffer is written before we update index. */ + virtio_wmb(vrh->weak_barriers); + + err = putu16(&vrh->vring.used->idx, used_idx + num_used); + if (err) { + vringh_bad("Failed to update used index at %p", + &vrh->vring.used->idx); + return err; + } + + vrh->completed += num_used; + return 0; +} + + +static inline int __vringh_need_notify(struct vringh *vrh, + int (*getu16)(u16 *val, const u16 *p)) +{ + bool notify; + u16 used_event; + int err; + + /* Flush out used index update. This is paired with the + * barrier that the Guest executes when enabling + * interrupts. */ + virtio_mb(vrh->weak_barriers); + + /* Old-style, without event indices. */ + if (!vrh->event_indices) { + u16 flags; + err = getu16(&flags, &vrh->vring.avail->flags); + if (err) { + vringh_bad("Failed to get flags at %p", + &vrh->vring.avail->flags); + return err; + } + return (!(flags & VRING_AVAIL_F_NO_INTERRUPT)); + } + + /* Modern: we know when other side wants to know. */ + err = getu16(&used_event, &vring_used_event(&vrh->vring)); + if (err) { + vringh_bad("Failed to get used event idx at %p", + &vring_used_event(&vrh->vring)); + return err; + } + + /* Just in case we added so many that we wrap. */ + if (unlikely(vrh->completed > 0xffff)) + notify = true; + else + notify = vring_need_event(used_event, + vrh->last_used_idx + vrh->completed, + vrh->last_used_idx); + + vrh->last_used_idx += vrh->completed; + vrh->completed = 0; + return notify; +} + +static inline bool __vringh_notify_enable(struct vringh *vrh, + int (*getu16)(u16 *val, const u16 *p), + int (*putu16)(u16 *p, u16 val)) +{ + u16 avail; + + if (!vrh->event_indices) { + /* Old-school; update flags. */ + if (putu16(&vrh->vring.used->flags, 0) != 0) { + vringh_bad("Clearing used flags %p", + &vrh->vring.used->flags); + return true; + } + } else { + if (putu16(&vring_avail_event(&vrh->vring), + vrh->last_avail_idx) != 0) { + vringh_bad("Updating avail event index %p", + &vring_avail_event(&vrh->vring)); + return true; + } + } + + /* They could have slipped one in as we were doing that: make + * sure it's written, then check again. */ + virtio_mb(vrh->weak_barriers); + + if (getu16(&avail, &vrh->vring.avail->idx) != 0) { + vringh_bad("Failed to check avail idx at %p", + &vrh->vring.avail->idx); + return true; + } + + /* This is unlikely, so we just leave notifications enabled + * (if we're using event_indices, we'll only get one + * notification anyway). */ + return avail == vrh->last_avail_idx; +} + +static inline void __vringh_notify_disable(struct vringh *vrh, + int (*putu16)(u16 *p, u16 val)) +{ + if (!vrh->event_indices) { + /* Old-school; update flags. */ + if (putu16(&vrh->vring.used->flags, VRING_USED_F_NO_NOTIFY)) { + vringh_bad("Setting used flags %p", + &vrh->vring.used->flags); + } + } +} + +/* Userspace access helpers: in this case, addresses are really userspace. */ +static inline int getu16_user(u16 *val, const u16 *p) +{ + return get_user(*val, (__force u16 __user *)p); +} + +static inline int putu16_user(u16 *p, u16 val) +{ + return put_user(val, (__force u16 __user *)p); +} + +static inline int copydesc_user(void *dst, const void *src, size_t len) +{ + return copy_from_user(dst, (__force void __user *)src, len) ? + -EFAULT : 0; +} + +static inline int putused_user(struct vring_used_elem *dst, + const struct vring_used_elem *src, + unsigned int num) +{ + return copy_to_user((__force void __user *)dst, src, + sizeof(*dst) * num) ? -EFAULT : 0; +} + +static inline int xfer_from_user(void *src, void *dst, size_t len) +{ + return copy_from_user(dst, (__force void __user *)src, len) ? + -EFAULT : 0; +} + +static inline int xfer_to_user(void *dst, void *src, size_t len) +{ + return copy_to_user((__force void __user *)dst, src, len) ? + -EFAULT : 0; +} + +/** + * vringh_init_user - initialize a vringh for a userspace vring. + * @vrh: the vringh to initialize. + * @features: the feature bits for this ring. + * @num: the number of elements. + * @weak_barriers: true if we only need memory barriers, not I/O. + * @desc: the userpace descriptor pointer. + * @avail: the userpace avail pointer. + * @used: the userpace used pointer. + * + * Returns an error if num is invalid: you should check pointers + * yourself! + */ +int vringh_init_user(struct vringh *vrh, u32 features, + unsigned int num, bool weak_barriers, + struct vring_desc __user *desc, + struct vring_avail __user *avail, + struct vring_used __user *used) +{ + /* Sane power of 2 please! */ + if (!num || num > 0xffff || (num & (num - 1))) { + vringh_bad("Bad ring size %u", num); + return -EINVAL; + } + + vrh->event_indices = (features & (1 << VIRTIO_RING_F_EVENT_IDX)); + vrh->weak_barriers = weak_barriers; + vrh->completed = 0; + vrh->last_avail_idx = 0; + vrh->last_used_idx = 0; + vrh->vring.num = num; + /* vring expects kernel addresses, but only used via accessors. */ + vrh->vring.desc = (__force struct vring_desc *)desc; + vrh->vring.avail = (__force struct vring_avail *)avail; + vrh->vring.used = (__force struct vring_used *)used; + return 0; +} +EXPORT_SYMBOL(vringh_init_user); + +/** + * vringh_getdesc_user - get next available descriptor from userspace ring. + * @vrh: the userspace vring. + * @riov: where to put the readable descriptors (or NULL) + * @wiov: where to put the writable descriptors (or NULL) + * @getrange: function to call to check ranges. + * @head: head index we received, for passing to vringh_complete_user(). + * + * Returns 0 if there was no descriptor, 1 if there was, or -errno. + * + * Note that on error return, you can tell the difference between an + * invalid ring and a single invalid descriptor: in the former case, + * *head will be vrh->vring.num. You may be able to ignore an invalid + * descriptor, but there's not much you can do with an invalid ring. + * + * Note that you may need to clean up riov and wiov, even on error! + */ +int vringh_getdesc_user(struct vringh *vrh, + struct vringh_iov *riov, + struct vringh_iov *wiov, + bool (*getrange)(struct vringh *vrh, + u64 addr, struct vringh_range *r), + u16 *head) +{ + int err; + + *head = vrh->vring.num; + err = __vringh_get_head(vrh, getu16_user, &vrh->last_avail_idx); + if (err < 0) + return err; + + /* Empty... */ + if (err == vrh->vring.num) + return 0; + + /* We need the layouts to be the identical for this to work */ + BUILD_BUG_ON(sizeof(struct vringh_kiov) != sizeof(struct vringh_iov)); + BUILD_BUG_ON(offsetof(struct vringh_kiov, iov) != + offsetof(struct vringh_iov, iov)); + BUILD_BUG_ON(offsetof(struct vringh_kiov, i) != + offsetof(struct vringh_iov, i)); + BUILD_BUG_ON(offsetof(struct vringh_kiov, used) != + offsetof(struct vringh_iov, used)); + BUILD_BUG_ON(offsetof(struct vringh_kiov, max_num) != + offsetof(struct vringh_iov, max_num)); + BUILD_BUG_ON(sizeof(struct iovec) != sizeof(struct kvec)); + BUILD_BUG_ON(offsetof(struct iovec, iov_base) != + offsetof(struct kvec, iov_base)); + BUILD_BUG_ON(offsetof(struct iovec, iov_len) != + offsetof(struct kvec, iov_len)); + BUILD_BUG_ON(sizeof(((struct iovec *)NULL)->iov_base) + != sizeof(((struct kvec *)NULL)->iov_base)); + BUILD_BUG_ON(sizeof(((struct iovec *)NULL)->iov_len) + != sizeof(((struct kvec *)NULL)->iov_len)); + + *head = err; + err = __vringh_iov(vrh, *head, (struct vringh_kiov *)riov, + (struct vringh_kiov *)wiov, + range_check, getrange, GFP_KERNEL, copydesc_user); + if (err) + return err; + + return 1; +} +EXPORT_SYMBOL(vringh_getdesc_user); + +/** + * vringh_iov_pull_user - copy bytes from vring_iov. + * @riov: the riov as passed to vringh_getdesc_user() (updated as we consume) + * @dst: the place to copy. + * @len: the maximum length to copy. + * + * Returns the bytes copied <= len or a negative errno. + */ +ssize_t vringh_iov_pull_user(struct vringh_iov *riov, void *dst, size_t len) +{ + return vringh_iov_xfer((struct vringh_kiov *)riov, + dst, len, xfer_from_user); +} +EXPORT_SYMBOL(vringh_iov_pull_user); + +/** + * vringh_iov_push_user - copy bytes into vring_iov. + * @wiov: the wiov as passed to vringh_getdesc_user() (updated as we consume) + * @dst: the place to copy. + * @len: the maximum length to copy. + * + * Returns the bytes copied <= len or a negative errno. + */ +ssize_t vringh_iov_push_user(struct vringh_iov *wiov, + const void *src, size_t len) +{ + return vringh_iov_xfer((struct vringh_kiov *)wiov, + (void *)src, len, xfer_to_user); +} +EXPORT_SYMBOL(vringh_iov_push_user); + +/** + * vringh_abandon_user - we've decided not to handle the descriptor(s). + * @vrh: the vring. + * @num: the number of descriptors to put back (ie. num + * vringh_get_user() to undo). + * + * The next vringh_get_user() will return the old descriptor(s) again. + */ +void vringh_abandon_user(struct vringh *vrh, unsigned int num) +{ + /* We only update vring_avail_event(vr) when we want to be notified, + * so we haven't changed that yet. */ + vrh->last_avail_idx -= num; +} +EXPORT_SYMBOL(vringh_abandon_user); + +/** + * vringh_complete_user - we've finished with descriptor, publish it. + * @vrh: the vring. + * @head: the head as filled in by vringh_getdesc_user. + * @len: the length of data we have written. + * + * You should check vringh_need_notify_user() after one or more calls + * to this function. + */ +int vringh_complete_user(struct vringh *vrh, u16 head, u32 len) +{ + struct vring_used_elem used; + + used.id = head; + used.len = len; + return __vringh_complete(vrh, &used, 1, putu16_user, putused_user); +} +EXPORT_SYMBOL(vringh_complete_user); + +/** + * vringh_complete_multi_user - we've finished with many descriptors. + * @vrh: the vring. + * @used: the head, length pairs. + * @num_used: the number of used elements. + * + * You should check vringh_need_notify_user() after one or more calls + * to this function. + */ +int vringh_complete_multi_user(struct vringh *vrh, + const struct vring_used_elem used[], + unsigned num_used) +{ + return __vringh_complete(vrh, used, num_used, + putu16_user, putused_user); +} +EXPORT_SYMBOL(vringh_complete_multi_user); + +/** + * vringh_notify_enable_user - we want to know if something changes. + * @vrh: the vring. + * + * This always enables notifications, but returns false if there are + * now more buffers available in the vring. + */ +bool vringh_notify_enable_user(struct vringh *vrh) +{ + return __vringh_notify_enable(vrh, getu16_user, putu16_user); +} +EXPORT_SYMBOL(vringh_notify_enable_user); + +/** + * vringh_notify_disable_user - don't tell us if something changes. + * @vrh: the vring. + * + * This is our normal running state: we disable and then only enable when + * we're going to sleep. + */ +void vringh_notify_disable_user(struct vringh *vrh) +{ + __vringh_notify_disable(vrh, putu16_user); +} +EXPORT_SYMBOL(vringh_notify_disable_user); + +/** + * vringh_need_notify_user - must we tell the other side about used buffers? + * @vrh: the vring we've called vringh_complete_user() on. + * + * Returns -errno or 0 if we don't need to tell the other side, 1 if we do. + */ +int vringh_need_notify_user(struct vringh *vrh) +{ + return __vringh_need_notify(vrh, getu16_user); +} +EXPORT_SYMBOL(vringh_need_notify_user); + +/* Kernelspace access helpers. */ +static inline int getu16_kern(u16 *val, const u16 *p) +{ + *val = ACCESS_ONCE(*p); + return 0; +} + +static inline int putu16_kern(u16 *p, u16 val) +{ + ACCESS_ONCE(*p) = val; + return 0; +} + +static inline int copydesc_kern(void *dst, const void *src, size_t len) +{ + memcpy(dst, src, len); + return 0; +} + +static inline int putused_kern(struct vring_used_elem *dst, + const struct vring_used_elem *src, + unsigned int num) +{ + memcpy(dst, src, num * sizeof(*dst)); + return 0; +} + +static inline int xfer_kern(void *src, void *dst, size_t len) +{ + memcpy(dst, src, len); + return 0; +} + +/** + * vringh_init_kern - initialize a vringh for a kernelspace vring. + * @vrh: the vringh to initialize. + * @features: the feature bits for this ring. + * @num: the number of elements. + * @weak_barriers: true if we only need memory barriers, not I/O. + * @desc: the userpace descriptor pointer. + * @avail: the userpace avail pointer. + * @used: the userpace used pointer. + * + * Returns an error if num is invalid. + */ +int vringh_init_kern(struct vringh *vrh, u32 features, + unsigned int num, bool weak_barriers, + struct vring_desc *desc, + struct vring_avail *avail, + struct vring_used *used) +{ + /* Sane power of 2 please! */ + if (!num || num > 0xffff || (num & (num - 1))) { + vringh_bad("Bad ring size %u", num); + return -EINVAL; + } + + vrh->event_indices = (features & (1 << VIRTIO_RING_F_EVENT_IDX)); + vrh->weak_barriers = weak_barriers; + vrh->completed = 0; + vrh->last_avail_idx = 0; + vrh->last_used_idx = 0; + vrh->vring.num = num; + vrh->vring.desc = desc; + vrh->vring.avail = avail; + vrh->vring.used = used; + return 0; +} +EXPORT_SYMBOL(vringh_init_kern); + +/** + * vringh_getdesc_kern - get next available descriptor from kernelspace ring. + * @vrh: the kernelspace vring. + * @riov: where to put the readable descriptors (or NULL) + * @wiov: where to put the writable descriptors (or NULL) + * @head: head index we received, for passing to vringh_complete_kern(). + * @gfp: flags for allocating larger riov/wiov. + * + * Returns 0 if there was no descriptor, 1 if there was, or -errno. + * + * Note that on error return, you can tell the difference between an + * invalid ring and a single invalid descriptor: in the former case, + * *head will be vrh->vring.num. You may be able to ignore an invalid + * descriptor, but there's not much you can do with an invalid ring. + * + * Note that you may need to clean up riov and wiov, even on error! + */ +int vringh_getdesc_kern(struct vringh *vrh, + struct vringh_kiov *riov, + struct vringh_kiov *wiov, + u16 *head, + gfp_t gfp) +{ + int err; + + err = __vringh_get_head(vrh, getu16_kern, &vrh->last_avail_idx); + if (err < 0) + return err; + + /* Empty... */ + if (err == vrh->vring.num) + return 0; + + *head = err; + err = __vringh_iov(vrh, *head, riov, wiov, no_range_check, NULL, + gfp, copydesc_kern); + if (err) + return err; + + return 1; +} +EXPORT_SYMBOL(vringh_getdesc_kern); + +/** + * vringh_iov_pull_kern - copy bytes from vring_iov. + * @riov: the riov as passed to vringh_getdesc_kern() (updated as we consume) + * @dst: the place to copy. + * @len: the maximum length to copy. + * + * Returns the bytes copied <= len or a negative errno. + */ +ssize_t vringh_iov_pull_kern(struct vringh_kiov *riov, void *dst, size_t len) +{ + return vringh_iov_xfer(riov, dst, len, xfer_kern); +} +EXPORT_SYMBOL(vringh_iov_pull_kern); + +/** + * vringh_iov_push_kern - copy bytes into vring_iov. + * @wiov: the wiov as passed to vringh_getdesc_kern() (updated as we consume) + * @dst: the place to copy. + * @len: the maximum length to copy. + * + * Returns the bytes copied <= len or a negative errno. + */ +ssize_t vringh_iov_push_kern(struct vringh_kiov *wiov, + const void *src, size_t len) +{ + return vringh_iov_xfer(wiov, (void *)src, len, xfer_kern); +} +EXPORT_SYMBOL(vringh_iov_push_kern); + +/** + * vringh_abandon_kern - we've decided not to handle the descriptor(s). + * @vrh: the vring. + * @num: the number of descriptors to put back (ie. num + * vringh_get_kern() to undo). + * + * The next vringh_get_kern() will return the old descriptor(s) again. + */ +void vringh_abandon_kern(struct vringh *vrh, unsigned int num) +{ + /* We only update vring_avail_event(vr) when we want to be notified, + * so we haven't changed that yet. */ + vrh->last_avail_idx -= num; +} +EXPORT_SYMBOL(vringh_abandon_kern); + +/** + * vringh_complete_kern - we've finished with descriptor, publish it. + * @vrh: the vring. + * @head: the head as filled in by vringh_getdesc_kern. + * @len: the length of data we have written. + * + * You should check vringh_need_notify_kern() after one or more calls + * to this function. + */ +int vringh_complete_kern(struct vringh *vrh, u16 head, u32 len) +{ + struct vring_used_elem used; + + used.id = head; + used.len = len; + + return __vringh_complete(vrh, &used, 1, putu16_kern, putused_kern); +} +EXPORT_SYMBOL(vringh_complete_kern); + +/** + * vringh_notify_enable_kern - we want to know if something changes. + * @vrh: the vring. + * + * This always enables notifications, but returns false if there are + * now more buffers available in the vring. + */ +bool vringh_notify_enable_kern(struct vringh *vrh) +{ + return __vringh_notify_enable(vrh, getu16_kern, putu16_kern); +} +EXPORT_SYMBOL(vringh_notify_enable_kern); + +/** + * vringh_notify_disable_kern - don't tell us if something changes. + * @vrh: the vring. + * + * This is our normal running state: we disable and then only enable when + * we're going to sleep. + */ +void vringh_notify_disable_kern(struct vringh *vrh) +{ + __vringh_notify_disable(vrh, putu16_kern); +} +EXPORT_SYMBOL(vringh_notify_disable_kern); + +/** + * vringh_need_notify_kern - must we tell the other side about used buffers? + * @vrh: the vring we've called vringh_complete_kern() on. + * + * Returns -errno or 0 if we don't need to tell the other side, 1 if we do. + */ +int vringh_need_notify_kern(struct vringh *vrh) +{ + return __vringh_need_notify(vrh, getu16_kern); +} +EXPORT_SYMBOL(vringh_need_notify_kern); diff --git a/include/linux/vringh.h b/include/linux/vringh.h new file mode 100644 index 00000000000..b8f086625c4 --- /dev/null +++ b/include/linux/vringh.h @@ -0,0 +1,196 @@ +/* + * Linux host-side vring helpers; for when the kernel needs to access + * someone else's vring. + * + * Copyright IBM Corporation, 2013. + * Parts taken from drivers/vhost/vhost.c Copyright 2009 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Written by: Rusty Russell + */ +#ifndef _LINUX_VRINGH_H +#define _LINUX_VRINGH_H +#include +#include +#include +#include + +/* virtio_ring with information needed for host access. */ +struct vringh { + /* Guest publishes used event idx (note: we always do). */ + bool event_indices; + + /* Can we get away with weak barriers? */ + bool weak_barriers; + + /* Last available index we saw (ie. where we're up to). */ + u16 last_avail_idx; + + /* Last index we used. */ + u16 last_used_idx; + + /* How many descriptors we've completed since last need_notify(). */ + u32 completed; + + /* The vring (note: it may contain user pointers!) */ + struct vring vring; +}; + +/* The memory the vring can access, and what offset to apply. */ +struct vringh_range { + u64 start, end_incl; + u64 offset; +}; + +/** + * struct vringh_iov - iovec mangler. + * + * Mangles iovec in place, and restores it. + * Remaining data is iov + i, of used - i elements. + */ +struct vringh_iov { + struct iovec *iov; + size_t consumed; /* Within iov[i] */ + unsigned i, used, max_num; +}; + +/** + * struct vringh_iov - kvec mangler. + * + * Mangles kvec in place, and restores it. + * Remaining data is iov + i, of used - i elements. + */ +struct vringh_kiov { + struct kvec *iov; + size_t consumed; /* Within iov[i] */ + unsigned i, used, max_num; +}; + +/* Flag on max_num to indicate we're kmalloced. */ +#define VRINGH_IOV_ALLOCATED 0x8000000 + +/* Helpers for userspace vrings. */ +int vringh_init_user(struct vringh *vrh, u32 features, + unsigned int num, bool weak_barriers, + struct vring_desc __user *desc, + struct vring_avail __user *avail, + struct vring_used __user *used); + +static inline void vringh_iov_init(struct vringh_iov *iov, + struct iovec *iovec, unsigned num) +{ + iov->used = iov->i = 0; + iov->consumed = 0; + iov->max_num = num; + iov->iov = iovec; +} + +static inline void vringh_iov_reset(struct vringh_iov *iov) +{ + iov->iov[iov->i].iov_len += iov->consumed; + iov->iov[iov->i].iov_base -= iov->consumed; + iov->consumed = 0; + iov->i = 0; +} + +static inline void vringh_iov_cleanup(struct vringh_iov *iov) +{ + if (iov->max_num & VRINGH_IOV_ALLOCATED) + kfree(iov->iov); + iov->max_num = iov->used = iov->i = iov->consumed = 0; + iov->iov = NULL; +} + +/* Convert a descriptor into iovecs. */ +int vringh_getdesc_user(struct vringh *vrh, + struct vringh_iov *riov, + struct vringh_iov *wiov, + bool (*getrange)(struct vringh *vrh, + u64 addr, struct vringh_range *r), + u16 *head); + +/* Copy bytes from readable vsg, consuming it (and incrementing wiov->i). */ +ssize_t vringh_iov_pull_user(struct vringh_iov *riov, void *dst, size_t len); + +/* Copy bytes into writable vsg, consuming it (and incrementing wiov->i). */ +ssize_t vringh_iov_push_user(struct vringh_iov *wiov, + const void *src, size_t len); + +/* Mark a descriptor as used. */ +int vringh_complete_user(struct vringh *vrh, u16 head, u32 len); +int vringh_complete_multi_user(struct vringh *vrh, + const struct vring_used_elem used[], + unsigned num_used); + +/* Pretend we've never seen descriptor (for easy error handling). */ +void vringh_abandon_user(struct vringh *vrh, unsigned int num); + +/* Do we need to fire the eventfd to notify the other side? */ +int vringh_need_notify_user(struct vringh *vrh); + +bool vringh_notify_enable_user(struct vringh *vrh); +void vringh_notify_disable_user(struct vringh *vrh); + +/* Helpers for kernelspace vrings. */ +int vringh_init_kern(struct vringh *vrh, u32 features, + unsigned int num, bool weak_barriers, + struct vring_desc *desc, + struct vring_avail *avail, + struct vring_used *used); + +static inline void vringh_kiov_init(struct vringh_kiov *kiov, + struct kvec *kvec, unsigned num) +{ + kiov->used = kiov->i = 0; + kiov->consumed = 0; + kiov->max_num = num; + kiov->iov = kvec; +} + +static inline void vringh_kiov_reset(struct vringh_kiov *kiov) +{ + kiov->iov[kiov->i].iov_len += kiov->consumed; + kiov->iov[kiov->i].iov_base -= kiov->consumed; + kiov->consumed = 0; + kiov->i = 0; +} + +static inline void vringh_kiov_cleanup(struct vringh_kiov *kiov) +{ + if (kiov->max_num & VRINGH_IOV_ALLOCATED) + kfree(kiov->iov); + kiov->max_num = kiov->used = kiov->i = kiov->consumed = 0; + kiov->iov = NULL; +} + +int vringh_getdesc_kern(struct vringh *vrh, + struct vringh_kiov *riov, + struct vringh_kiov *wiov, + u16 *head, + gfp_t gfp); + +ssize_t vringh_iov_pull_kern(struct vringh_kiov *riov, void *dst, size_t len); +ssize_t vringh_iov_push_kern(struct vringh_kiov *wiov, + const void *src, size_t len); +void vringh_abandon_kern(struct vringh *vrh, unsigned int num); +int vringh_complete_kern(struct vringh *vrh, u16 head, u32 len); + +bool vringh_notify_enable_kern(struct vringh *vrh); +void vringh_notify_disable_kern(struct vringh *vrh); + +int vringh_need_notify_kern(struct vringh *vrh); + +#endif /* _LINUX_VRINGH_H */ -- cgit v1.2.3-70-g09d2 From 3beee86a4b9374e38dba36b44e81f1423a0d6b54 Mon Sep 17 00:00:00 2001 From: Sjur Brændeland Date: Wed, 20 Mar 2013 13:51:24 +1030 Subject: virtio: Introduce vringh wrappers in virtio_config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add wrappers for the host vrings to support loose coupling between the virtio device and driver. A new struct vringh_config_ops with the functions find_vrhs() and del_vrhs() is added to the virtio_device struct. This enables virtio drivers to manage virtio host rings without detailed knowledge of how the vrings are created and deleted. The function vringh_notify() is added so vringh clients can notify the other side that buffers are added to the used-ring. Cc: Ohad Ben-Cohen Signed-off-by: Sjur Brændeland Signed-off-by: Rusty Russell (constified vringh_config) --- include/linux/virtio.h | 3 +++ include/linux/vringh.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'include') diff --git a/include/linux/virtio.h b/include/linux/virtio.h index ff6714e6d0f..5d5b3abc283 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -8,6 +8,7 @@ #include #include #include +#include /** * virtqueue - a queue to register buffers for sending or receiving. @@ -70,6 +71,7 @@ static inline unsigned int virtqueue_get_queue_index(struct virtqueue *vq) * @dev: underlying device. * @id: the device type identification (used to match it with a driver). * @config: the configuration ops for this device. + * @vringh_config: configuration ops for host vrings. * @vqs: the list of virtqueues for this device. * @features: the features supported by both driver and device. * @priv: private pointer for the driver's use. @@ -79,6 +81,7 @@ struct virtio_device { struct device dev; struct virtio_device_id id; const struct virtio_config_ops *config; + const struct vringh_config_ops *vringh_config; struct list_head vqs; /* Note that this is a Linux set_bit-style bitmap. */ unsigned long features[1]; diff --git a/include/linux/vringh.h b/include/linux/vringh.h index b8f086625c4..749cde28728 100644 --- a/include/linux/vringh.h +++ b/include/linux/vringh.h @@ -47,6 +47,28 @@ struct vringh { /* The vring (note: it may contain user pointers!) */ struct vring vring; + + /* The function to call to notify the guest about added buffers */ + void (*notify)(struct vringh *); +}; + +/** + * struct vringh_config_ops - ops for creating a host vring from a virtio driver + * @find_vrhs: find the host vrings and instantiate them + * vdev: the virtio_device + * nhvrs: the number of host vrings to find + * hvrs: on success, includes new host vrings + * callbacks: array of driver callbacks, for each host vring + * include a NULL entry for vqs that do not need a callback + * Returns 0 on success or error status + * @del_vrhs: free the host vrings found by find_vrhs(). + */ +struct virtio_device; +typedef void vrh_callback_t(struct virtio_device *, struct vringh *); +struct vringh_config_ops { + int (*find_vrhs)(struct virtio_device *vdev, unsigned nhvrs, + struct vringh *vrhs[], vrh_callback_t *callbacks[]); + void (*del_vrhs)(struct virtio_device *vdev); }; /* The memory the vring can access, and what offset to apply. */ @@ -193,4 +215,11 @@ void vringh_notify_disable_kern(struct vringh *vrh); int vringh_need_notify_kern(struct vringh *vrh); +/* Notify the guest about buffers added to the used ring */ +static inline void vringh_notify(struct vringh *vrh) +{ + if (vrh->notify) + vrh->notify(vrh); +} + #endif /* _LINUX_VRINGH_H */ -- cgit v1.2.3-70-g09d2 From 0d2e1a2926b1839a4b74519e660739b2566c9386 Mon Sep 17 00:00:00 2001 From: Erwan Yvin Date: Wed, 20 Mar 2013 13:52:24 +1030 Subject: caif_virtio: Introduce caif over virtio Add the CAIF Virtio shared memory driver for talking to a modem. This CAIF Link layer communicates to the modem over shared memory. It is implemented as a virtio_driver. The underlying virtio device is managed by the remoteproc framework. The Virtio queue is used for transmitting data to the modem, and the new vringh is used for receiving data. Genalloc is used for managing the shared memory used for TX data. The default dma-alloc-coherent allocator can only allocate whole pages, and this wastes too much shared memory. Flow control is implemented by stopping the TX-queues if the virtio queues go full or we run out of memory. Queued are reopened when queues are below the watermark. NAPI is used in RX path, and a dedicated tasklet is used for releasing TX buffers. Signed-off-by: Erwan Yvin Acked-by: David S. Miller Signed-off-by: Rusty Russell (minor fixes) --- drivers/net/caif/Kconfig | 14 + drivers/net/caif/Makefile | 3 + drivers/net/caif/caif_virtio.c | 785 ++++++++++++++++++++++++++++++++++++++++ include/linux/virtio_caif.h | 24 ++ include/uapi/linux/virtio_ids.h | 1 + 5 files changed, 827 insertions(+) create mode 100644 drivers/net/caif/caif_virtio.c create mode 100644 include/linux/virtio_caif.h (limited to 'include') diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig index 60c2142373c..893f9154011 100644 --- a/drivers/net/caif/Kconfig +++ b/drivers/net/caif/Kconfig @@ -47,3 +47,17 @@ config CAIF_HSI The caif low level driver for CAIF over HSI. Be aware that if you enable this then you also need to enable a low-level HSI driver. + +config CAIF_VIRTIO + tristate "CAIF virtio transport driver" + depends on CAIF + select VHOST_RING + select VIRTIO + select GENERIC_ALLOCATOR + default n + ---help--- + The caif driver for CAIF over Virtio. + +if CAIF_VIRTIO +source "drivers/vhost/Kconfig" +endif diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile index 91dff861560..d9ee26a96c6 100644 --- a/drivers/net/caif/Makefile +++ b/drivers/net/caif/Makefile @@ -13,3 +13,6 @@ obj-$(CONFIG_CAIF_SHM) += caif_shm.o # HSI interface obj-$(CONFIG_CAIF_HSI) += caif_hsi.o + +# Virtio interface +obj-$(CONFIG_CAIF_VIRTIO) += caif_virtio.o diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c new file mode 100644 index 00000000000..b1e1205e4e2 --- /dev/null +++ b/drivers/net/caif/caif_virtio.c @@ -0,0 +1,785 @@ +/* + * Copyright (C) ST-Ericsson AB 2013 + * Authors: Vicram Arv / vikram.arv@stericsson.com, + * Dmitry Tarnyagin / dmitry.tarnyagin@stericsson.com + * Sjur Brendeland / sjur.brandeland@stericsson.com + * License terms: GNU General Public License (GPL) version 2 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Vicram Arv "); +MODULE_AUTHOR("Sjur Brendeland "); +MODULE_DESCRIPTION("Virtio CAIF Driver"); + +/* NAPI schedule quota */ +#define CFV_DEFAULT_QUOTA 32 + +/* Defaults used if virtio config space is unavailable */ +#define CFV_DEF_MTU_SIZE 4096 +#define CFV_DEF_HEADROOM 32 +#define CFV_DEF_TAILROOM 32 + +/* Required IP header alignment */ +#define IP_HDR_ALIGN 4 + +/* struct cfv_napi_contxt - NAPI context info + * @riov: IOV holding data read from the ring. Note that riov may + * still hold data when cfv_rx_poll() returns. + * @head: Last descriptor ID we received from vringh_getdesc_kern. + * We use this to put descriptor back on the used ring. USHRT_MAX is + * used to indicate invalid head-id. + */ +struct cfv_napi_context { + struct vringh_kiov riov; + unsigned short head; +}; + +/* struct cfv_stats - statistics for debugfs + * @rx_napi_complete: Number of NAPI completions (RX) + * @rx_napi_resched: Number of calls where the full quota was used (RX) + * @rx_nomem: Number of SKB alloc failures (RX) + * @rx_kicks: Number of RX kicks + * @tx_full_ring: Number times TX ring was full + * @tx_no_mem: Number of times TX went out of memory + * @tx_flow_on: Number of flow on (TX) + * @tx_kicks: Number of TX kicks + */ +struct cfv_stats { + u32 rx_napi_complete; + u32 rx_napi_resched; + u32 rx_nomem; + u32 rx_kicks; + u32 tx_full_ring; + u32 tx_no_mem; + u32 tx_flow_on; + u32 tx_kicks; +}; + +/* struct cfv_info - Caif Virtio control structure + * @cfdev: caif common header + * @vdev: Associated virtio device + * @vr_rx: rx/downlink host vring + * @vq_tx: tx/uplink virtqueue + * @ndev: CAIF link layer device + * @watermark_tx: indicates number of free descriptors we need + * to reopen the tx-queues after overload. + * @tx_lock: protects vq_tx from concurrent use + * @tx_release_tasklet: Tasklet for freeing consumed TX buffers + * @napi: Napi context used in cfv_rx_poll() + * @ctx: Context data used in cfv_rx_poll() + * @tx_hr: transmit headroom + * @rx_hr: receive headroom + * @tx_tr: transmit tail room + * @rx_tr: receive tail room + * @mtu: transmit max size + * @mru: receive max size + * @allocsz: size of dma memory reserved for TX buffers + * @alloc_addr: virtual address to dma memory for TX buffers + * @alloc_dma: dma address to dma memory for TX buffers + * @genpool: Gen Pool used for allocating TX buffers + * @reserved_mem: Pointer to memory reserve allocated from genpool + * @reserved_size: Size of memory reserve allocated from genpool + * @stats: Statistics exposed in sysfs + * @debugfs: Debugfs dentry for statistic counters + */ +struct cfv_info { + struct caif_dev_common cfdev; + struct virtio_device *vdev; + struct vringh *vr_rx; + struct virtqueue *vq_tx; + struct net_device *ndev; + unsigned int watermark_tx; + /* Protect access to vq_tx */ + spinlock_t tx_lock; + struct tasklet_struct tx_release_tasklet; + struct napi_struct napi; + struct cfv_napi_context ctx; + u16 tx_hr; + u16 rx_hr; + u16 tx_tr; + u16 rx_tr; + u32 mtu; + u32 mru; + size_t allocsz; + void *alloc_addr; + dma_addr_t alloc_dma; + struct gen_pool *genpool; + unsigned long reserved_mem; + size_t reserved_size; + struct cfv_stats stats; + struct dentry *debugfs; +}; + +/* struct buf_info - maintains transmit buffer data handle + * @size: size of transmit buffer + * @dma_handle: handle to allocated dma device memory area + * @vaddr: virtual address mapping to allocated memory area + */ +struct buf_info { + size_t size; + u8 *vaddr; +}; + +/* Called from virtio device, in IRQ context */ +static void cfv_release_cb(struct virtqueue *vq_tx) +{ + struct cfv_info *cfv = vq_tx->vdev->priv; + + ++cfv->stats.tx_kicks; + tasklet_schedule(&cfv->tx_release_tasklet); +} + +static void free_buf_info(struct cfv_info *cfv, struct buf_info *buf_info) +{ + if (!buf_info) + return; + gen_pool_free(cfv->genpool, (unsigned long) buf_info->vaddr, + buf_info->size); + kfree(buf_info); +} + +/* This is invoked whenever the remote processor completed processing + * a TX msg we just sent, and the buffer is put back to the used ring. + */ +static void cfv_release_used_buf(struct virtqueue *vq_tx) +{ + struct cfv_info *cfv = vq_tx->vdev->priv; + unsigned long flags; + + BUG_ON(vq_tx != cfv->vq_tx); + + for (;;) { + unsigned int len; + struct buf_info *buf_info; + + /* Get used buffer from used ring to recycle used descriptors */ + spin_lock_irqsave(&cfv->tx_lock, flags); + buf_info = virtqueue_get_buf(vq_tx, &len); + spin_unlock_irqrestore(&cfv->tx_lock, flags); + + /* Stop looping if there are no more buffers to free */ + if (!buf_info) + break; + + free_buf_info(cfv, buf_info); + + /* watermark_tx indicates if we previously stopped the tx + * queues. If we have enough free stots in the virtio ring, + * re-establish memory reserved and open up tx queues. + */ + if (cfv->vq_tx->num_free <= cfv->watermark_tx) + continue; + + /* Re-establish memory reserve */ + if (cfv->reserved_mem == 0 && cfv->genpool) + cfv->reserved_mem = + gen_pool_alloc(cfv->genpool, + cfv->reserved_size); + + /* Open up the tx queues */ + if (cfv->reserved_mem) { + cfv->watermark_tx = + virtqueue_get_vring_size(cfv->vq_tx); + netif_tx_wake_all_queues(cfv->ndev); + /* Buffers are recycled in cfv_netdev_tx, so + * disable notifications when queues are opened. + */ + virtqueue_disable_cb(cfv->vq_tx); + ++cfv->stats.tx_flow_on; + } else { + /* if no memory reserve, wait for more free slots */ + WARN_ON(cfv->watermark_tx > + virtqueue_get_vring_size(cfv->vq_tx)); + cfv->watermark_tx += + virtqueue_get_vring_size(cfv->vq_tx) / 4; + } + } +} + +/* Allocate a SKB and copy packet data to it */ +static struct sk_buff *cfv_alloc_and_copy_skb(int *err, + struct cfv_info *cfv, + u8 *frm, u32 frm_len) +{ + struct sk_buff *skb; + u32 cfpkt_len, pad_len; + + *err = 0; + /* Verify that packet size with down-link header and mtu size */ + if (frm_len > cfv->mru || frm_len <= cfv->rx_hr + cfv->rx_tr) { + netdev_err(cfv->ndev, + "Invalid frmlen:%u mtu:%u hr:%d tr:%d\n", + frm_len, cfv->mru, cfv->rx_hr, + cfv->rx_tr); + *err = -EPROTO; + return NULL; + } + + cfpkt_len = frm_len - (cfv->rx_hr + cfv->rx_tr); + pad_len = (unsigned long)(frm + cfv->rx_hr) & (IP_HDR_ALIGN - 1); + + skb = netdev_alloc_skb(cfv->ndev, frm_len + pad_len); + if (!skb) { + *err = -ENOMEM; + return NULL; + } + + skb_reserve(skb, cfv->rx_hr + pad_len); + + memcpy(skb_put(skb, cfpkt_len), frm + cfv->rx_hr, cfpkt_len); + return skb; +} + +/* Get packets from the host vring */ +static int cfv_rx_poll(struct napi_struct *napi, int quota) +{ + struct cfv_info *cfv = container_of(napi, struct cfv_info, napi); + int rxcnt = 0; + int err = 0; + void *buf; + struct sk_buff *skb; + struct vringh_kiov *riov = &cfv->ctx.riov; + unsigned int skb_len; + +again: + do { + skb = NULL; + + /* Put the previous iovec back on the used ring and + * fetch a new iovec if we have processed all elements. + */ + if (riov->i == riov->used) { + if (cfv->ctx.head != USHRT_MAX) { + vringh_complete_kern(cfv->vr_rx, + cfv->ctx.head, + 0); + cfv->ctx.head = USHRT_MAX; + } + + err = vringh_getdesc_kern( + cfv->vr_rx, + riov, + NULL, + &cfv->ctx.head, + GFP_ATOMIC); + + if (err <= 0) + goto exit; + } + + buf = phys_to_virt((unsigned long) riov->iov[riov->i].iov_base); + /* TODO: Add check on valid buffer address */ + + skb = cfv_alloc_and_copy_skb(&err, cfv, buf, + riov->iov[riov->i].iov_len); + if (unlikely(err)) + goto exit; + + /* Push received packet up the stack. */ + skb_len = skb->len; + skb->protocol = htons(ETH_P_CAIF); + skb_reset_mac_header(skb); + skb->dev = cfv->ndev; + err = netif_receive_skb(skb); + if (unlikely(err)) { + ++cfv->ndev->stats.rx_dropped; + } else { + ++cfv->ndev->stats.rx_packets; + cfv->ndev->stats.rx_bytes += skb_len; + } + + ++riov->i; + ++rxcnt; + } while (rxcnt < quota); + + ++cfv->stats.rx_napi_resched; + goto out; + +exit: + switch (err) { + case 0: + ++cfv->stats.rx_napi_complete; + + /* Really out of patckets? (stolen from virtio_net)*/ + napi_complete(napi); + if (unlikely(vringh_notify_enable_kern(cfv->vr_rx)) && + napi_schedule_prep(napi)) { + vringh_notify_disable_kern(cfv->vr_rx); + __napi_schedule(napi); + goto again; + } + break; + + case -ENOMEM: + ++cfv->stats.rx_nomem; + dev_kfree_skb(skb); + /* Stop NAPI poll on OOM, we hope to be polled later */ + napi_complete(napi); + vringh_notify_enable_kern(cfv->vr_rx); + break; + + default: + /* We're doomed, any modem fault is fatal */ + netdev_warn(cfv->ndev, "Bad ring, disable device\n"); + cfv->ndev->stats.rx_dropped = riov->used - riov->i; + napi_complete(napi); + vringh_notify_disable_kern(cfv->vr_rx); + netif_carrier_off(cfv->ndev); + break; + } +out: + if (rxcnt && vringh_need_notify_kern(cfv->vr_rx) > 0) + vringh_notify(cfv->vr_rx); + return rxcnt; +} + +static void cfv_recv(struct virtio_device *vdev, struct vringh *vr_rx) +{ + struct cfv_info *cfv = vdev->priv; + + ++cfv->stats.rx_kicks; + vringh_notify_disable_kern(cfv->vr_rx); + napi_schedule(&cfv->napi); +} + +static void cfv_destroy_genpool(struct cfv_info *cfv) +{ + if (cfv->alloc_addr) + dma_free_coherent(cfv->vdev->dev.parent->parent, + cfv->allocsz, cfv->alloc_addr, + cfv->alloc_dma); + + if (!cfv->genpool) + return; + gen_pool_free(cfv->genpool, cfv->reserved_mem, + cfv->reserved_size); + gen_pool_destroy(cfv->genpool); + cfv->genpool = NULL; +} + +static int cfv_create_genpool(struct cfv_info *cfv) +{ + int err; + + /* dma_alloc can only allocate whole pages, and we need a more + * fine graned allocation so we use genpool. We ask for space needed + * by IP and a full ring. If the dma allcoation fails we retry with a + * smaller allocation size. + */ + err = -ENOMEM; + cfv->allocsz = (virtqueue_get_vring_size(cfv->vq_tx) * + (ETH_DATA_LEN + cfv->tx_hr + cfv->tx_tr) * 11)/10; + if (cfv->allocsz <= (num_possible_cpus() + 1) * cfv->ndev->mtu) + return -EINVAL; + + for (;;) { + if (cfv->allocsz <= num_possible_cpus() * cfv->ndev->mtu) { + netdev_info(cfv->ndev, "Not enough device memory\n"); + return -ENOMEM; + } + + cfv->alloc_addr = dma_alloc_coherent( + cfv->vdev->dev.parent->parent, + cfv->allocsz, &cfv->alloc_dma, + GFP_ATOMIC); + if (cfv->alloc_addr) + break; + + cfv->allocsz = (cfv->allocsz * 3) >> 2; + } + + netdev_dbg(cfv->ndev, "Allocated %zd bytes from dma-memory\n", + cfv->allocsz); + + /* Allocate on 128 bytes boundaries (1 << 7)*/ + cfv->genpool = gen_pool_create(7, -1); + if (!cfv->genpool) + goto err; + + err = gen_pool_add_virt(cfv->genpool, (unsigned long)cfv->alloc_addr, + (phys_addr_t)virt_to_phys(cfv->alloc_addr), + cfv->allocsz, -1); + if (err) + goto err; + + /* Reserve some memory for low memory situations. If we hit the roof + * in the memory pool, we stop TX flow and release the reserve. + */ + cfv->reserved_size = num_possible_cpus() * cfv->ndev->mtu; + cfv->reserved_mem = gen_pool_alloc(cfv->genpool, + cfv->reserved_size); + if (!cfv->reserved_mem) + goto err; + + cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx); + return 0; +err: + cfv_destroy_genpool(cfv); + return err; +} + +/* Enable the CAIF interface and allocate the memory-pool */ +static int cfv_netdev_open(struct net_device *netdev) +{ + struct cfv_info *cfv = netdev_priv(netdev); + + if (cfv_create_genpool(cfv)) + return -ENOMEM; + + netif_carrier_on(netdev); + napi_enable(&cfv->napi); + + /* Schedule NAPI to read any pending packets */ + napi_schedule(&cfv->napi); + return 0; +} + +/* Disable the CAIF interface and free the memory-pool */ +static int cfv_netdev_close(struct net_device *netdev) +{ + struct cfv_info *cfv = netdev_priv(netdev); + unsigned long flags; + struct buf_info *buf_info; + + /* Disable interrupts, queues and NAPI polling */ + netif_carrier_off(netdev); + virtqueue_disable_cb(cfv->vq_tx); + vringh_notify_disable_kern(cfv->vr_rx); + napi_disable(&cfv->napi); + + /* Release any TX buffers on both used and avilable rings */ + cfv_release_used_buf(cfv->vq_tx); + spin_lock_irqsave(&cfv->tx_lock, flags); + while ((buf_info = virtqueue_detach_unused_buf(cfv->vq_tx))) + free_buf_info(cfv, buf_info); + spin_unlock_irqrestore(&cfv->tx_lock, flags); + + /* Release all dma allocated memory and destroy the pool */ + cfv_destroy_genpool(cfv); + return 0; +} + +/* Allocate a buffer in dma-memory and copy skb to it */ +static struct buf_info *cfv_alloc_and_copy_to_shm(struct cfv_info *cfv, + struct sk_buff *skb, + struct scatterlist *sg) +{ + struct caif_payload_info *info = (void *)&skb->cb; + struct buf_info *buf_info = NULL; + u8 pad_len, hdr_ofs; + + if (!cfv->genpool) + goto err; + + if (unlikely(cfv->tx_hr + skb->len + cfv->tx_tr > cfv->mtu)) { + netdev_warn(cfv->ndev, "Invalid packet len (%d > %d)\n", + cfv->tx_hr + skb->len + cfv->tx_tr, cfv->mtu); + goto err; + } + + buf_info = kmalloc(sizeof(struct buf_info), GFP_ATOMIC); + if (unlikely(!buf_info)) + goto err; + + /* Make the IP header aligned in tbe buffer */ + hdr_ofs = cfv->tx_hr + info->hdr_len; + pad_len = hdr_ofs & (IP_HDR_ALIGN - 1); + buf_info->size = cfv->tx_hr + skb->len + cfv->tx_tr + pad_len; + + /* allocate dma memory buffer */ + buf_info->vaddr = (void *)gen_pool_alloc(cfv->genpool, buf_info->size); + if (unlikely(!buf_info->vaddr)) + goto err; + + /* copy skbuf contents to send buffer */ + skb_copy_bits(skb, 0, buf_info->vaddr + cfv->tx_hr + pad_len, skb->len); + sg_init_one(sg, buf_info->vaddr + pad_len, + skb->len + cfv->tx_hr + cfv->rx_hr); + + return buf_info; +err: + kfree(buf_info); + return NULL; +} + +/* Put the CAIF packet on the virtio ring and kick the receiver */ +static int cfv_netdev_tx(struct sk_buff *skb, struct net_device *netdev) +{ + struct cfv_info *cfv = netdev_priv(netdev); + struct buf_info *buf_info; + struct scatterlist sg; + unsigned long flags; + bool flow_off = false; + int ret; + + /* garbage collect released buffers */ + cfv_release_used_buf(cfv->vq_tx); + spin_lock_irqsave(&cfv->tx_lock, flags); + + /* Flow-off check takes into account number of cpus to make sure + * virtqueue will not be overfilled in any possible smp conditions. + * + * Flow-on is triggered when sufficient buffers are freed + */ + if (unlikely(cfv->vq_tx->num_free <= num_present_cpus())) { + flow_off = true; + cfv->stats.tx_full_ring++; + } + + /* If we run out of memory, we release the memory reserve and retry + * allocation. + */ + buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); + if (unlikely(!buf_info)) { + cfv->stats.tx_no_mem++; + flow_off = true; + + if (cfv->reserved_mem && cfv->genpool) { + gen_pool_free(cfv->genpool, cfv->reserved_mem, + cfv->reserved_size); + cfv->reserved_mem = 0; + buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); + } + } + + if (unlikely(flow_off)) { + /* Turn flow on when a 1/4 of the descriptors are released */ + cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx) / 4; + /* Enable notifications of recycled TX buffers */ + virtqueue_enable_cb(cfv->vq_tx); + netif_tx_stop_all_queues(netdev); + } + + if (unlikely(!buf_info)) { + /* If the memory reserve does it's job, this shouldn't happen */ + netdev_warn(cfv->ndev, "Out of gen_pool memory\n"); + goto err; + } + + ret = virtqueue_add_buf(cfv->vq_tx, &sg, 1, 0, + buf_info, GFP_ATOMIC); + if (unlikely((ret < 0))) { + /* If flow control works, this shouldn't happen */ + netdev_warn(cfv->ndev, "Failed adding buffer to TX vring:%d\n", + ret); + goto err; + } + + /* update netdev statistics */ + cfv->ndev->stats.tx_packets++; + cfv->ndev->stats.tx_bytes += skb->len; + spin_unlock_irqrestore(&cfv->tx_lock, flags); + + /* tell the remote processor it has a pending message to read */ + virtqueue_kick(cfv->vq_tx); + + dev_kfree_skb(skb); + return NETDEV_TX_OK; +err: + spin_unlock_irqrestore(&cfv->tx_lock, flags); + cfv->ndev->stats.tx_dropped++; + free_buf_info(cfv, buf_info); + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static void cfv_tx_release_tasklet(unsigned long drv) +{ + struct cfv_info *cfv = (struct cfv_info *)drv; + cfv_release_used_buf(cfv->vq_tx); +} + +static const struct net_device_ops cfv_netdev_ops = { + .ndo_open = cfv_netdev_open, + .ndo_stop = cfv_netdev_close, + .ndo_start_xmit = cfv_netdev_tx, +}; + +static void cfv_netdev_setup(struct net_device *netdev) +{ + netdev->netdev_ops = &cfv_netdev_ops; + netdev->type = ARPHRD_CAIF; + netdev->tx_queue_len = 100; + netdev->flags = IFF_POINTOPOINT | IFF_NOARP; + netdev->mtu = CFV_DEF_MTU_SIZE; + netdev->destructor = free_netdev; +} + +/* Create debugfs counters for the device */ +static inline void debugfs_init(struct cfv_info *cfv) +{ + cfv->debugfs = + debugfs_create_dir(netdev_name(cfv->ndev), NULL); + + if (IS_ERR(cfv->debugfs)) + return; + + debugfs_create_u32("rx-napi-complete", S_IRUSR, cfv->debugfs, + &cfv->stats.rx_napi_complete); + debugfs_create_u32("rx-napi-resched", S_IRUSR, cfv->debugfs, + &cfv->stats.rx_napi_resched); + debugfs_create_u32("rx-nomem", S_IRUSR, cfv->debugfs, + &cfv->stats.rx_nomem); + debugfs_create_u32("rx-kicks", S_IRUSR, cfv->debugfs, + &cfv->stats.rx_kicks); + debugfs_create_u32("tx-full-ring", S_IRUSR, cfv->debugfs, + &cfv->stats.tx_full_ring); + debugfs_create_u32("tx-no-mem", S_IRUSR, cfv->debugfs, + &cfv->stats.tx_no_mem); + debugfs_create_u32("tx-kicks", S_IRUSR, cfv->debugfs, + &cfv->stats.tx_kicks); + debugfs_create_u32("tx-flow-on", S_IRUSR, cfv->debugfs, + &cfv->stats.tx_flow_on); +} + +/* Setup CAIF for the a virtio device */ +static int cfv_probe(struct virtio_device *vdev) +{ + vq_callback_t *vq_cbs = cfv_release_cb; + vrh_callback_t *vrh_cbs = cfv_recv; + const char *names = "output"; + const char *cfv_netdev_name = "cfvrt"; + struct net_device *netdev; + struct cfv_info *cfv; + int err = -EINVAL; + + netdev = alloc_netdev(sizeof(struct cfv_info), cfv_netdev_name, + cfv_netdev_setup); + if (!netdev) + return -ENOMEM; + + cfv = netdev_priv(netdev); + cfv->vdev = vdev; + cfv->ndev = netdev; + + spin_lock_init(&cfv->tx_lock); + + /* Get the RX virtio ring. This is a "host side vring". */ + err = vdev->vringh_config->find_vrhs(vdev, 1, &cfv->vr_rx, &vrh_cbs); + if (err) + goto err; + + /* Get the TX virtio ring. This is a "guest side vring". */ + err = vdev->config->find_vqs(vdev, 1, &cfv->vq_tx, &vq_cbs, &names); + if (err) + goto err; + + /* Get the CAIF configuration from virtio config space, if available */ +#define GET_VIRTIO_CONFIG_OPS(_v, _var, _f) \ + ((_v)->config->get(_v, offsetof(struct virtio_caif_transf_config, _f), \ + &_var, \ + FIELD_SIZEOF(struct virtio_caif_transf_config, _f))) + + if (vdev->config->get) { + GET_VIRTIO_CONFIG_OPS(vdev, cfv->tx_hr, headroom); + GET_VIRTIO_CONFIG_OPS(vdev, cfv->rx_hr, headroom); + GET_VIRTIO_CONFIG_OPS(vdev, cfv->tx_tr, tailroom); + GET_VIRTIO_CONFIG_OPS(vdev, cfv->rx_tr, tailroom); + GET_VIRTIO_CONFIG_OPS(vdev, cfv->mtu, mtu); + GET_VIRTIO_CONFIG_OPS(vdev, cfv->mru, mtu); + } else { + cfv->tx_hr = CFV_DEF_HEADROOM; + cfv->rx_hr = CFV_DEF_HEADROOM; + cfv->tx_tr = CFV_DEF_TAILROOM; + cfv->rx_tr = CFV_DEF_TAILROOM; + cfv->mtu = CFV_DEF_MTU_SIZE; + cfv->mru = CFV_DEF_MTU_SIZE; + } + + netdev->needed_headroom = cfv->tx_hr; + netdev->needed_tailroom = cfv->tx_tr; + + /* Disable buffer release interrupts unless we have stopped TX queues */ + virtqueue_disable_cb(cfv->vq_tx); + + netdev->mtu = cfv->mtu - cfv->tx_tr; + vdev->priv = cfv; + + /* Initialize NAPI poll context data */ + vringh_kiov_init(&cfv->ctx.riov, NULL, 0); + cfv->ctx.head = USHRT_MAX; + netif_napi_add(netdev, &cfv->napi, cfv_rx_poll, CFV_DEFAULT_QUOTA); + + tasklet_init(&cfv->tx_release_tasklet, + cfv_tx_release_tasklet, + (unsigned long)cfv); + + /* Carrier is off until netdevice is opened */ + netif_carrier_off(netdev); + + /* register Netdev */ + err = register_netdev(netdev); + if (err) { + dev_err(&vdev->dev, "Unable to register netdev (%d)\n", err); + goto err; + } + + debugfs_init(cfv); + + return 0; +err: + netdev_warn(cfv->ndev, "CAIF Virtio probe failed:%d\n", err); + + if (cfv->vr_rx) + vdev->vringh_config->del_vrhs(cfv->vdev); + if (cfv->vdev) + vdev->config->del_vqs(cfv->vdev); + free_netdev(netdev); + return err; +} + +static void cfv_remove(struct virtio_device *vdev) +{ + struct cfv_info *cfv = vdev->priv; + + rtnl_lock(); + dev_close(cfv->ndev); + rtnl_unlock(); + + tasklet_kill(&cfv->tx_release_tasklet); + debugfs_remove_recursive(cfv->debugfs); + + vringh_kiov_cleanup(&cfv->ctx.riov); + vdev->config->reset(vdev); + vdev->vringh_config->del_vrhs(cfv->vdev); + cfv->vr_rx = NULL; + vdev->config->del_vqs(cfv->vdev); + unregister_netdev(cfv->ndev); +} + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_CAIF, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static unsigned int features[] = { +}; + +static struct virtio_driver caif_virtio_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = cfv_probe, + .remove = cfv_remove, +}; + +module_virtio_driver(caif_virtio_driver); +MODULE_DEVICE_TABLE(virtio, id_table); diff --git a/include/linux/virtio_caif.h b/include/linux/virtio_caif.h new file mode 100644 index 00000000000..5d2d3124ca3 --- /dev/null +++ b/include/linux/virtio_caif.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) ST-Ericsson AB 2012 + * Author: Sjur Brændeland + * + * This header is BSD licensed so + * anyone can use the definitions to implement compatible remote processors + */ + +#ifndef VIRTIO_CAIF_H +#define VIRTIO_CAIF_H + +#include +struct virtio_caif_transf_config { + u16 headroom; + u16 tailroom; + u32 mtu; + u8 reserved[4]; +}; + +struct virtio_caif_config { + struct virtio_caif_transf_config uplink, downlink; + u8 reserved[8]; +}; +#endif diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index a7630d04029..284fc3a05f7 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -38,5 +38,6 @@ #define VIRTIO_ID_SCSI 8 /* virtio scsi */ #define VIRTIO_ID_9P 9 /* 9p virtio console */ #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */ +#define VIRTIO_ID_CAIF 12 /* Virtio caif */ #endif /* _LINUX_VIRTIO_IDS_H */ -- cgit v1.2.3-70-g09d2 From c8164d8931fdee9ac5314708c4071adf1d997425 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 20 Mar 2013 15:37:08 +1030 Subject: scatterlist: introduce sg_unmark_end This is useful in places that recycle the same scatterlist multiple times, and do not want to incur the cost of sg_init_table every time in hot paths. Acked-by: Jens Axboe Signed-off-by: Paolo Bonzini Signed-off-by: Rusty Russell --- block/blk-integrity.c | 2 +- block/blk-merge.c | 2 +- include/linux/scatterlist.h | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/block/blk-integrity.c b/block/blk-integrity.c index dabd221857e..03cf7179e8e 100644 --- a/block/blk-integrity.c +++ b/block/blk-integrity.c @@ -110,7 +110,7 @@ new_segment: if (!sg) sg = sglist; else { - sg->page_link &= ~0x02; + sg_unmark_end(sg); sg = sg_next(sg); } diff --git a/block/blk-merge.c b/block/blk-merge.c index 936a110de0b..5f244825379 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -143,7 +143,7 @@ new_segment: * termination bit to avoid doing a full * sg_init_table() in drivers for each command. */ - (*sg)->page_link &= ~0x02; + sg_unmark_end(*sg); *sg = sg_next(*sg); } diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 2d8bdaef961..bfc47e0de81 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -171,6 +171,22 @@ static inline void sg_mark_end(struct scatterlist *sg) sg->page_link &= ~0x01; } +/** + * sg_unmark_end - Undo setting the end of the scatterlist + * @sg: SG entryScatterlist + * + * Description: + * Removes the termination marker from the given entry of the scatterlist. + * + **/ +static inline void sg_unmark_end(struct scatterlist *sg) +{ +#ifdef CONFIG_DEBUG_SG + BUG_ON(sg->sg_magic != SG_MAGIC); +#endif + sg->page_link &= ~0x02; +} + /** * sg_phys - Return physical address of an sg entry * @sg: SG entry -- cgit v1.2.3-70-g09d2 From 13816c768d46586e925b22736992258d6105ad2c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Mar 2013 15:37:09 +1030 Subject: virtio_ring: virtqueue_add_sgs, to add multiple sgs. virtio_scsi can really use this, to avoid the current hack of copying the whole sg array. Some other things get slightly neater, too. This causes a slowdown in virtqueue_add_buf(), which is implemented as a wrapper. This is addressed in the next patches. for i in `seq 50`; do /usr/bin/time -f 'Wall time:%e' ./vringh_test --indirect --eventidx --parallel --fast-vringh; done 2>&1 | stats --trim-outliers: Before: Using CPUS 0 and 3 Guest: notified 0, pinged 39009-39063(39062) Host: notified 39009-39063(39062), pinged 0 Wall time:1.700000-1.950000(1.723542) After: Using CPUS 0 and 3 Guest: notified 0, pinged 39062-39063(39063) Host: notified 39062-39063(39063), pinged 0 Wall time:1.760000-2.220000(1.789167) Signed-off-by: Rusty Russell Reviewed-by: Wanlong Gao Reviewed-by: Asias He --- drivers/virtio/virtio_ring.c | 220 ++++++++++++++++++++++++++++----------- include/linux/virtio.h | 7 ++ tools/virtio/linux/scatterlist.h | 16 +++ tools/virtio/linux/virtio.h | 7 ++ 4 files changed, 187 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 245177c286a..a78ad459cc8 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -98,16 +98,36 @@ struct vring_virtqueue #define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq) +static inline struct scatterlist *sg_next_chained(struct scatterlist *sg, + unsigned int *count) +{ + return sg_next(sg); +} + +static inline struct scatterlist *sg_next_arr(struct scatterlist *sg, + unsigned int *count) +{ + if (--(*count) == 0) + return NULL; + return sg + 1; +} + /* Set up an indirect table of descriptors and add it to the queue. */ -static int vring_add_indirect(struct vring_virtqueue *vq, - struct scatterlist sg[], - unsigned int out, - unsigned int in, - gfp_t gfp) +static inline int vring_add_indirect(struct vring_virtqueue *vq, + struct scatterlist *sgs[], + struct scatterlist *(*next) + (struct scatterlist *, unsigned int *), + unsigned int total_sg, + unsigned int total_out, + unsigned int total_in, + unsigned int out_sgs, + unsigned int in_sgs, + gfp_t gfp) { struct vring_desc *desc; unsigned head; - int i; + struct scatterlist *sg; + int i, n; /* * We require lowmem mappings for the descriptors because @@ -116,25 +136,31 @@ static int vring_add_indirect(struct vring_virtqueue *vq, */ gfp &= ~(__GFP_HIGHMEM | __GFP_HIGH); - desc = kmalloc((out + in) * sizeof(struct vring_desc), gfp); + desc = kmalloc(total_sg * sizeof(struct vring_desc), gfp); if (!desc) return -ENOMEM; - /* Transfer entries from the sg list into the indirect page */ - for (i = 0; i < out; i++) { - desc[i].flags = VRING_DESC_F_NEXT; - desc[i].addr = sg_phys(sg); - desc[i].len = sg->length; - desc[i].next = i+1; - sg++; + /* Transfer entries from the sg lists into the indirect page */ + i = 0; + for (n = 0; n < out_sgs; n++) { + for (sg = sgs[n]; sg; sg = next(sg, &total_out)) { + desc[i].flags = VRING_DESC_F_NEXT; + desc[i].addr = sg_phys(sg); + desc[i].len = sg->length; + desc[i].next = i+1; + i++; + } } - for (; i < (out + in); i++) { - desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE; - desc[i].addr = sg_phys(sg); - desc[i].len = sg->length; - desc[i].next = i+1; - sg++; + for (; n < (out_sgs + in_sgs); n++) { + for (sg = sgs[n]; sg; sg = next(sg, &total_in)) { + desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE; + desc[i].addr = sg_phys(sg); + desc[i].len = sg->length; + desc[i].next = i+1; + i++; + } } + BUG_ON(i != total_sg); /* Last one doesn't continue. */ desc[i-1].flags &= ~VRING_DESC_F_NEXT; @@ -155,29 +181,20 @@ static int vring_add_indirect(struct vring_virtqueue *vq, return head; } -/** - * virtqueue_add_buf - expose buffer to other end - * @vq: the struct virtqueue we're talking about. - * @sg: the description of the buffer(s). - * @out_num: the number of sg readable by other side - * @in_num: the number of sg which are writable (after readable ones) - * @data: the token identifying the buffer. - * @gfp: how to do memory allocations (if necessary). - * - * Caller must ensure we don't call this with other virtqueue operations - * at the same time (except where noted). - * - * Returns zero or a negative error (ie. ENOSPC, ENOMEM). - */ -int virtqueue_add_buf(struct virtqueue *_vq, - struct scatterlist sg[], - unsigned int out, - unsigned int in, - void *data, - gfp_t gfp) +static inline int virtqueue_add(struct virtqueue *_vq, + struct scatterlist *sgs[], + struct scatterlist *(*next) + (struct scatterlist *, unsigned int *), + unsigned int total_out, + unsigned int total_in, + unsigned int out_sgs, + unsigned int in_sgs, + void *data, + gfp_t gfp) { struct vring_virtqueue *vq = to_vvq(_vq); - unsigned int i, avail, uninitialized_var(prev); + struct scatterlist *sg; + unsigned int i, n, avail, uninitialized_var(prev), total_sg; int head; START_USE(vq); @@ -197,46 +214,54 @@ int virtqueue_add_buf(struct virtqueue *_vq, } #endif + total_sg = total_in + total_out; + /* If the host supports indirect descriptor tables, and we have multiple * buffers, then go indirect. FIXME: tune this threshold */ - if (vq->indirect && (out + in) > 1 && vq->vq.num_free) { - head = vring_add_indirect(vq, sg, out, in, gfp); + if (vq->indirect && total_sg > 1 && vq->vq.num_free) { + head = vring_add_indirect(vq, sgs, next, total_sg, total_out, + total_in, + out_sgs, in_sgs, gfp); if (likely(head >= 0)) goto add_head; } - BUG_ON(out + in > vq->vring.num); - BUG_ON(out + in == 0); + BUG_ON(total_sg > vq->vring.num); + BUG_ON(total_sg == 0); - if (vq->vq.num_free < out + in) { + if (vq->vq.num_free < total_sg) { pr_debug("Can't add buf len %i - avail = %i\n", - out + in, vq->vq.num_free); + total_sg, vq->vq.num_free); /* FIXME: for historical reasons, we force a notify here if * there are outgoing parts to the buffer. Presumably the * host should service the ring ASAP. */ - if (out) + if (out_sgs) vq->notify(&vq->vq); END_USE(vq); return -ENOSPC; } /* We're about to use some buffers from the free list. */ - vq->vq.num_free -= out + in; - - head = vq->free_head; - for (i = vq->free_head; out; i = vq->vring.desc[i].next, out--) { - vq->vring.desc[i].flags = VRING_DESC_F_NEXT; - vq->vring.desc[i].addr = sg_phys(sg); - vq->vring.desc[i].len = sg->length; - prev = i; - sg++; + vq->vq.num_free -= total_sg; + + head = i = vq->free_head; + for (n = 0; n < out_sgs; n++) { + for (sg = sgs[n]; sg; sg = next(sg, &total_out)) { + vq->vring.desc[i].flags = VRING_DESC_F_NEXT; + vq->vring.desc[i].addr = sg_phys(sg); + vq->vring.desc[i].len = sg->length; + prev = i; + i = vq->vring.desc[i].next; + } } - for (; in; i = vq->vring.desc[i].next, in--) { - vq->vring.desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE; - vq->vring.desc[i].addr = sg_phys(sg); - vq->vring.desc[i].len = sg->length; - prev = i; - sg++; + for (; n < (out_sgs + in_sgs); n++) { + for (sg = sgs[n]; sg; sg = next(sg, &total_in)) { + vq->vring.desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE; + vq->vring.desc[i].addr = sg_phys(sg); + vq->vring.desc[i].len = sg->length; + prev = i; + i = vq->vring.desc[i].next; + } } /* Last one doesn't continue. */ vq->vring.desc[prev].flags &= ~VRING_DESC_F_NEXT; @@ -269,8 +294,77 @@ add_head: return 0; } + +/** + * virtqueue_add_buf - expose buffer to other end + * @vq: the struct virtqueue we're talking about. + * @sg: the description of the buffer(s). + * @out_num: the number of sg readable by other side + * @in_num: the number of sg which are writable (after readable ones) + * @data: the token identifying the buffer. + * @gfp: how to do memory allocations (if necessary). + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error (ie. ENOSPC, ENOMEM). + */ +int virtqueue_add_buf(struct virtqueue *_vq, + struct scatterlist sg[], + unsigned int out, + unsigned int in, + void *data, + gfp_t gfp) +{ + struct scatterlist *sgs[2]; + + sgs[0] = sg; + sgs[1] = sg + out; + + return virtqueue_add(_vq, sgs, sg_next_arr, + out, in, out ? 1 : 0, in ? 1 : 0, data, gfp); +} EXPORT_SYMBOL_GPL(virtqueue_add_buf); +/** + * virtqueue_add_sgs - expose buffers to other end + * @vq: the struct virtqueue we're talking about. + * @sgs: array of terminated scatterlists. + * @out_num: the number of scatterlists readable by other side + * @in_num: the number of scatterlists which are writable (after readable ones) + * @data: the token identifying the buffer. + * @gfp: how to do memory allocations (if necessary). + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error (ie. ENOSPC, ENOMEM). + */ +int virtqueue_add_sgs(struct virtqueue *_vq, + struct scatterlist *sgs[], + unsigned int out_sgs, + unsigned int in_sgs, + void *data, + gfp_t gfp) +{ + unsigned int i, total_out, total_in; + + /* Count them first. */ + for (i = total_out = total_in = 0; i < out_sgs; i++) { + struct scatterlist *sg; + for (sg = sgs[i]; sg; sg = sg_next(sg)) + total_out++; + } + for (; i < out_sgs + in_sgs; i++) { + struct scatterlist *sg; + for (sg = sgs[i]; sg; sg = sg_next(sg)) + total_in++; + } + return virtqueue_add(_vq, sgs, sg_next_chained, + total_out, total_in, out_sgs, in_sgs, data, gfp); +} +EXPORT_SYMBOL_GPL(virtqueue_add_sgs); + /** * virtqueue_kick_prepare - first half of split virtqueue_kick call. * @vq: the struct virtqueue diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 5d5b3abc283..ac80288b292 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -41,6 +41,13 @@ int virtqueue_add_buf(struct virtqueue *vq, void *data, gfp_t gfp); +int virtqueue_add_sgs(struct virtqueue *vq, + struct scatterlist *sgs[], + unsigned int out_sgs, + unsigned int in_sgs, + void *data, + gfp_t gfp); + void virtqueue_kick(struct virtqueue *vq); bool virtqueue_kick_prepare(struct virtqueue *vq); diff --git a/tools/virtio/linux/scatterlist.h b/tools/virtio/linux/scatterlist.h index b2cf7d0f613..68c9e2adc99 100644 --- a/tools/virtio/linux/scatterlist.h +++ b/tools/virtio/linux/scatterlist.h @@ -125,6 +125,22 @@ static inline void sg_mark_end(struct scatterlist *sg) sg->page_link &= ~0x01; } +/** + * sg_unmark_end - Undo setting the end of the scatterlist + * @sg: SG entryScatterlist + * + * Description: + * Removes the termination marker from the given entry of the scatterlist. + * + **/ +static inline void sg_unmark_end(struct scatterlist *sg) +{ +#ifdef CONFIG_DEBUG_SG + BUG_ON(sg->sg_magic != SG_MAGIC); +#endif + sg->page_link &= ~0x02; +} + static inline struct scatterlist *sg_next(struct scatterlist *sg) { #ifdef CONFIG_DEBUG_SG diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h index e4af6591f5f..5fa612ad932 100644 --- a/tools/virtio/linux/virtio.h +++ b/tools/virtio/linux/virtio.h @@ -56,6 +56,13 @@ int virtqueue_add_buf(struct virtqueue *vq, void *data, gfp_t gfp); +int virtqueue_add_sgs(struct virtqueue *vq, + struct scatterlist *sgs[], + unsigned int out_sgs, + unsigned int in_sgs, + void *data, + gfp_t gfp); + void virtqueue_kick(struct virtqueue *vq); void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len); -- cgit v1.2.3-70-g09d2 From 282edb36499042a92b71f052f51754ae7ed936e4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Mar 2013 15:44:26 +1030 Subject: virtio_ring: virtqueue_add_outbuf / virtqueue_add_inbuf. These are specialized versions of virtqueue_add_buf(), which cover over 80% of cases and are far clearer. In particular, the scatterlists passed to these functions don't have to be clean (ie. we ignore end markers). Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/virtio.h | 10 ++++++++++ 2 files changed, 54 insertions(+) (limited to 'include') diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index a78ad459cc8..5217baf5528 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -365,6 +365,50 @@ int virtqueue_add_sgs(struct virtqueue *_vq, } EXPORT_SYMBOL_GPL(virtqueue_add_sgs); +/** + * virtqueue_add_outbuf - expose output buffers to other end + * @vq: the struct virtqueue we're talking about. + * @sgs: array of scatterlists (need not be terminated!) + * @num: the number of scatterlists readable by other side + * @data: the token identifying the buffer. + * @gfp: how to do memory allocations (if necessary). + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error (ie. ENOSPC, ENOMEM). + */ +int virtqueue_add_outbuf(struct virtqueue *vq, + struct scatterlist sg[], unsigned int num, + void *data, + gfp_t gfp) +{ + return virtqueue_add(vq, &sg, sg_next_arr, num, 0, 1, 0, data, gfp); +} +EXPORT_SYMBOL_GPL(virtqueue_add_outbuf); + +/** + * virtqueue_add_inbuf - expose input buffers to other end + * @vq: the struct virtqueue we're talking about. + * @sgs: array of scatterlists (need not be terminated!) + * @num: the number of scatterlists writable by other side + * @data: the token identifying the buffer. + * @gfp: how to do memory allocations (if necessary). + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error (ie. ENOSPC, ENOMEM). + */ +int virtqueue_add_inbuf(struct virtqueue *vq, + struct scatterlist sg[], unsigned int num, + void *data, + gfp_t gfp) +{ + return virtqueue_add(vq, &sg, sg_next_arr, 0, num, 0, 1, data, gfp); +} +EXPORT_SYMBOL_GPL(virtqueue_add_inbuf); + /** * virtqueue_kick_prepare - first half of split virtqueue_kick call. * @vq: the struct virtqueue diff --git a/include/linux/virtio.h b/include/linux/virtio.h index ac80288b292..833f17b6a74 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -41,6 +41,16 @@ int virtqueue_add_buf(struct virtqueue *vq, void *data, gfp_t gfp); +int virtqueue_add_outbuf(struct virtqueue *vq, + struct scatterlist sg[], unsigned int num, + void *data, + gfp_t gfp); + +int virtqueue_add_inbuf(struct virtqueue *vq, + struct scatterlist sg[], unsigned int num, + void *data, + gfp_t gfp); + int virtqueue_add_sgs(struct virtqueue *vq, struct scatterlist *sgs[], unsigned int out_sgs, -- cgit v1.2.3-70-g09d2 From 4480764f57ba494e3f64003e13223c0b5ec6a2ca Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 19 Mar 2013 14:58:43 -0700 Subject: ASoC:: max98090: Remove executable bit Source files shouldn't have the executable bit set. Signed-off-by: Joe Perches Signed-off-by: Mark Brown --- include/sound/max98090.h | 0 sound/soc/codecs/max98090.c | 0 sound/soc/codecs/max98090.h | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 include/sound/max98090.h mode change 100755 => 100644 sound/soc/codecs/max98090.c mode change 100755 => 100644 sound/soc/codecs/max98090.h (limited to 'include') diff --git a/include/sound/max98090.h b/include/sound/max98090.h old mode 100755 new mode 100644 diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c old mode 100755 new mode 100644 diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h old mode 100755 new mode 100644 -- cgit v1.2.3-70-g09d2 From 081aa458c38ba576bdd4265fc807fa95b48b9e79 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 13 Mar 2013 09:17:09 +0800 Subject: cgroup: consolidate cgroup_attach_task() and cgroup_attach_proc() These two functions share most of the code. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 3 +- kernel/cgroup.c | 109 +++++++++---------------------------------------- kernel/cpuset.c | 2 +- 3 files changed, 23 insertions(+), 91 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 7e818a3ef60..01c48c6806d 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -693,7 +693,8 @@ struct task_struct *cgroup_iter_next(struct cgroup *cgrp, struct cgroup_iter *it); void cgroup_iter_end(struct cgroup *cgrp, struct cgroup_iter *it); int cgroup_scan_tasks(struct cgroup_scanner *scan); -int cgroup_attach_task(struct cgroup *, struct task_struct *); +int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk, + bool threadgroup); int cgroup_attach_task_all(struct task_struct *from, struct task_struct *); /* diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 54689fc008f..04fa2abf94b 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -59,7 +59,7 @@ #include /* TODO: replace with more sophisticated array */ #include #include -#include /* used in cgroup_attach_proc */ +#include /* used in cgroup_attach_task */ #include #include @@ -1943,82 +1943,6 @@ static void cgroup_task_migrate(struct cgroup *cgrp, struct cgroup *oldcgrp, put_css_set(oldcg); } -/** - * cgroup_attach_task - attach task 'tsk' to cgroup 'cgrp' - * @cgrp: the cgroup the task is attaching to - * @tsk: the task to be attached - * - * Call with cgroup_mutex and threadgroup locked. May take task_lock of - * @tsk during call. - */ -int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) -{ - int retval = 0; - struct cgroup_subsys *ss, *failed_ss = NULL; - struct cgroup *oldcgrp; - struct cgroupfs_root *root = cgrp->root; - struct cgroup_taskset tset = { }; - struct css_set *newcg; - - /* @tsk either already exited or can't exit until the end */ - if (tsk->flags & PF_EXITING) - return -ESRCH; - - /* Nothing to do if the task is already in that cgroup */ - oldcgrp = task_cgroup_from_root(tsk, root); - if (cgrp == oldcgrp) - return 0; - - tset.single.task = tsk; - tset.single.cgrp = oldcgrp; - - for_each_subsys(root, ss) { - if (ss->can_attach) { - retval = ss->can_attach(cgrp, &tset); - if (retval) { - /* - * Remember on which subsystem the can_attach() - * failed, so that we only call cancel_attach() - * against the subsystems whose can_attach() - * succeeded. (See below) - */ - failed_ss = ss; - goto out; - } - } - } - - newcg = find_css_set(tsk->cgroups, cgrp); - if (!newcg) { - retval = -ENOMEM; - goto out; - } - - cgroup_task_migrate(cgrp, oldcgrp, tsk, newcg); - - for_each_subsys(root, ss) { - if (ss->attach) - ss->attach(cgrp, &tset); - } - -out: - if (retval) { - for_each_subsys(root, ss) { - if (ss == failed_ss) - /* - * This subsystem was the one that failed the - * can_attach() check earlier, so we don't need - * to call cancel_attach() against it or any - * remaining subsystems. - */ - break; - if (ss->cancel_attach) - ss->cancel_attach(cgrp, &tset); - } - } - return retval; -} - /** * cgroup_attach_task_all - attach task 'tsk' to all cgroups of task 'from' * @from: attach to all cgroups of a given task @@ -2033,7 +1957,7 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk) for_each_active_root(root) { struct cgroup *from_cg = task_cgroup_from_root(from, root); - retval = cgroup_attach_task(from_cg, tsk); + retval = cgroup_attach_task(from_cg, tsk, false); if (retval) break; } @@ -2044,21 +1968,22 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk) EXPORT_SYMBOL_GPL(cgroup_attach_task_all); /** - * cgroup_attach_proc - attach all threads in a threadgroup to a cgroup + * cgroup_attach_task - attach a task or a whole threadgroup to a cgroup * @cgrp: the cgroup to attach to - * @leader: the threadgroup leader task_struct of the group to be attached + * @tsk: the task or the leader of the threadgroup to be attached + * @threadgroup: attach the whole threadgroup? * * Call holding cgroup_mutex and the group_rwsem of the leader. Will take - * task_lock of each thread in leader's threadgroup individually in turn. + * task_lock of @tsk or each thread in the threadgroup individually in turn. */ -static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) +int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk, + bool threadgroup) { int retval, i, group_size; struct cgroup_subsys *ss, *failed_ss = NULL; - /* guaranteed to be initialized later, but the compiler needs this */ struct cgroupfs_root *root = cgrp->root; /* threadgroup list cursor and array */ - struct task_struct *tsk; + struct task_struct *leader = tsk; struct task_and_cgroup *tc; struct flex_array *group; struct cgroup_taskset tset = { }; @@ -2070,7 +1995,10 @@ static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) * group - group_rwsem prevents new threads from appearing, and if * threads exit, this will just be an over-estimate. */ - group_size = get_nr_threads(leader); + if (threadgroup) + group_size = get_nr_threads(tsk); + else + group_size = 1; /* flex_array supports very large thread-groups better than kmalloc. */ group = flex_array_alloc(sizeof(*tc), group_size, GFP_KERNEL); if (!group) @@ -2080,7 +2008,6 @@ static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) if (retval) goto out_free_group_list; - tsk = leader; i = 0; /* * Prevent freeing of tasks while we take a snapshot. Tasks that are @@ -2109,6 +2036,9 @@ static int cgroup_attach_proc(struct cgroup *cgrp, struct task_struct *leader) retval = flex_array_put(group, i, &ent, GFP_ATOMIC); BUG_ON(retval != 0); i++; + + if (!threadgroup) + break; } while_each_thread(leader, tsk); rcu_read_unlock(); /* remember the number of threads in the array for later. */ @@ -2262,9 +2192,10 @@ retry_find_task: put_task_struct(tsk); goto retry_find_task; } - ret = cgroup_attach_proc(cgrp, tsk); - } else - ret = cgroup_attach_task(cgrp, tsk); + } + + ret = cgroup_attach_task(cgrp, tsk, threadgroup); + threadgroup_unlock(tsk); put_task_struct(tsk); diff --git a/kernel/cpuset.c b/kernel/cpuset.c index efbfca7a33e..98d458aad78 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -2008,7 +2008,7 @@ static void cpuset_do_move_task(struct task_struct *tsk, struct cgroup *new_cgroup = scan->data; cgroup_lock(); - cgroup_attach_task(new_cgroup, tsk); + cgroup_attach_task(new_cgroup, tsk, false); cgroup_unlock(); } -- cgit v1.2.3-70-g09d2 From f77668dc25b27270fe589031b22c432c3462b1d8 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 19 Mar 2013 06:39:30 +0000 Subject: net: flow_dissector: add __skb_get_poff to get a start offset to payload __skb_get_poff() returns the offset to the payload as far as it could be dissected. The main user is currently BPF, so that we can dynamically truncate packets without needing to push actual payload to the user space and instead can analyze headers only. Suggested-by: Eric Dumazet Signed-off-by: Daniel Borkmann Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 2 ++ net/core/flow_dissector.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index b66ecc6ef10..497412165b1 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -2840,6 +2840,8 @@ static inline void skb_checksum_none_assert(const struct sk_buff *skb) bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off); +u32 __skb_get_poff(const struct sk_buff *skb); + /** * skb_head_is_locked - Determine if the skb->head is locked down * @skb: skb to check diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index f4be293bab9..00ee068efc1 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -5,6 +5,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -228,6 +232,59 @@ u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb, } EXPORT_SYMBOL(__skb_tx_hash); +/* __skb_get_poff() returns the offset to the payload as far as it could + * be dissected. The main user is currently BPF, so that we can dynamically + * truncate packets without needing to push actual payload to the user + * space and can analyze headers only, instead. + */ +u32 __skb_get_poff(const struct sk_buff *skb) +{ + struct flow_keys keys; + u32 poff = 0; + + if (!skb_flow_dissect(skb, &keys)) + return 0; + + poff += keys.thoff; + switch (keys.ip_proto) { + case IPPROTO_TCP: { + const struct tcphdr *tcph; + struct tcphdr _tcph; + + tcph = skb_header_pointer(skb, poff, sizeof(_tcph), &_tcph); + if (!tcph) + return poff; + + poff += max_t(u32, sizeof(struct tcphdr), tcph->doff * 4); + break; + } + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + poff += sizeof(struct udphdr); + break; + /* For the rest, we do not really care about header + * extensions at this point for now. + */ + case IPPROTO_ICMP: + poff += sizeof(struct icmphdr); + break; + case IPPROTO_ICMPV6: + poff += sizeof(struct icmp6hdr); + break; + case IPPROTO_IGMP: + poff += sizeof(struct igmphdr); + break; + case IPPROTO_DCCP: + poff += sizeof(struct dccp_hdr); + break; + case IPPROTO_SCTP: + poff += sizeof(struct sctphdr); + break; + } + + return poff; +} + static inline u16 dev_cap_txqueue(struct net_device *dev, u16 queue_index) { if (unlikely(queue_index >= dev->real_num_tx_queues)) { -- cgit v1.2.3-70-g09d2 From 3e5289d5e3f98b7b5b8cac32e9e5a7004c067436 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 19 Mar 2013 06:39:31 +0000 Subject: filter: add ANC_PAY_OFFSET instruction for loading payload start offset It is very useful to do dynamic truncation of packets. In particular, we're interested to push the necessary header bytes to the user space and cut off user payload that should probably not be transferred for some reasons (e.g. privacy, speed, or others). With the ancillary extension PAY_OFFSET, we can load it into the accumulator, and return it. E.g. in bpfc syntax ... ld #poff ; { 0x20, 0, 0, 0xfffff034 }, ret a ; { 0x16, 0, 0, 0x00000000 }, ... as a filter will accomplish this without having to do a big hackery in a BPF filter itself. Follow-up JIT implementations are welcome. Thanks to Eric Dumazet for suggesting and discussing this during the Netfilter Workshop in Copenhagen. Suggested-by: Eric Dumazet Signed-off-by: Daniel Borkmann Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/filter.h | 1 + include/uapi/linux/filter.h | 3 ++- net/core/filter.c | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index c45eabc135e..d2059cb4e46 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -126,6 +126,7 @@ enum { BPF_S_ANC_SECCOMP_LD_W, BPF_S_ANC_VLAN_TAG, BPF_S_ANC_VLAN_TAG_PRESENT, + BPF_S_ANC_PAY_OFFSET, }; #endif /* __LINUX_FILTER_H__ */ diff --git a/include/uapi/linux/filter.h b/include/uapi/linux/filter.h index 9cfde694109..8eb9ccaa5b4 100644 --- a/include/uapi/linux/filter.h +++ b/include/uapi/linux/filter.h @@ -129,7 +129,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */ #define SKF_AD_ALU_XOR_X 40 #define SKF_AD_VLAN_TAG 44 #define SKF_AD_VLAN_TAG_PRESENT 48 -#define SKF_AD_MAX 52 +#define SKF_AD_PAY_OFFSET 52 +#define SKF_AD_MAX 56 #define SKF_NET_OFF (-0x100000) #define SKF_LL_OFF (-0x200000) diff --git a/net/core/filter.c b/net/core/filter.c index 2e20b55a783..dad2a178f9f 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -348,6 +348,9 @@ load_b: case BPF_S_ANC_VLAN_TAG_PRESENT: A = !!vlan_tx_tag_present(skb); continue; + case BPF_S_ANC_PAY_OFFSET: + A = __skb_get_poff(skb); + continue; case BPF_S_ANC_NLATTR: { struct nlattr *nla; @@ -612,6 +615,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) ANCILLARY(ALU_XOR_X); ANCILLARY(VLAN_TAG); ANCILLARY(VLAN_TAG_PRESENT); + ANCILLARY(PAY_OFFSET); } /* ancillary operation unknown or unsupported */ @@ -814,6 +818,7 @@ static void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to) [BPF_S_ANC_SECCOMP_LD_W] = BPF_LD|BPF_B|BPF_ABS, [BPF_S_ANC_VLAN_TAG] = BPF_LD|BPF_B|BPF_ABS, [BPF_S_ANC_VLAN_TAG_PRESENT] = BPF_LD|BPF_B|BPF_ABS, + [BPF_S_ANC_PAY_OFFSET] = BPF_LD|BPF_B|BPF_ABS, [BPF_S_LD_W_LEN] = BPF_LD|BPF_W|BPF_LEN, [BPF_S_LD_W_IND] = BPF_LD|BPF_W|BPF_IND, [BPF_S_LD_H_IND] = BPF_LD|BPF_H|BPF_IND, -- cgit v1.2.3-70-g09d2 From 2b5faa4c553f90ee2dde1d976b220b1ca9741ef0 Mon Sep 17 00:00:00 2001 From: Jesper Derehag Date: Tue, 19 Mar 2013 20:50:05 +0000 Subject: connector: Added coredumping event to the process connector Process connector can now also detect coredumping events. Main aim of patch is get notified at start of coredumping, instead of having to wait for it to finish and then being notified through EXIT event. Could be used for instance by process-managers that want to get notified as soon as possible about process failures, and not necessarily beeing notified after coredump, which could be in the order of minutes depending on size of coredump, piping and so on. Signed-off-by: Jesper Derehag Signed-off-by: David S. Miller --- drivers/connector/cn_proc.c | 25 +++++++++++++++++++++++++ include/linux/cn_proc.h | 4 ++++ include/uapi/linux/cn_proc.h | 10 +++++++++- kernel/signal.c | 2 ++ 4 files changed, 40 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c index 1110478dd0f..08ae128cce9 100644 --- a/drivers/connector/cn_proc.c +++ b/drivers/connector/cn_proc.c @@ -232,6 +232,31 @@ void proc_comm_connector(struct task_struct *task) cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); } +void proc_coredump_connector(struct task_struct *task) +{ + struct cn_msg *msg; + struct proc_event *ev; + __u8 buffer[CN_PROC_MSG_SIZE]; + struct timespec ts; + + if (atomic_read(&proc_event_num_listeners) < 1) + return; + + msg = (struct cn_msg *)buffer; + ev = (struct proc_event *)msg->data; + get_seq(&msg->seq, &ev->cpu); + ktime_get_ts(&ts); /* get high res monotonic timestamp */ + put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); + ev->what = PROC_EVENT_COREDUMP; + ev->event_data.coredump.process_pid = task->pid; + ev->event_data.coredump.process_tgid = task->tgid; + + memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); + msg->ack = 0; /* not used */ + msg->len = sizeof(*ev); + cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); +} + void proc_exit_connector(struct task_struct *task) { struct cn_msg *msg; diff --git a/include/linux/cn_proc.h b/include/linux/cn_proc.h index 2c1bc1ea04e..1d5b02a96c4 100644 --- a/include/linux/cn_proc.h +++ b/include/linux/cn_proc.h @@ -26,6 +26,7 @@ void proc_id_connector(struct task_struct *task, int which_id); void proc_sid_connector(struct task_struct *task); void proc_ptrace_connector(struct task_struct *task, int which_id); void proc_comm_connector(struct task_struct *task); +void proc_coredump_connector(struct task_struct *task); void proc_exit_connector(struct task_struct *task); #else static inline void proc_fork_connector(struct task_struct *task) @@ -48,6 +49,9 @@ static inline void proc_ptrace_connector(struct task_struct *task, int ptrace_id) {} +static inline void proc_coredump_connector(struct task_struct *task) +{} + static inline void proc_exit_connector(struct task_struct *task) {} #endif /* CONFIG_PROC_EVENTS */ diff --git a/include/uapi/linux/cn_proc.h b/include/uapi/linux/cn_proc.h index 0d7b49973bb..f6c271035bb 100644 --- a/include/uapi/linux/cn_proc.h +++ b/include/uapi/linux/cn_proc.h @@ -56,7 +56,9 @@ struct proc_event { PROC_EVENT_PTRACE = 0x00000100, PROC_EVENT_COMM = 0x00000200, /* "next" should be 0x00000400 */ - /* "last" is the last process event: exit */ + /* "last" is the last process event: exit, + * while "next to last" is coredumping event */ + PROC_EVENT_COREDUMP = 0x40000000, PROC_EVENT_EXIT = 0x80000000 } what; __u32 cpu; @@ -110,11 +112,17 @@ struct proc_event { char comm[16]; } comm; + struct coredump_proc_event { + __kernel_pid_t process_pid; + __kernel_pid_t process_tgid; + } coredump; + struct exit_proc_event { __kernel_pid_t process_pid; __kernel_pid_t process_tgid; __u32 exit_code, exit_signal; } exit; + } event_data; }; diff --git a/kernel/signal.c b/kernel/signal.c index dd72567767d..497330ec2ae 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -32,6 +32,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -2350,6 +2351,7 @@ relock: if (sig_kernel_coredump(signr)) { if (print_fatal_signals) print_fatal_signal(info->si_signo); + proc_coredump_connector(current); /* * If it was able to dump core, this kills all * other threads in the group and synchronizes with -- cgit v1.2.3-70-g09d2 From 6cd2c7db41eab204b6474534df4ca68a7dc53d86 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 21 Mar 2013 14:20:12 +0200 Subject: videomode: videomode_from_timing work We currently have videomode_from_timing(), which takes one display_timing entry from display_timings. To make it easier to use display_timing without display_timings, this patch renames videomode_from_timing() to videomode_from_timings(), and adds a new videomode_from_timing() which just converts a given display_timing to videomode. Signed-off-by: Tomi Valkeinen Cc: Steffen Trumtrar --- drivers/gpu/drm/tilcdc/tilcdc_panel.c | 2 +- drivers/video/of_videomode.c | 2 +- drivers/video/videomode.c | 25 ++++++++++++++++--------- include/video/videomode.h | 15 +++++++++++++-- 4 files changed, 31 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 580b74e2022..90ee4978637 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -173,7 +173,7 @@ static int panel_connector_get_modes(struct drm_connector *connector) struct drm_display_mode *mode = drm_mode_create(dev); struct videomode vm; - if (videomode_from_timing(timings, &vm, i)) + if (videomode_from_timings(timings, &vm, i)) break; drm_display_mode_from_videomode(&vm, mode); diff --git a/drivers/video/of_videomode.c b/drivers/video/of_videomode.c index 5b8066cd397..111c2d1911d 100644 --- a/drivers/video/of_videomode.c +++ b/drivers/video/of_videomode.c @@ -43,7 +43,7 @@ int of_get_videomode(struct device_node *np, struct videomode *vm, if (index == OF_USE_NATIVE_MODE) index = disp->native_mode; - ret = videomode_from_timing(disp, vm, index); + ret = videomode_from_timings(disp, vm, index); if (ret) return ret; diff --git a/drivers/video/videomode.c b/drivers/video/videomode.c index a3d95f263cd..df375c96c5d 100644 --- a/drivers/video/videomode.c +++ b/drivers/video/videomode.c @@ -11,15 +11,9 @@ #include