From 2b144498350860b6ee9dc57ff27a93ad488de5dc Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Thu, 9 Feb 2012 14:56:42 +0530 Subject: uprobes, mm, x86: Add the ability to install and remove uprobes breakpoints Add uprobes support to the core kernel, with x86 support. This commit adds the kernel facilities, the actual uprobes user-space ABI and perf probe support comes in later commits. General design: Uprobes are maintained in an rb-tree indexed by inode and offset (the offset here is from the start of the mapping). For a unique (inode, offset) tuple, there can be at most one uprobe in the rb-tree. Since the (inode, offset) tuple identifies a unique uprobe, more than one user may be interested in the same uprobe. This provides the ability to connect multiple 'consumers' to the same uprobe. Each consumer defines a handler and a filter (optional). The 'handler' is run every time the uprobe is hit, if it matches the 'filter' criteria. The first consumer of a uprobe causes the breakpoint to be inserted at the specified address and subsequent consumers are appended to this list. On subsequent probes, the consumer gets appended to the existing list of consumers. The breakpoint is removed when the last consumer unregisters. For all other unregisterations, the consumer is removed from the list of consumers. Given a inode, we get a list of the mms that have mapped the inode. Do the actual registration if mm maps the page where a probe needs to be inserted/removed. We use a temporary list to walk through the vmas that map the inode. - The number of maps that map the inode, is not known before we walk the rmap and keeps changing. - extending vm_area_struct wasn't recommended, it's a size-critical data structure. - There can be more than one maps of the inode in the same mm. We add callbacks to the mmap methods to keep an eye on text vmas that are of interest to uprobes. When a vma of interest is mapped, we insert the breakpoint at the right address. Uprobe works by replacing the instruction at the address defined by (inode, offset) with the arch specific breakpoint instruction. We save a copy of the original instruction at the uprobed address. This is needed for: a. executing the instruction out-of-line (xol). b. instruction analysis for any subsequent fixups. c. restoring the instruction back when the uprobe is unregistered. We insert or delete a breakpoint instruction, and this breakpoint instruction is assumed to be the smallest instruction available on the platform. For fixed size instruction platforms this is trivially true, for variable size instruction platforms the breakpoint instruction is typically the smallest (often a single byte). Writing the instruction is done by COWing the page and changing the instruction during the copy, this even though most platforms allow atomic writes of the breakpoint instruction. This also mirrors the behaviour of a ptrace() memory write to a PRIVATE file map. The core worker is derived from KSM's replace_page() logic. In essence, similar to KSM: a. allocate a new page and copy over contents of the page that has the uprobed vaddr b. modify the copy and insert the breakpoint at the required address c. switch the original page with the copy containing the breakpoint d. flush page tables. replace_page() is being replicated here because of some minor changes in the type of pages and also because Hugh Dickins had plans to improve replace_page() for KSM specific work. Instruction analysis on x86 is based on instruction decoder and determines if an instruction can be probed and determines the necessary fixups after singlestep. Instruction analysis is done at probe insertion time so that we avoid having to repeat the same analysis every time a probe is hit. A lot of code here is due to the improvement/suggestions/inputs from Peter Zijlstra. Changelog: (v10): - Add code to clear REX.B prefix as suggested by Denys Vlasenko and Masami Hiramatsu. (v9): - Use insn_offset_modrm as suggested by Masami Hiramatsu. (v7): Handle comments from Peter Zijlstra: - Dont take reference to inode. (expect inode to uprobe_register to be sane). - Use PTR_ERR to set the return value. - No need to take reference to inode. - use PTR_ERR to return error value. - register and uprobe_unregister share code. (v5): - Modified del_consumer as per comments from Peter. - Drop reference to inode before dropping reference to uprobe. - Use i_size_read(inode) instead of inode->i_size. - Ensure uprobe->consumers is NULL, before __uprobe_unregister() is called. - Includes errno.h as recommended by Stephen Rothwell to fix a build issue on sparc defconfig - Remove restrictions while unregistering. - Earlier code leaked inode references under some conditions while registering/unregistering. - Continue the vma-rmap walk even if the intermediate vma doesnt meet the requirements. - Validate the vma found by find_vma before inserting/removing the breakpoint - Call del_consumer under mutex_lock. - Use hash locks. - Handle mremap. - Introduce find_least_offset_node() instead of close match logic in find_uprobe - Uprobes no more depends on MM_OWNER; No reference to task_structs while inserting/removing a probe. - Uses read_mapping_page instead of grab_cache_page so that the pages have valid content. - pass NULL to get_user_pages for the task parameter. - call SetPageUptodate on the new page allocated in write_opcode. - fix leaking a reference to the new page under certain conditions. - Include Instruction Decoder if Uprobes gets defined. - Remove const attributes for instruction prefix arrays. - Uses mm_context to know if the application is 32 bit. Signed-off-by: Srikar Dronamraju Also-written-by: Jim Keniston Reviewed-by: Peter Zijlstra Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Roland McGrath Cc: Masami Hiramatsu Cc: Arnaldo Carvalho de Melo Cc: Anton Arapov Cc: Ananth N Mavinakayanahalli Cc: Stephen Rothwell Cc: Denys Vlasenko Cc: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Cc: Linux-mm Link: http://lkml.kernel.org/r/20120209092642.GE16600@linux.vnet.ibm.com [ Made various small edits to the commit log ] Signed-off-by: Ingo Molnar --- include/linux/uprobes.h | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 include/linux/uprobes.h (limited to 'include/linux') diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h new file mode 100644 index 000000000000..f1d13fd140f2 --- /dev/null +++ b/include/linux/uprobes.h @@ -0,0 +1,98 @@ +#ifndef _LINUX_UPROBES_H +#define _LINUX_UPROBES_H +/* + * Userspace Probes (UProbes) + * + * 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. + * + * Copyright (C) IBM Corporation, 2008-2011 + * Authors: + * Srikar Dronamraju + * Jim Keniston + */ + +#include +#include + +struct vm_area_struct; +#ifdef CONFIG_ARCH_SUPPORTS_UPROBES +#include +#else + +typedef u8 uprobe_opcode_t; +struct uprobe_arch_info {}; + +#define MAX_UINSN_BYTES 4 +#endif + +#define uprobe_opcode_sz sizeof(uprobe_opcode_t) + +/* flags that denote/change uprobes behaviour */ +/* Have a copy of original instruction */ +#define UPROBES_COPY_INSN 0x1 +/* Dont run handlers when first register/ last unregister in progress*/ +#define UPROBES_RUN_HANDLER 0x2 + +struct uprobe_consumer { + int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); + /* + * filter is optional; If a filter exists, handler is run + * if and only if filter returns true. + */ + bool (*filter)(struct uprobe_consumer *self, struct task_struct *task); + + struct uprobe_consumer *next; +}; + +struct uprobe { + struct rb_node rb_node; /* node in the rb tree */ + atomic_t ref; + struct rw_semaphore consumer_rwsem; + struct list_head pending_list; + struct uprobe_arch_info arch_info; + struct uprobe_consumer *consumers; + struct inode *inode; /* Also hold a ref to inode */ + loff_t offset; + int flags; + u8 insn[MAX_UINSN_BYTES]; +}; + +#ifdef CONFIG_UPROBES +extern int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, + unsigned long vaddr); +extern int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, + unsigned long vaddr, bool verify); +extern bool __weak is_bkpt_insn(uprobe_opcode_t *insn); +extern int register_uprobe(struct inode *inode, loff_t offset, + struct uprobe_consumer *consumer); +extern void unregister_uprobe(struct inode *inode, loff_t offset, + struct uprobe_consumer *consumer); +extern int mmap_uprobe(struct vm_area_struct *vma); +#else /* CONFIG_UPROBES is not defined */ +static inline int register_uprobe(struct inode *inode, loff_t offset, + struct uprobe_consumer *consumer) +{ + return -ENOSYS; +} +static inline void unregister_uprobe(struct inode *inode, loff_t offset, + struct uprobe_consumer *consumer) +{ +} +static inline int mmap_uprobe(struct vm_area_struct *vma) +{ + return 0; +} +#endif /* CONFIG_UPROBES */ +#endif /* _LINUX_UPROBES_H */ -- cgit v1.2.3 From 7b2d81d48a2d8e37efb6ce7b4d5ef58822b30d89 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 17 Feb 2012 09:27:41 +0100 Subject: uprobes/core: Clean up, refactor and improve the code Make the uprobes code readable to me: - improve the Kconfig text so that a mere mortal gets some idea what CONFIG_UPROBES=y is really about - do trivial renames to standardize around the uprobes_*() namespace - clean up and simplify various code flow details - separate basic blocks of functionality - line break artifact and white space related removal - use standard local varible definition blocks - use vertical spacing to make things more readable - remove unnecessary volatile - restructure comment blocks to make them more uniform and more readable in general Cc: Srikar Dronamraju Cc: Jim Keniston Cc: Peter Zijlstra Cc: Oleg Nesterov Cc: Masami Hiramatsu Cc: Arnaldo Carvalho de Melo Cc: Anton Arapov Cc: Ananth N Mavinakayanahalli Link: http://lkml.kernel.org/n/tip-ewbwhb8o6navvllsauu7k07p@git.kernel.org Signed-off-by: Ingo Molnar --- arch/Kconfig | 14 ++- arch/x86/include/asm/uprobes.h | 17 ++-- arch/x86/kernel/uprobes.c | 129 ++++++++++++------------ include/linux/uprobes.h | 28 +++--- kernel/uprobes.c | 219 ++++++++++++++++++++++++----------------- mm/mmap.c | 12 +-- 6 files changed, 233 insertions(+), 186 deletions(-) (limited to 'include/linux') diff --git a/arch/Kconfig b/arch/Kconfig index 284f5898f526..cca5b545d806 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -66,13 +66,19 @@ config OPTPROBES depends on !PREEMPT config UPROBES - bool "User-space probes (EXPERIMENTAL)" + bool "Transparent user-space probes (EXPERIMENTAL)" depends on ARCH_SUPPORTS_UPROBES default n help - Uprobes enables kernel subsystems to establish probepoints - in user applications and execute handler functions when - the probepoints are hit. + Uprobes is the user-space counterpart to kprobes: they + enable instrumentation applications (such as 'perf probe') + to establish unintrusive probes in user-space binaries and + libraries, by executing handler functions when the probes + are hit by user-space applications. + + ( These probes come in the form of single-byte breakpoints, + managed by the kernel and kept transparent to the probed + application. ) If in doubt, say "N". diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 8208234391ff..072df3902636 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -1,7 +1,7 @@ #ifndef _ASM_UPROBES_H #define _ASM_UPROBES_H /* - * Userspace Probes (UProbes) for x86 + * User-space Probes (UProbes) for x86 * * 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 @@ -24,19 +24,20 @@ */ typedef u8 uprobe_opcode_t; -#define MAX_UINSN_BYTES 16 -#define UPROBES_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ -#define UPROBES_BKPT_INSN 0xcc -#define UPROBES_BKPT_INSN_SIZE 1 +#define MAX_UINSN_BYTES 16 +#define UPROBES_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ + +#define UPROBES_BKPT_INSN 0xcc +#define UPROBES_BKPT_INSN_SIZE 1 struct uprobe_arch_info { - u16 fixups; + u16 fixups; #ifdef CONFIG_X86_64 - unsigned long rip_rela_target_address; + unsigned long rip_rela_target_address; #endif }; struct uprobe; -extern int analyze_insn(struct mm_struct *mm, struct uprobe *uprobe); +extern int arch_uprobes_analyze_insn(struct mm_struct *mm, struct uprobe *uprobe); #endif /* _ASM_UPROBES_H */ diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 2a301bb91bdb..cf2a18498425 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -1,5 +1,5 @@ /* - * Userspace Probes (UProbes) for x86 + * User-space Probes (UProbes) for x86 * * 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 @@ -20,7 +20,6 @@ * Srikar Dronamraju * Jim Keniston */ - #include #include #include @@ -42,10 +41,10 @@ #define UPROBES_FIX_RIP_CX 0x4000 /* Adaptations for mhiramat x86 decoder v14. */ -#define OPCODE1(insn) ((insn)->opcode.bytes[0]) -#define OPCODE2(insn) ((insn)->opcode.bytes[1]) -#define OPCODE3(insn) ((insn)->opcode.bytes[2]) -#define MODRM_REG(insn) X86_MODRM_REG(insn->modrm.value) +#define OPCODE1(insn) ((insn)->opcode.bytes[0]) +#define OPCODE2(insn) ((insn)->opcode.bytes[1]) +#define OPCODE3(insn) ((insn)->opcode.bytes[2]) +#define MODRM_REG(insn) X86_MODRM_REG(insn->modrm.value) #define W(row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf)\ (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \ @@ -55,7 +54,7 @@ << (row % 32)) #ifdef CONFIG_X86_64 -static volatile u32 good_insns_64[256 / 32] = { +static u32 good_insns_64[256 / 32] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* ---------------------------------------------- */ W(0x00, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 00 */ @@ -81,7 +80,7 @@ static volatile u32 good_insns_64[256 / 32] = { /* Good-instruction tables for 32-bit apps */ -static volatile u32 good_insns_32[256 / 32] = { +static u32 good_insns_32[256 / 32] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* ---------------------------------------------- */ W(0x00, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0) | /* 00 */ @@ -105,7 +104,7 @@ static volatile u32 good_insns_32[256 / 32] = { }; /* Using this for both 64-bit and 32-bit apps */ -static volatile u32 good_2byte_insns[256 / 32] = { +static u32 good_2byte_insns[256 / 32] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* ---------------------------------------------- */ W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) | /* 00 */ @@ -132,42 +131,47 @@ static volatile u32 good_2byte_insns[256 / 32] = { /* * opcodes we'll probably never support: - * 6c-6d, e4-e5, ec-ed - in - * 6e-6f, e6-e7, ee-ef - out - * cc, cd - int3, int - * cf - iret - * d6 - illegal instruction - * f1 - int1/icebp - * f4 - hlt - * fa, fb - cli, sti - * 0f - lar, lsl, syscall, clts, sysret, sysenter, sysexit, invd, wbinvd, ud2 + * + * 6c-6d, e4-e5, ec-ed - in + * 6e-6f, e6-e7, ee-ef - out + * cc, cd - int3, int + * cf - iret + * d6 - illegal instruction + * f1 - int1/icebp + * f4 - hlt + * fa, fb - cli, sti + * 0f - lar, lsl, syscall, clts, sysret, sysenter, sysexit, invd, wbinvd, ud2 * * invalid opcodes in 64-bit mode: - * 06, 0e, 16, 1e, 27, 2f, 37, 3f, 60-62, 82, c4-c5, d4-d5 * - * 63 - we support this opcode in x86_64 but not in i386. + * 06, 0e, 16, 1e, 27, 2f, 37, 3f, 60-62, 82, c4-c5, d4-d5 + * 63 - we support this opcode in x86_64 but not in i386. * * opcodes we may need to refine support for: - * 0f - 2-byte instructions: For many of these instructions, the validity - * depends on the prefix and/or the reg field. On such instructions, we - * just consider the opcode combination valid if it corresponds to any - * valid instruction. - * 8f - Group 1 - only reg = 0 is OK - * c6-c7 - Group 11 - only reg = 0 is OK - * d9-df - fpu insns with some illegal encodings - * f2, f3 - repnz, repz prefixes. These are also the first byte for - * certain floating-point instructions, such as addsd. - * fe - Group 4 - only reg = 0 or 1 is OK - * ff - Group 5 - only reg = 0-6 is OK + * + * 0f - 2-byte instructions: For many of these instructions, the validity + * depends on the prefix and/or the reg field. On such instructions, we + * just consider the opcode combination valid if it corresponds to any + * valid instruction. + * + * 8f - Group 1 - only reg = 0 is OK + * c6-c7 - Group 11 - only reg = 0 is OK + * d9-df - fpu insns with some illegal encodings + * f2, f3 - repnz, repz prefixes. These are also the first byte for + * certain floating-point instructions, such as addsd. + * + * fe - Group 4 - only reg = 0 or 1 is OK + * ff - Group 5 - only reg = 0-6 is OK * * others -- Do we need to support these? - * 0f - (floating-point?) prefetch instructions - * 07, 17, 1f - pop es, pop ss, pop ds - * 26, 2e, 36, 3e - es:, cs:, ss:, ds: segment prefixes -- + * + * 0f - (floating-point?) prefetch instructions + * 07, 17, 1f - pop es, pop ss, pop ds + * 26, 2e, 36, 3e - es:, cs:, ss:, ds: segment prefixes -- * but 64 and 65 (fs: and gs:) seem to be used, so we support them - * 67 - addr16 prefix - * ce - into - * f0 - lock prefix + * 67 - addr16 prefix + * ce - into + * f0 - lock prefix */ /* @@ -182,11 +186,11 @@ static bool is_prefix_bad(struct insn *insn) for (i = 0; i < insn->prefixes.nbytes; i++) { switch (insn->prefixes.bytes[i]) { - case 0x26: /*INAT_PFX_ES */ - case 0x2E: /*INAT_PFX_CS */ - case 0x36: /*INAT_PFX_DS */ - case 0x3E: /*INAT_PFX_SS */ - case 0xF0: /*INAT_PFX_LOCK */ + case 0x26: /* INAT_PFX_ES */ + case 0x2E: /* INAT_PFX_CS */ + case 0x36: /* INAT_PFX_DS */ + case 0x3E: /* INAT_PFX_SS */ + case 0xF0: /* INAT_PFX_LOCK */ return true; } } @@ -201,12 +205,15 @@ static int validate_insn_32bits(struct uprobe *uprobe, struct insn *insn) insn_get_opcode(insn); if (is_prefix_bad(insn)) return -ENOTSUPP; + if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_32)) return 0; + if (insn->opcode.nbytes == 2) { if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns)) return 0; } + return -ENOTSUPP; } @@ -282,12 +289,12 @@ static void prepare_fixups(struct uprobe *uprobe, struct insn *insn) * disastrous. * * Some useful facts about rip-relative instructions: - * - There's always a modrm byte. - * - There's never a SIB byte. - * - The displacement is always 4 bytes. + * + * - There's always a modrm byte. + * - There's never a SIB byte. + * - The displacement is always 4 bytes. */ -static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, - struct insn *insn) +static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) { u8 *cursor; u8 reg; @@ -342,13 +349,12 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, } /* Target address = address of next instruction + (signed) offset */ - uprobe->arch_info.rip_rela_target_address = (long)insn->length - + insn->displacement.value; + uprobe->arch_info.rip_rela_target_address = (long)insn->length + insn->displacement.value; + /* Displacement field is gone; slide immediate field (if any) over. */ if (insn->immediate.nbytes) { cursor++; - memmove(cursor, cursor + insn->displacement.nbytes, - insn->immediate.nbytes); + memmove(cursor, cursor + insn->displacement.nbytes, insn->immediate.nbytes); } return; } @@ -361,8 +367,10 @@ static int validate_insn_64bits(struct uprobe *uprobe, struct insn *insn) insn_get_opcode(insn); if (is_prefix_bad(insn)) return -ENOTSUPP; + if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_64)) return 0; + if (insn->opcode.nbytes == 2) { if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns)) return 0; @@ -370,34 +378,31 @@ static int validate_insn_64bits(struct uprobe *uprobe, struct insn *insn) return -ENOTSUPP; } -static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, - struct insn *insn) +static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) { if (mm->context.ia32_compat) return validate_insn_32bits(uprobe, insn); return validate_insn_64bits(uprobe, insn); } -#else -static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, - struct insn *insn) +#else /* 32-bit: */ +static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) { - return; + /* No RIP-relative addressing on 32-bit */ } -static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, - struct insn *insn) +static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) { return validate_insn_32bits(uprobe, insn); } #endif /* CONFIG_X86_64 */ /** - * analyze_insn - instruction analysis including validity and fixups. + * arch_uprobes_analyze_insn - instruction analysis including validity and fixups. * @mm: the probed address space. * @uprobe: the probepoint information. * Return 0 on success or a -ve number on error. */ -int analyze_insn(struct mm_struct *mm, struct uprobe *uprobe) +int arch_uprobes_analyze_insn(struct mm_struct *mm, struct uprobe *uprobe) { int ret; struct insn insn; @@ -406,7 +411,9 @@ int analyze_insn(struct mm_struct *mm, struct uprobe *uprobe) ret = validate_insn_bits(mm, uprobe, &insn); if (ret != 0) return ret; + handle_riprel_insn(mm, uprobe, &insn); prepare_fixups(uprobe, &insn); + return 0; } diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index f1d13fd140f2..64e45f116b2a 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -1,7 +1,7 @@ #ifndef _LINUX_UPROBES_H #define _LINUX_UPROBES_H /* - * Userspace Probes (UProbes) + * User-space Probes (UProbes) * * 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 @@ -40,8 +40,10 @@ struct uprobe_arch_info {}; #define uprobe_opcode_sz sizeof(uprobe_opcode_t) /* flags that denote/change uprobes behaviour */ + /* Have a copy of original instruction */ #define UPROBES_COPY_INSN 0x1 + /* Dont run handlers when first register/ last unregister in progress*/ #define UPROBES_RUN_HANDLER 0x2 @@ -70,27 +72,23 @@ struct uprobe { }; #ifdef CONFIG_UPROBES -extern int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, - unsigned long vaddr); -extern int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, - unsigned long vaddr, bool verify); +extern int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr); +extern int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, bool verify); extern bool __weak is_bkpt_insn(uprobe_opcode_t *insn); -extern int register_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer); -extern void unregister_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer); -extern int mmap_uprobe(struct vm_area_struct *vma); +extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); +extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); +extern int uprobe_mmap(struct vm_area_struct *vma); #else /* CONFIG_UPROBES is not defined */ -static inline int register_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer) +static inline int +uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) { return -ENOSYS; } -static inline void unregister_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer) +static inline void +uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) { } -static inline int mmap_uprobe(struct vm_area_struct *vma) +static inline int uprobe_mmap(struct vm_area_struct *vma) { return 0; } diff --git a/kernel/uprobes.c b/kernel/uprobes.c index 72e8bb3b52cd..884817f1b0d3 100644 --- a/kernel/uprobes.c +++ b/kernel/uprobes.c @@ -1,5 +1,5 @@ /* - * Userspace Probes (UProbes) + * User-space Probes (UProbes) * * 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 @@ -29,24 +29,26 @@ #include /* anon_vma_prepare */ #include /* set_pte_at_notify */ #include /* try_to_free_swap */ + #include static struct rb_root uprobes_tree = RB_ROOT; + static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ #define UPROBES_HASH_SZ 13 + /* serialize (un)register */ static struct mutex uprobes_mutex[UPROBES_HASH_SZ]; -#define uprobes_hash(v) (&uprobes_mutex[((unsigned long)(v)) %\ - UPROBES_HASH_SZ]) + +#define uprobes_hash(v) (&uprobes_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) /* serialize uprobe->pending_list */ static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ]; -#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) %\ - UPROBES_HASH_SZ]) +#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) /* - * uprobe_events allows us to skip the mmap_uprobe if there are no uprobe + * uprobe_events allows us to skip the uprobe_mmap if there are no uprobe * events active at this time. Probably a fine grained per inode count is * better? */ @@ -58,9 +60,9 @@ static atomic_t uprobe_events = ATOMIC_INIT(0); * vm_area_struct wasnt recommended. */ struct vma_info { - struct list_head probe_list; - struct mm_struct *mm; - loff_t vaddr; + struct list_head probe_list; + struct mm_struct *mm; + loff_t vaddr; }; /* @@ -79,8 +81,7 @@ static bool valid_vma(struct vm_area_struct *vma, bool is_register) if (!is_register) return true; - if ((vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) == - (VM_READ|VM_EXEC)) + if ((vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) == (VM_READ|VM_EXEC)) return true; return false; @@ -92,6 +93,7 @@ static loff_t vma_address(struct vm_area_struct *vma, loff_t offset) vaddr = vma->vm_start + offset; vaddr -= vma->vm_pgoff << PAGE_SHIFT; + return vaddr; } @@ -105,8 +107,7 @@ static loff_t vma_address(struct vm_area_struct *vma, loff_t offset) * * Returns 0 on success, -EFAULT on failure. */ -static int __replace_page(struct vm_area_struct *vma, struct page *page, - struct page *kpage) +static int __replace_page(struct vm_area_struct *vma, struct page *page, struct page *kpage) { struct mm_struct *mm = vma->vm_mm; pgd_t *pgd; @@ -163,7 +164,7 @@ out: */ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) { - return (*insn == UPROBES_BKPT_INSN); + return *insn == UPROBES_BKPT_INSN; } /* @@ -203,6 +204,7 @@ static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma); if (ret <= 0) return ret; + ret = -EINVAL; /* @@ -239,6 +241,7 @@ static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, vaddr_new = kmap_atomic(new_page); memcpy(vaddr_new, vaddr_old, PAGE_SIZE); + /* poke the new insn in, ASSUMES we don't cross page boundary */ vaddr &= ~PAGE_MASK; BUG_ON(vaddr + uprobe_opcode_sz > PAGE_SIZE); @@ -260,7 +263,8 @@ unlock_out: page_cache_release(new_page); put_out: - put_page(old_page); /* we did a get_page in the beginning */ + put_page(old_page); + return ret; } @@ -276,8 +280,7 @@ put_out: * For mm @mm, read the opcode at @vaddr and store it in @opcode. * Return 0 (success) or a negative errno. */ -static int read_opcode(struct mm_struct *mm, unsigned long vaddr, - uprobe_opcode_t *opcode) +static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t *opcode) { struct page *page; void *vaddr_new; @@ -293,15 +296,18 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, memcpy(opcode, vaddr_new + vaddr, uprobe_opcode_sz); kunmap_atomic(vaddr_new); unlock_page(page); - put_page(page); /* we did a get_user_pages in the beginning */ + + put_page(page); + return 0; } static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) { uprobe_opcode_t opcode; - int result = read_opcode(mm, vaddr, &opcode); + int result; + result = read_opcode(mm, vaddr, &opcode); if (result) return result; @@ -320,11 +326,11 @@ static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) * For mm @mm, store the breakpoint instruction at @vaddr. * Return 0 (success) or a negative errno. */ -int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, - unsigned long vaddr) +int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr) { - int result = is_bkpt_at_addr(mm, vaddr); + int result; + result = is_bkpt_at_addr(mm, vaddr); if (result == 1) return -EEXIST; @@ -344,35 +350,35 @@ int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, * For mm @mm, restore the original opcode (opcode) at @vaddr. * Return 0 (success) or a negative errno. */ -int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, - unsigned long vaddr, bool verify) +int __weak +set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, bool verify) { if (verify) { - int result = is_bkpt_at_addr(mm, vaddr); + int result; + result = is_bkpt_at_addr(mm, vaddr); if (!result) return -EINVAL; if (result != 1) return result; } - return write_opcode(mm, uprobe, vaddr, - *(uprobe_opcode_t *)uprobe->insn); + return write_opcode(mm, uprobe, vaddr, *(uprobe_opcode_t *)uprobe->insn); } static int match_uprobe(struct uprobe *l, struct uprobe *r) { if (l->inode < r->inode) return -1; + if (l->inode > r->inode) return 1; - else { - if (l->offset < r->offset) - return -1; - if (l->offset > r->offset) - return 1; - } + if (l->offset < r->offset) + return -1; + + if (l->offset > r->offset) + return 1; return 0; } @@ -391,6 +397,7 @@ static struct uprobe *__find_uprobe(struct inode *inode, loff_t offset) atomic_inc(&uprobe->ref); return uprobe; } + if (match < 0) n = n->rb_left; else @@ -411,6 +418,7 @@ static struct uprobe *find_uprobe(struct inode *inode, loff_t offset) spin_lock_irqsave(&uprobes_treelock, flags); uprobe = __find_uprobe(inode, offset); spin_unlock_irqrestore(&uprobes_treelock, flags); + return uprobe; } @@ -436,16 +444,18 @@ static struct uprobe *__insert_uprobe(struct uprobe *uprobe) p = &parent->rb_right; } + u = NULL; rb_link_node(&uprobe->rb_node, parent, p); rb_insert_color(&uprobe->rb_node, &uprobes_tree); /* get access + creation ref */ atomic_set(&uprobe->ref, 2); + return u; } /* - * Acquires uprobes_treelock. + * Acquire uprobes_treelock. * Matching uprobe already exists in rbtree; * increment (access refcount) and return the matching uprobe. * @@ -460,6 +470,7 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe) spin_lock_irqsave(&uprobes_treelock, flags); u = __insert_uprobe(uprobe); spin_unlock_irqrestore(&uprobes_treelock, flags); + return u; } @@ -490,19 +501,22 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) kfree(uprobe); uprobe = cur_uprobe; iput(inode); - } else + } else { atomic_inc(&uprobe_events); + } + return uprobe; } /* Returns the previous consumer */ -static struct uprobe_consumer *add_consumer(struct uprobe *uprobe, - struct uprobe_consumer *consumer) +static struct uprobe_consumer * +consumer_add(struct uprobe *uprobe, struct uprobe_consumer *consumer) { down_write(&uprobe->consumer_rwsem); consumer->next = uprobe->consumers; uprobe->consumers = consumer; up_write(&uprobe->consumer_rwsem); + return consumer->next; } @@ -511,8 +525,7 @@ static struct uprobe_consumer *add_consumer(struct uprobe *uprobe, * Return true if the @consumer is deleted successfully * or return false. */ -static bool del_consumer(struct uprobe *uprobe, - struct uprobe_consumer *consumer) +static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *consumer) { struct uprobe_consumer **con; bool ret = false; @@ -526,6 +539,7 @@ static bool del_consumer(struct uprobe *uprobe, } } up_write(&uprobe->consumer_rwsem); + return ret; } @@ -557,15 +571,15 @@ static int __copy_insn(struct address_space *mapping, memcpy(insn, vaddr + off1, nbytes); kunmap_atomic(vaddr); page_cache_release(page); + return 0; } -static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, - unsigned long addr) +static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr) { struct address_space *mapping; - int bytes; unsigned long nbytes; + int bytes; addr &= ~PAGE_MASK; nbytes = PAGE_SIZE - addr; @@ -605,6 +619,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, return -EEXIST; addr = (unsigned long)vaddr; + if (!(uprobe->flags & UPROBES_COPY_INSN)) { ret = copy_insn(uprobe, vma, addr); if (ret) @@ -613,7 +628,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, if (is_bkpt_insn((uprobe_opcode_t *)uprobe->insn)) return -EEXIST; - ret = analyze_insn(mm, uprobe); + ret = arch_uprobes_analyze_insn(mm, uprobe); if (ret) return ret; @@ -624,8 +639,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, return ret; } -static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, - loff_t vaddr) +static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, loff_t vaddr) { set_orig_insn(mm, uprobe, (unsigned long)vaddr, true); } @@ -649,9 +663,11 @@ static struct vma_info *__find_next_vma_info(struct list_head *head, struct prio_tree_iter iter; struct vm_area_struct *vma; struct vma_info *tmpvi; - loff_t vaddr; - unsigned long pgoff = offset >> PAGE_SHIFT; + unsigned long pgoff; int existing_vma; + loff_t vaddr; + + pgoff = offset >> PAGE_SHIFT; vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { if (!valid_vma(vma, is_register)) @@ -659,6 +675,7 @@ static struct vma_info *__find_next_vma_info(struct list_head *head, existing_vma = 0; vaddr = vma_address(vma, offset); + list_for_each_entry(tmpvi, head, probe_list) { if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) { existing_vma = 1; @@ -670,14 +687,15 @@ static struct vma_info *__find_next_vma_info(struct list_head *head, * Another vma needs a probe to be installed. However skip * installing the probe if the vma is about to be unlinked. */ - if (!existing_vma && - atomic_inc_not_zero(&vma->vm_mm->mm_users)) { + if (!existing_vma && atomic_inc_not_zero(&vma->vm_mm->mm_users)) { vi->mm = vma->vm_mm; vi->vaddr = vaddr; list_add(&vi->probe_list, head); + return vi; } } + return NULL; } @@ -685,11 +703,12 @@ static struct vma_info *__find_next_vma_info(struct list_head *head, * Iterate in the rmap prio tree and find a vma where a probe has not * yet been inserted. */ -static struct vma_info *find_next_vma_info(struct list_head *head, - loff_t offset, struct address_space *mapping, - bool is_register) +static struct vma_info * +find_next_vma_info(struct list_head *head, loff_t offset, struct address_space *mapping, + bool is_register) { struct vma_info *vi, *retvi; + vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL); if (!vi) return ERR_PTR(-ENOMEM); @@ -700,6 +719,7 @@ static struct vma_info *find_next_vma_info(struct list_head *head, if (!retvi) kfree(vi); + return retvi; } @@ -711,16 +731,23 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) struct vma_info *vi, *tmpvi; struct mm_struct *mm; loff_t vaddr; - int ret = 0; + int ret; mapping = uprobe->inode->i_mapping; INIT_LIST_HEAD(&try_list); - while ((vi = find_next_vma_info(&try_list, uprobe->offset, - mapping, is_register)) != NULL) { + + ret = 0; + + for (;;) { + vi = find_next_vma_info(&try_list, uprobe->offset, mapping, is_register); + if (!vi) + break; + if (IS_ERR(vi)) { ret = PTR_ERR(vi); break; } + mm = vi->mm; down_read(&mm->mmap_sem); vma = find_vma(mm, (unsigned long)vi->vaddr); @@ -755,19 +782,21 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) break; } } + list_for_each_entry_safe(vi, tmpvi, &try_list, probe_list) { list_del(&vi->probe_list); kfree(vi); } + return ret; } -static int __register_uprobe(struct uprobe *uprobe) +static int __uprobe_register(struct uprobe *uprobe) { return register_for_each_vma(uprobe, true); } -static void __unregister_uprobe(struct uprobe *uprobe) +static void __uprobe_unregister(struct uprobe *uprobe) { if (!register_for_each_vma(uprobe, false)) delete_uprobe(uprobe); @@ -776,15 +805,15 @@ static void __unregister_uprobe(struct uprobe *uprobe) } /* - * register_uprobe - register a probe + * uprobe_register - register a probe * @inode: the file in which the probe has to be placed. * @offset: offset from the start of the file. * @consumer: information on howto handle the probe.. * - * Apart from the access refcount, register_uprobe() takes a creation + * Apart from the access refcount, uprobe_register() takes a creation * refcount (thro alloc_uprobe) if and only if this @uprobe is getting * inserted into the rbtree (i.e first consumer for a @inode:@offset - * tuple). Creation refcount stops unregister_uprobe from freeing the + * tuple). Creation refcount stops uprobe_unregister from freeing the * @uprobe even before the register operation is complete. Creation * refcount is released when the last @consumer for the @uprobe * unregisters. @@ -792,28 +821,29 @@ static void __unregister_uprobe(struct uprobe *uprobe) * Return errno if it cannot successully install probes * else return 0 (success) */ -int register_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer) +int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) { struct uprobe *uprobe; - int ret = -EINVAL; + int ret; if (!inode || !consumer || consumer->next) - return ret; + return -EINVAL; if (offset > i_size_read(inode)) - return ret; + return -EINVAL; ret = 0; mutex_lock(uprobes_hash(inode)); uprobe = alloc_uprobe(inode, offset); - if (uprobe && !add_consumer(uprobe, consumer)) { - ret = __register_uprobe(uprobe); + + if (uprobe && !consumer_add(uprobe, consumer)) { + ret = __uprobe_register(uprobe); if (ret) { uprobe->consumers = NULL; - __unregister_uprobe(uprobe); - } else + __uprobe_unregister(uprobe); + } else { uprobe->flags |= UPROBES_RUN_HANDLER; + } } mutex_unlock(uprobes_hash(inode)); @@ -823,15 +853,14 @@ int register_uprobe(struct inode *inode, loff_t offset, } /* - * unregister_uprobe - unregister a already registered probe. + * uprobe_unregister - unregister a already registered probe. * @inode: the file in which the probe has to be removed. * @offset: offset from the start of the file. * @consumer: identify which probe if multiple probes are colocated. */ -void unregister_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer) +void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) { - struct uprobe *uprobe = NULL; + struct uprobe *uprobe; if (!inode || !consumer) return; @@ -841,15 +870,14 @@ void unregister_uprobe(struct inode *inode, loff_t offset, return; mutex_lock(uprobes_hash(inode)); - if (!del_consumer(uprobe, consumer)) - goto unreg_out; - if (!uprobe->consumers) { - __unregister_uprobe(uprobe); - uprobe->flags &= ~UPROBES_RUN_HANDLER; + if (consumer_del(uprobe, consumer)) { + if (!uprobe->consumers) { + __uprobe_unregister(uprobe); + uprobe->flags &= ~UPROBES_RUN_HANDLER; + } } -unreg_out: mutex_unlock(uprobes_hash(inode)); if (uprobe) put_uprobe(uprobe); @@ -870,6 +898,7 @@ static struct rb_node *find_least_offset_node(struct inode *inode) while (n) { uprobe = rb_entry(n, struct uprobe, rb_node); match = match_uprobe(&u, uprobe); + if (uprobe->inode == inode) close_node = n; @@ -881,6 +910,7 @@ static struct rb_node *find_least_offset_node(struct inode *inode) else n = n->rb_right; } + return close_node; } @@ -890,11 +920,13 @@ static struct rb_node *find_least_offset_node(struct inode *inode) static void build_probe_list(struct inode *inode, struct list_head *head) { struct uprobe *uprobe; - struct rb_node *n; unsigned long flags; + struct rb_node *n; spin_lock_irqsave(&uprobes_treelock, flags); + n = find_least_offset_node(inode); + for (; n; n = rb_next(n)) { uprobe = rb_entry(n, struct uprobe, rb_node); if (uprobe->inode != inode) @@ -903,6 +935,7 @@ static void build_probe_list(struct inode *inode, struct list_head *head) list_add(&uprobe->pending_list, head); atomic_inc(&uprobe->ref); } + spin_unlock_irqrestore(&uprobes_treelock, flags); } @@ -912,42 +945,44 @@ static void build_probe_list(struct inode *inode, struct list_head *head) * * Return -ve no if we fail to insert probes and we cannot * bail-out. - * Return 0 otherwise. i.e : + * Return 0 otherwise. i.e: + * * - successful insertion of probes * - (or) no possible probes to be inserted. * - (or) insertion of probes failed but we can bail-out. */ -int mmap_uprobe(struct vm_area_struct *vma) +int uprobe_mmap(struct vm_area_struct *vma) { struct list_head tmp_list; struct uprobe *uprobe, *u; struct inode *inode; - int ret = 0; + int ret; if (!atomic_read(&uprobe_events) || !valid_vma(vma, true)) - return ret; /* Bail-out */ + return 0; inode = vma->vm_file->f_mapping->host; if (!inode) - return ret; + return 0; INIT_LIST_HEAD(&tmp_list); mutex_lock(uprobes_mmap_hash(inode)); build_probe_list(inode, &tmp_list); + + ret = 0; + list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { loff_t vaddr; list_del(&uprobe->pending_list); if (!ret) { vaddr = vma_address(vma, uprobe->offset); - if (vaddr < vma->vm_start || vaddr >= vma->vm_end) { - put_uprobe(uprobe); - continue; + if (vaddr >= vma->vm_start && vaddr < vma->vm_end) { + ret = install_breakpoint(vma->vm_mm, uprobe, vma, vaddr); + /* Ignore double add: */ + if (ret == -EEXIST) + ret = 0; } - ret = install_breakpoint(vma->vm_mm, uprobe, vma, - vaddr); - if (ret == -EEXIST) - ret = 0; } put_uprobe(uprobe); } diff --git a/mm/mmap.c b/mm/mmap.c index 1aed183636d7..5a863d328a44 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -618,10 +618,10 @@ again: remove_next = 1 + (end > next->vm_end); mutex_unlock(&mapping->i_mmap_mutex); if (root) { - mmap_uprobe(vma); + uprobe_mmap(vma); if (adjust_next) - mmap_uprobe(next); + uprobe_mmap(next); } if (remove_next) { @@ -646,7 +646,7 @@ again: remove_next = 1 + (end > next->vm_end); } } if (insert && file) - mmap_uprobe(insert); + uprobe_mmap(insert); validate_mm(mm); @@ -1340,7 +1340,7 @@ out: } else if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK)) make_pages_present(addr, addr + len); - if (file && mmap_uprobe(vma)) + if (file && uprobe_mmap(vma)) /* matching probes but cannot insert */ goto unmap_and_free_vma; @@ -2301,7 +2301,7 @@ int insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma) security_vm_enough_memory_mm(mm, vma_pages(vma))) return -ENOMEM; - if (vma->vm_file && mmap_uprobe(vma)) + if (vma->vm_file && uprobe_mmap(vma)) return -EINVAL; vma_link(mm, vma, prev, rb_link, rb_parent); @@ -2374,7 +2374,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, if (new_vma->vm_file) { get_file(new_vma->vm_file); - if (mmap_uprobe(new_vma)) + if (uprobe_mmap(new_vma)) goto out_free_mempol; if (vma->vm_flags & VM_EXECUTABLE) -- cgit v1.2.3 From 96379f60075c75b261328aa7830ef8aa158247ac Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Wed, 22 Feb 2012 14:45:49 +0530 Subject: uprobes/core: Remove uprobe_opcode_sz uprobe_opcode_sz refers to the smallest instruction size for that architecture. UPROBES_BKPT_INSN_SIZE refers to the size of the breakpoint instruction for that architecture. For now we are assuming that both uprobe_opcode_sz and UPROBES_BKPT_INSN_SIZE are the same for all archs and hence removing uprobe_opcode_sz in favour of UPROBES_BKPT_INSN_SIZE. However if we have to support architectures where the smallest instruction size is different from the size of breakpoint instruction, we may have to re-introduce uprobe_opcode_sz. Signed-off-by: Srikar Dronamraju Cc: Peter Zijlstra Cc: Linus Torvalds Cc: Oleg Nesterov Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Masami Hiramatsu Cc: Anton Arapov Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Jiri Olsa Cc: Josh Stone Link: http://lkml.kernel.org/r/20120222091549.15880.67020.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- include/linux/uprobes.h | 2 -- kernel/events/uprobes.c | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 64e45f116b2a..fd45b70750d4 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -37,8 +37,6 @@ struct uprobe_arch_info {}; #define MAX_UINSN_BYTES 4 #endif -#define uprobe_opcode_sz sizeof(uprobe_opcode_t) - /* flags that denote/change uprobes behaviour */ /* Have a copy of original instruction */ diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 884817f1b0d3..ee496ad95db3 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -244,8 +244,8 @@ static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, /* poke the new insn in, ASSUMES we don't cross page boundary */ vaddr &= ~PAGE_MASK; - BUG_ON(vaddr + uprobe_opcode_sz > PAGE_SIZE); - memcpy(vaddr_new + vaddr, &opcode, uprobe_opcode_sz); + BUG_ON(vaddr + UPROBES_BKPT_INSN_SIZE > PAGE_SIZE); + memcpy(vaddr_new + vaddr, &opcode, UPROBES_BKPT_INSN_SIZE); kunmap_atomic(vaddr_new); kunmap_atomic(vaddr_old); @@ -293,7 +293,7 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_ lock_page(page); vaddr_new = kmap_atomic(page); vaddr &= ~PAGE_MASK; - memcpy(opcode, vaddr_new + vaddr, uprobe_opcode_sz); + memcpy(opcode, vaddr_new + vaddr, UPROBES_BKPT_INSN_SIZE); kunmap_atomic(vaddr_new); unlock_page(page); -- cgit v1.2.3 From 3ff54efdfaace9e9b2b7c1959a865be6b91de96c Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Wed, 22 Feb 2012 14:46:02 +0530 Subject: uprobes/core: Move insn to arch specific structure Few cleanups suggested by Ingo Molnar. - Rename struct uprobe_arch_info to struct arch_uprobe. - Move insn from struct uprobe to struct arch_uprobe. - Make arch specific uprobe functions to accept struct arch_uprobe instead of struct uprobe. - Move struct uprobe to kernel/uprobes.c from include/linux/uprobes.h Signed-off-by: Srikar Dronamraju Cc: Peter Zijlstra Cc: Linus Torvalds Cc: Oleg Nesterov Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Masami Hiramatsu Cc: Anton Arapov Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Jiri Olsa Cc: Josh Stone Link: http://lkml.kernel.org/r/20120222091602.15880.40249.sendpatchset@srdronam.in.ibm.com [ Made various small improvements ] Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uprobes.h | 6 ++--- arch/x86/kernel/uprobes.c | 60 +++++++++++++++++++++--------------------- include/linux/uprobes.h | 23 ++-------------- kernel/events/uprobes.c | 38 +++++++++++++++++--------- 4 files changed, 61 insertions(+), 66 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 072df3902636..f7ce310a429d 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -31,13 +31,13 @@ typedef u8 uprobe_opcode_t; #define UPROBES_BKPT_INSN 0xcc #define UPROBES_BKPT_INSN_SIZE 1 -struct uprobe_arch_info { +struct arch_uprobe { u16 fixups; + u8 insn[MAX_UINSN_BYTES]; #ifdef CONFIG_X86_64 unsigned long rip_rela_target_address; #endif }; -struct uprobe; -extern int arch_uprobes_analyze_insn(struct mm_struct *mm, struct uprobe *uprobe); +extern int arch_uprobes_analyze_insn(struct mm_struct *mm, struct arch_uprobe *arch_uprobe); #endif /* _ASM_UPROBES_H */ diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 13d616d6519b..04dfcef2d028 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -200,9 +200,9 @@ static bool is_prefix_bad(struct insn *insn) return false; } -static int validate_insn_32bits(struct uprobe *uprobe, struct insn *insn) +static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn) { - insn_init(insn, uprobe->insn, false); + insn_init(insn, auprobe->insn, false); /* Skip good instruction prefixes; reject "bad" ones. */ insn_get_opcode(insn); @@ -222,11 +222,11 @@ static int validate_insn_32bits(struct uprobe *uprobe, struct insn *insn) /* * Figure out which fixups post_xol() will need to perform, and annotate - * uprobe->arch_info.fixups accordingly. To start with, - * uprobe->arch_info.fixups is either zero or it reflects rip-related + * arch_uprobe->fixups accordingly. To start with, + * arch_uprobe->fixups is either zero or it reflects rip-related * fixups. */ -static void prepare_fixups(struct uprobe *uprobe, struct insn *insn) +static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) { bool fix_ip = true, fix_call = false; /* defaults */ int reg; @@ -269,17 +269,17 @@ static void prepare_fixups(struct uprobe *uprobe, struct insn *insn) break; } if (fix_ip) - uprobe->arch_info.fixups |= UPROBES_FIX_IP; + auprobe->fixups |= UPROBES_FIX_IP; if (fix_call) - uprobe->arch_info.fixups |= UPROBES_FIX_CALL; + auprobe->fixups |= UPROBES_FIX_CALL; } #ifdef CONFIG_X86_64 /* - * If uprobe->insn doesn't use rip-relative addressing, return + * If arch_uprobe->insn doesn't use rip-relative addressing, return * immediately. Otherwise, rewrite the instruction so that it accesses * its memory operand indirectly through a scratch register. Set - * uprobe->arch_info.fixups and uprobe->arch_info.rip_rela_target_address + * arch_uprobe->fixups and arch_uprobe->rip_rela_target_address * accordingly. (The contents of the scratch register will be saved * before we single-step the modified instruction, and restored * afterward.) @@ -297,7 +297,7 @@ static void prepare_fixups(struct uprobe *uprobe, struct insn *insn) * - There's never a SIB byte. * - The displacement is always 4 bytes. */ -static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) +static void handle_riprel_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) { u8 *cursor; u8 reg; @@ -305,7 +305,7 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, stru if (mm->context.ia32_compat) return; - uprobe->arch_info.rip_rela_target_address = 0x0; + auprobe->rip_rela_target_address = 0x0; if (!insn_rip_relative(insn)) return; @@ -315,7 +315,7 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, stru * we want to encode rax/rcx, not r8/r9. */ if (insn->rex_prefix.nbytes) { - cursor = uprobe->insn + insn_offset_rex_prefix(insn); + cursor = auprobe->insn + insn_offset_rex_prefix(insn); *cursor &= 0xfe; /* Clearing REX.B bit */ } @@ -324,7 +324,7 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, stru * displacement. Beyond the displacement, for some instructions, * is the immediate operand. */ - cursor = uprobe->insn + insn_offset_modrm(insn); + cursor = auprobe->insn + insn_offset_modrm(insn); insn_get_length(insn); /* @@ -341,18 +341,18 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, stru * is NOT the register operand, so we use %rcx (register * #1) for the scratch register. */ - uprobe->arch_info.fixups = UPROBES_FIX_RIP_CX; + auprobe->fixups = UPROBES_FIX_RIP_CX; /* Change modrm from 00 000 101 to 00 000 001. */ *cursor = 0x1; } else { /* Use %rax (register #0) for the scratch register. */ - uprobe->arch_info.fixups = UPROBES_FIX_RIP_AX; + auprobe->fixups = UPROBES_FIX_RIP_AX; /* Change modrm from 00 xxx 101 to 00 xxx 000 */ *cursor = (reg << 3); } /* Target address = address of next instruction + (signed) offset */ - uprobe->arch_info.rip_rela_target_address = (long)insn->length + insn->displacement.value; + auprobe->rip_rela_target_address = (long)insn->length + insn->displacement.value; /* Displacement field is gone; slide immediate field (if any) over. */ if (insn->immediate.nbytes) { @@ -362,9 +362,9 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, stru return; } -static int validate_insn_64bits(struct uprobe *uprobe, struct insn *insn) +static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn) { - insn_init(insn, uprobe->insn, true); + insn_init(insn, auprobe->insn, true); /* Skip good instruction prefixes; reject "bad" ones. */ insn_get_opcode(insn); @@ -381,42 +381,42 @@ static int validate_insn_64bits(struct uprobe *uprobe, struct insn *insn) return -ENOTSUPP; } -static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) +static int validate_insn_bits(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) { if (mm->context.ia32_compat) - return validate_insn_32bits(uprobe, insn); - return validate_insn_64bits(uprobe, insn); + return validate_insn_32bits(auprobe, insn); + return validate_insn_64bits(auprobe, insn); } #else /* 32-bit: */ -static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) +static void handle_riprel_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) { /* No RIP-relative addressing on 32-bit */ } -static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) +static int validate_insn_bits(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) { - return validate_insn_32bits(uprobe, insn); + return validate_insn_32bits(auprobe, insn); } #endif /* CONFIG_X86_64 */ /** * arch_uprobes_analyze_insn - instruction analysis including validity and fixups. * @mm: the probed address space. - * @uprobe: the probepoint information. + * @arch_uprobe: the probepoint information. * Return 0 on success or a -ve number on error. */ -int arch_uprobes_analyze_insn(struct mm_struct *mm, struct uprobe *uprobe) +int arch_uprobes_analyze_insn(struct mm_struct *mm, struct arch_uprobe *auprobe) { int ret; struct insn insn; - uprobe->arch_info.fixups = 0; - ret = validate_insn_bits(mm, uprobe, &insn); + auprobe->fixups = 0; + ret = validate_insn_bits(mm, auprobe, &insn); if (ret != 0) return ret; - handle_riprel_insn(mm, uprobe, &insn); - prepare_fixups(uprobe, &insn); + handle_riprel_insn(mm, auprobe, &insn); + prepare_fixups(auprobe, &insn); return 0; } diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index fd45b70750d4..9c6be62787ed 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -29,12 +29,6 @@ struct vm_area_struct; #ifdef CONFIG_ARCH_SUPPORTS_UPROBES #include -#else - -typedef u8 uprobe_opcode_t; -struct uprobe_arch_info {}; - -#define MAX_UINSN_BYTES 4 #endif /* flags that denote/change uprobes behaviour */ @@ -56,22 +50,9 @@ struct uprobe_consumer { struct uprobe_consumer *next; }; -struct uprobe { - struct rb_node rb_node; /* node in the rb tree */ - atomic_t ref; - struct rw_semaphore consumer_rwsem; - struct list_head pending_list; - struct uprobe_arch_info arch_info; - struct uprobe_consumer *consumers; - struct inode *inode; /* Also hold a ref to inode */ - loff_t offset; - int flags; - u8 insn[MAX_UINSN_BYTES]; -}; - #ifdef CONFIG_UPROBES -extern int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr); -extern int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, bool verify); +extern int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr); +extern int __weak set_orig_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr, bool verify); extern bool __weak is_bkpt_insn(uprobe_opcode_t *insn); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index ee496ad95db3..13f1b5909af4 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -65,6 +65,18 @@ struct vma_info { loff_t vaddr; }; +struct uprobe { + struct rb_node rb_node; /* node in the rb tree */ + atomic_t ref; + struct rw_semaphore consumer_rwsem; + struct list_head pending_list; + struct uprobe_consumer *consumers; + struct inode *inode; /* Also hold a ref to inode */ + loff_t offset; + int flags; + struct arch_uprobe arch; +}; + /* * valid_vma: Verify if the specified vma is an executable vma * Relax restrictions while unregistering: vm_flags might have @@ -180,7 +192,7 @@ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) /* * write_opcode - write the opcode at a given virtual address. * @mm: the probed process address space. - * @uprobe: the breakpointing information. + * @arch_uprobe: the breakpointing information. * @vaddr: the virtual address to store the opcode. * @opcode: opcode to be written at @vaddr. * @@ -190,13 +202,14 @@ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) * For mm @mm, write the opcode at @vaddr. * Return 0 (success) or a negative errno. */ -static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, +static int write_opcode(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr, uprobe_opcode_t opcode) { struct page *old_page, *new_page; struct address_space *mapping; void *vaddr_old, *vaddr_new; struct vm_area_struct *vma; + struct uprobe *uprobe; loff_t addr; int ret; @@ -216,6 +229,7 @@ static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, if (!valid_vma(vma, is_bkpt_insn(&opcode))) goto put_out; + uprobe = container_of(auprobe, struct uprobe, arch); mapping = uprobe->inode->i_mapping; if (mapping != vma->vm_file->f_mapping) goto put_out; @@ -326,7 +340,7 @@ static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) * For mm @mm, store the breakpoint instruction at @vaddr. * Return 0 (success) or a negative errno. */ -int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr) +int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr) { int result; @@ -337,7 +351,7 @@ int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long v if (result) return result; - return write_opcode(mm, uprobe, vaddr, UPROBES_BKPT_INSN); + return write_opcode(mm, auprobe, vaddr, UPROBES_BKPT_INSN); } /** @@ -351,7 +365,7 @@ int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long v * Return 0 (success) or a negative errno. */ int __weak -set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, bool verify) +set_orig_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr, bool verify) { if (verify) { int result; @@ -363,7 +377,7 @@ set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, if (result != 1) return result; } - return write_opcode(mm, uprobe, vaddr, *(uprobe_opcode_t *)uprobe->insn); + return write_opcode(mm, auprobe, vaddr, *(uprobe_opcode_t *)auprobe->insn); } static int match_uprobe(struct uprobe *l, struct uprobe *r) @@ -593,13 +607,13 @@ static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned /* Instruction at the page-boundary; copy bytes in second page */ if (nbytes < bytes) { - if (__copy_insn(mapping, vma, uprobe->insn + nbytes, + if (__copy_insn(mapping, vma, uprobe->arch.insn + nbytes, bytes - nbytes, uprobe->offset + nbytes)) return -ENOMEM; bytes = nbytes; } - return __copy_insn(mapping, vma, uprobe->insn, bytes, uprobe->offset); + return __copy_insn(mapping, vma, uprobe->arch.insn, bytes, uprobe->offset); } static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, @@ -625,23 +639,23 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, if (ret) return ret; - if (is_bkpt_insn((uprobe_opcode_t *)uprobe->insn)) + if (is_bkpt_insn((uprobe_opcode_t *)uprobe->arch.insn)) return -EEXIST; - ret = arch_uprobes_analyze_insn(mm, uprobe); + ret = arch_uprobes_analyze_insn(mm, &uprobe->arch); if (ret) return ret; uprobe->flags |= UPROBES_COPY_INSN; } - ret = set_bkpt(mm, uprobe, addr); + ret = set_bkpt(mm, &uprobe->arch, addr); return ret; } static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, loff_t vaddr) { - set_orig_insn(mm, uprobe, (unsigned long)vaddr, true); + set_orig_insn(mm, &uprobe->arch, (unsigned long)vaddr, true); } static void delete_uprobe(struct uprobe *uprobe) -- cgit v1.2.3 From 35aa621b5ab9d08767f7bc8d209b696df281d715 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 22 Feb 2012 11:37:29 +0100 Subject: uprobes: Update copyright notices Add Peter Zijlstra's copyright to the uprobes code, whose contributions to the uprobes code are not visible in the Git history, because they were backmerged. Also update existing copyright notices to the year 2012. Acked-by: Srikar Dronamraju Cc: Peter Zijlstra Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Link: http://lkml.kernel.org/n/tip-vjqxst502pc1efz7ah8cyht4@git.kernel.org Signed-off-by: Ingo Molnar --- include/linux/uprobes.h | 3 ++- kernel/events/uprobes.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 9c6be62787ed..f85797e1ccd4 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -17,10 +17,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Copyright (C) IBM Corporation, 2008-2011 + * Copyright (C) IBM Corporation, 2008-2012 * Authors: * Srikar Dronamraju * Jim Keniston + * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra */ #include diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 13f1b5909af4..5ce32e3ae9e9 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -15,10 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Copyright (C) IBM Corporation, 2008-2011 + * Copyright (C) IBM Corporation, 2008-2012 * Authors: * Srikar Dronamraju * Jim Keniston + * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra */ #include -- cgit v1.2.3 From b2fab5acd28ead6f0dd6c3996ba23f0ef1772f15 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 5 Mar 2012 13:14:57 -0800 Subject: elevator: make elevator_init_fn() return 0/-errno elevator_ops->elevator_init_fn() has a weird return value. It returns a void * which the caller should assign to q->elevator->elevator_data and %NULL return denotes init failure. Update such that it returns integer 0/-errno and sets elevator_data directly as necessary. This makes the interface more conventional and eases further cleanup. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/cfq-iosched.c | 9 +++++---- block/deadline-iosched.c | 8 +++++--- block/elevator.c | 12 ++---------- block/noop-iosched.c | 8 +++++--- include/linux/elevator.h | 2 +- 5 files changed, 18 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 388fe01de18e..72680a6715fc 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3656,7 +3656,7 @@ static void cfq_exit_queue(struct elevator_queue *e) kfree(cfqd); } -static void *cfq_init_queue(struct request_queue *q) +static int cfq_init_queue(struct request_queue *q) { struct cfq_data *cfqd; int i, j; @@ -3665,7 +3665,7 @@ static void *cfq_init_queue(struct request_queue *q) cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node); if (!cfqd) - return NULL; + return -ENOMEM; /* Init root service tree */ cfqd->grp_service_tree = CFQ_RB_ROOT; @@ -3692,7 +3692,7 @@ static void *cfq_init_queue(struct request_queue *q) if (blkio_alloc_blkg_stats(&cfqg->blkg)) { kfree(cfqg); kfree(cfqd); - return NULL; + return -ENOMEM; } rcu_read_lock(); @@ -3723,6 +3723,7 @@ static void *cfq_init_queue(struct request_queue *q) cfq_link_cfqq_cfqg(&cfqd->oom_cfqq, &cfqd->root_group); cfqd->queue = q; + q->elevator->elevator_data = cfqd; init_timer(&cfqd->idle_slice_timer); cfqd->idle_slice_timer.function = cfq_idle_slice_timer; @@ -3747,7 +3748,7 @@ static void *cfq_init_queue(struct request_queue *q) * second, in order to have larger depth for async operations. */ cfqd->last_delayed_sync = jiffies - HZ; - return cfqd; + return 0; } /* diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c index 7bf12d793fcd..599b12e5380f 100644 --- a/block/deadline-iosched.c +++ b/block/deadline-iosched.c @@ -337,13 +337,13 @@ static void deadline_exit_queue(struct elevator_queue *e) /* * initialize elevator private data (deadline_data). */ -static void *deadline_init_queue(struct request_queue *q) +static int deadline_init_queue(struct request_queue *q) { struct deadline_data *dd; dd = kmalloc_node(sizeof(*dd), GFP_KERNEL | __GFP_ZERO, q->node); if (!dd) - return NULL; + return -ENOMEM; INIT_LIST_HEAD(&dd->fifo_list[READ]); INIT_LIST_HEAD(&dd->fifo_list[WRITE]); @@ -354,7 +354,9 @@ static void *deadline_init_queue(struct request_queue *q) dd->writes_starved = writes_starved; dd->front_merges = 1; dd->fifo_batch = fifo_batch; - return dd; + + q->elevator->elevator_data = dd; + return 0; } /* diff --git a/block/elevator.c b/block/elevator.c index f8c08e1bff2b..f81c061dad15 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -121,14 +121,6 @@ static struct elevator_type *elevator_get(const char *name) return e; } -static int elevator_init_queue(struct request_queue *q) -{ - q->elevator->elevator_data = q->elevator->type->ops.elevator_init_fn(q); - if (q->elevator->elevator_data) - return 0; - return -ENOMEM; -} - static char chosen_elevator[ELV_NAME_MAX]; static int __init elevator_setup(char *str) @@ -224,7 +216,7 @@ int elevator_init(struct request_queue *q, char *name) if (!q->elevator) return -ENOMEM; - err = elevator_init_queue(q); + err = e->ops.elevator_init_fn(q); if (err) { kobject_put(&q->elevator->kobj); return err; @@ -927,7 +919,7 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) if (!q->elevator) goto fail_init; - err = elevator_init_queue(q); + err = new_e->ops.elevator_init_fn(q); if (err) { kobject_put(&q->elevator->kobj); goto fail_init; diff --git a/block/noop-iosched.c b/block/noop-iosched.c index 413a0b1d788c..5d1bf70e33d5 100644 --- a/block/noop-iosched.c +++ b/block/noop-iosched.c @@ -59,15 +59,17 @@ noop_latter_request(struct request_queue *q, struct request *rq) return list_entry(rq->queuelist.next, struct request, queuelist); } -static void *noop_init_queue(struct request_queue *q) +static int noop_init_queue(struct request_queue *q) { struct noop_data *nd; nd = kmalloc_node(sizeof(*nd), GFP_KERNEL, q->node); if (!nd) - return NULL; + return -ENOMEM; + INIT_LIST_HEAD(&nd->queue); - return nd; + q->elevator->elevator_data = nd; + return 0; } static void noop_exit_queue(struct elevator_queue *e) diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 7d4e0356f329..97fb2557a18c 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -33,7 +33,7 @@ typedef void (elevator_put_req_fn) (struct request *); typedef void (elevator_activate_req_fn) (struct request_queue *, struct request *); typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct request *); -typedef void *(elevator_init_fn) (struct request_queue *); +typedef int (elevator_init_fn) (struct request_queue *); typedef void (elevator_exit_fn) (struct elevator_queue *); struct elevator_ops -- cgit v1.2.3 From d732580b4eb31553c63744a47d590f770cafb8f0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 5 Mar 2012 13:14:58 -0800 Subject: block: implement blk_queue_bypass_start/end() Rename and extend elv_queisce_start/end() to blk_queue_bypass_start/end() which are exported and supports nesting via @q->bypass_depth. Also add blk_queue_bypass() to test bypass state. This will be further extended and used for blkio_group management. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-core.c | 39 +++++++++++++++++++++++++++++++++++++-- block/blk.h | 6 ++---- block/elevator.c | 25 +++---------------------- include/linux/blkdev.h | 5 ++++- 4 files changed, 46 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index fccb25021121..98ddef430093 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -409,6 +409,42 @@ void blk_drain_queue(struct request_queue *q, bool drain_all) } } +/** + * blk_queue_bypass_start - enter queue bypass mode + * @q: queue of interest + * + * In bypass mode, only the dispatch FIFO queue of @q is used. This + * function makes @q enter bypass mode and drains all requests which were + * issued before. On return, it's guaranteed that no request has ELVPRIV + * set. + */ +void blk_queue_bypass_start(struct request_queue *q) +{ + spin_lock_irq(q->queue_lock); + q->bypass_depth++; + queue_flag_set(QUEUE_FLAG_BYPASS, q); + spin_unlock_irq(q->queue_lock); + + blk_drain_queue(q, false); +} +EXPORT_SYMBOL_GPL(blk_queue_bypass_start); + +/** + * blk_queue_bypass_end - leave queue bypass mode + * @q: queue of interest + * + * Leave bypass mode and restore the normal queueing behavior. + */ +void blk_queue_bypass_end(struct request_queue *q) +{ + spin_lock_irq(q->queue_lock); + if (!--q->bypass_depth) + queue_flag_clear(QUEUE_FLAG_BYPASS, q); + WARN_ON_ONCE(q->bypass_depth < 0); + spin_unlock_irq(q->queue_lock); +} +EXPORT_SYMBOL_GPL(blk_queue_bypass_end); + /** * blk_cleanup_queue - shutdown a request queue * @q: request queue to shutdown @@ -862,8 +898,7 @@ retry: * Also, lookup icq while holding queue_lock. If it doesn't exist, * it will be created after releasing queue_lock. */ - if (blk_rq_should_init_elevator(bio) && - !test_bit(QUEUE_FLAG_ELVSWITCH, &q->queue_flags)) { + if (blk_rq_should_init_elevator(bio) && !blk_queue_bypass(q)) { rw_flags |= REQ_ELVPRIV; rl->elvpriv++; if (et->icq_cache && ioc) diff --git a/block/blk.h b/block/blk.h index 9c12f80882b0..7422f3133c5d 100644 --- a/block/blk.h +++ b/block/blk.h @@ -23,7 +23,8 @@ void blk_rq_bio_prep(struct request_queue *q, struct request *rq, struct bio *bio); int blk_rq_append_bio(struct request_queue *q, struct request *rq, struct bio *bio); -void blk_drain_queue(struct request_queue *q, bool drain_all); +void blk_queue_bypass_start(struct request_queue *q); +void blk_queue_bypass_end(struct request_queue *q); void blk_dequeue_request(struct request *rq); void __blk_queue_free_tags(struct request_queue *q); bool __blk_end_bidi_request(struct request *rq, int error, @@ -144,9 +145,6 @@ void blk_queue_congestion_threshold(struct request_queue *q); int blk_dev_init(void); -void elv_quiesce_start(struct request_queue *q); -void elv_quiesce_end(struct request_queue *q); - /* * Return the threshold (number of used requests) at which the queue is diff --git a/block/elevator.c b/block/elevator.c index f81c061dad15..0bdea0ed03a3 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -553,25 +553,6 @@ void elv_drain_elevator(struct request_queue *q) } } -void elv_quiesce_start(struct request_queue *q) -{ - if (!q->elevator) - return; - - spin_lock_irq(q->queue_lock); - queue_flag_set(QUEUE_FLAG_ELVSWITCH, q); - spin_unlock_irq(q->queue_lock); - - blk_drain_queue(q, false); -} - -void elv_quiesce_end(struct request_queue *q) -{ - spin_lock_irq(q->queue_lock); - queue_flag_clear(QUEUE_FLAG_ELVSWITCH, q); - spin_unlock_irq(q->queue_lock); -} - void __elv_add_request(struct request_queue *q, struct request *rq, int where) { trace_block_rq_insert(q, rq); @@ -903,7 +884,7 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) * using INSERT_BACK. All requests have SOFTBARRIER set and no * merge happens either. */ - elv_quiesce_start(q); + blk_queue_bypass_start(q); /* unregister and clear all auxiliary data of the old elevator */ if (registered) @@ -933,7 +914,7 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) /* done, kill the old one and finish */ elevator_exit(old); - elv_quiesce_end(q); + blk_queue_bypass_end(q); blk_add_trace_msg(q, "elv switch: %s", new_e->elevator_name); @@ -945,7 +926,7 @@ fail_init: /* switch failed, restore and re-register old elevator */ q->elevator = old; elv_register_queue(q); - elv_quiesce_end(q); + blk_queue_bypass_end(q); return err; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 606cf339bb56..315db1d91bc4 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -389,6 +389,8 @@ struct request_queue { struct mutex sysfs_lock; + int bypass_depth; + #if defined(CONFIG_BLK_DEV_BSG) bsg_job_fn *bsg_job_fn; int bsg_job_size; @@ -406,7 +408,7 @@ struct request_queue { #define QUEUE_FLAG_SYNCFULL 3 /* read queue has been filled */ #define QUEUE_FLAG_ASYNCFULL 4 /* write queue has been filled */ #define QUEUE_FLAG_DEAD 5 /* queue being torn down */ -#define QUEUE_FLAG_ELVSWITCH 6 /* don't use elevator, just do FIFO */ +#define QUEUE_FLAG_BYPASS 6 /* act as dumb FIFO queue */ #define QUEUE_FLAG_BIDI 7 /* queue supports bidi requests */ #define QUEUE_FLAG_NOMERGES 8 /* disable merge attempts */ #define QUEUE_FLAG_SAME_COMP 9 /* complete on same CPU-group */ @@ -494,6 +496,7 @@ static inline void queue_flag_clear(unsigned int flag, struct request_queue *q) #define blk_queue_tagged(q) test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags) #define blk_queue_stopped(q) test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags) #define blk_queue_dead(q) test_bit(QUEUE_FLAG_DEAD, &(q)->queue_flags) +#define blk_queue_bypass(q) test_bit(QUEUE_FLAG_BYPASS, &(q)->queue_flags) #define blk_queue_nomerges(q) test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags) #define blk_queue_noxmerges(q) \ test_bit(QUEUE_FLAG_NOXMERGES, &(q)->queue_flags) -- cgit v1.2.3 From 923adde1be1df57cebd80c563058e503376645e8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 5 Mar 2012 13:15:13 -0800 Subject: blkcg: clear all request_queues on blkcg policy [un]registrations Keep track of all request_queues which have blkcg initialized and turn on bypass and invoke blkcg_clear_queue() on all before making changes to blkcg policies. This is to prepare for moving blkg management into blkcg core. Note that this uses more brute force than necessary. Finer grained shoot down will be implemented later and given that policy [un]registration almost never happens on running systems (blk-throtl can't be built as a module and cfq usually is the builtin default iosched), this shouldn't be a problem for the time being. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- include/linux/blkdev.h | 3 +++ 2 files changed, 50 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index b302ce1d662b..266c0707d588 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -27,6 +27,9 @@ static DEFINE_SPINLOCK(blkio_list_lock); static LIST_HEAD(blkio_list); +static DEFINE_MUTEX(all_q_mutex); +static LIST_HEAD(all_q_list); + struct blkio_cgroup blkio_root_cgroup = { .weight = 2*BLKIO_WEIGHT_DEFAULT }; EXPORT_SYMBOL_GPL(blkio_root_cgroup); @@ -1472,9 +1475,20 @@ done: */ int blkcg_init_queue(struct request_queue *q) { + int ret; + might_sleep(); - return blk_throtl_init(q); + ret = blk_throtl_init(q); + if (ret) + return ret; + + mutex_lock(&all_q_mutex); + INIT_LIST_HEAD(&q->all_q_node); + list_add_tail(&q->all_q_node, &all_q_list); + mutex_unlock(&all_q_mutex); + + return 0; } /** @@ -1498,6 +1512,10 @@ void blkcg_drain_queue(struct request_queue *q) */ void blkcg_exit_queue(struct request_queue *q) { + mutex_lock(&all_q_mutex); + list_del_init(&q->all_q_node); + mutex_unlock(&all_q_mutex); + blk_throtl_exit(q); } @@ -1543,8 +1561,33 @@ static void blkiocg_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, } } +static void blkcg_bypass_start(void) + __acquires(&all_q_mutex) +{ + struct request_queue *q; + + mutex_lock(&all_q_mutex); + + list_for_each_entry(q, &all_q_list, all_q_node) { + blk_queue_bypass_start(q); + blkg_destroy_all(q); + } +} + +static void blkcg_bypass_end(void) + __releases(&all_q_mutex) +{ + struct request_queue *q; + + list_for_each_entry(q, &all_q_list, all_q_node) + blk_queue_bypass_end(q); + + mutex_unlock(&all_q_mutex); +} + void blkio_policy_register(struct blkio_policy_type *blkiop) { + blkcg_bypass_start(); spin_lock(&blkio_list_lock); BUG_ON(blkio_policy[blkiop->plid]); @@ -1552,11 +1595,13 @@ void blkio_policy_register(struct blkio_policy_type *blkiop) list_add_tail(&blkiop->list, &blkio_list); spin_unlock(&blkio_list_lock); + blkcg_bypass_end(); } EXPORT_SYMBOL_GPL(blkio_policy_register); void blkio_policy_unregister(struct blkio_policy_type *blkiop) { + blkcg_bypass_start(); spin_lock(&blkio_list_lock); BUG_ON(blkio_policy[blkiop->plid] != blkiop); @@ -1564,5 +1609,6 @@ void blkio_policy_unregister(struct blkio_policy_type *blkiop) list_del_init(&blkiop->list); spin_unlock(&blkio_list_lock); + blkcg_bypass_end(); } EXPORT_SYMBOL_GPL(blkio_policy_unregister); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 315db1d91bc4..e8c0bbd06b9a 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -397,6 +397,9 @@ struct request_queue { struct bsg_class_device bsg_dev; #endif +#ifdef CONFIG_BLK_CGROUP + struct list_head all_q_node; +#endif #ifdef CONFIG_BLK_DEV_THROTTLING /* Throttle data */ struct throtl_data *td; -- cgit v1.2.3 From 4eef3049986e8397d5003916aed8cad6567a5e02 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 5 Mar 2012 13:15:18 -0800 Subject: blkcg: move per-queue blkg list heads and counters to queue and blkg Currently, specific policy implementations are responsible for maintaining list and number of blkgs. This duplicates code unnecessarily, and hinders factoring common code and providing blkcg API with better defined semantics. After this patch, request_queue hosts list heads and counters and blkg has list nodes for both policies. This patch only relocates the necessary fields and the next patch will actually move management code into blkcg core. Note that request_queue->blkg_list[] and ->nr_blkgs[] are hardcoded to have 2 elements. This is to avoid include dependency and will be removed by the next patch. This patch doesn't introduce any behavior change. -v2: Now unnecessary conditional on CONFIG_BLK_CGROUP_MODULE removed as pointed out by Vivek. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 2 ++ block/blk-cgroup.h | 1 + block/blk-core.c | 4 ++++ block/blk-throttle.c | 49 +++++++++++++++++++++++-------------------------- block/cfq-iosched.c | 47 +++++++++++++++++++---------------------------- include/linux/blkdev.h | 5 +++++ 6 files changed, 54 insertions(+), 54 deletions(-) (limited to 'include/linux') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 91f9824be5cc..e940972ccd66 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -499,6 +499,8 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, spin_lock_init(&blkg->stats_lock); rcu_assign_pointer(blkg->q, q); + INIT_LIST_HEAD(&blkg->q_node[0]); + INIT_LIST_HEAD(&blkg->q_node[1]); blkg->blkcg = blkcg; blkg->plid = pol->plid; blkg->refcnt = 1; diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 60e96b4be4ce..ae96f196d469 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -178,6 +178,7 @@ struct blkg_policy_data { struct blkio_group { /* Pointer to the associated request_queue, RCU protected */ struct request_queue __rcu *q; + struct list_head q_node[BLKIO_NR_POLICIES]; struct hlist_node blkcg_node; struct blkio_cgroup *blkcg; /* Store cgroup path */ diff --git a/block/blk-core.c b/block/blk-core.c index c3434c6395b9..83a47fcf5946 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -547,6 +547,10 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) INIT_LIST_HEAD(&q->queue_head); INIT_LIST_HEAD(&q->timeout_list); INIT_LIST_HEAD(&q->icq_list); +#ifdef CONFIG_BLK_CGROUP + INIT_LIST_HEAD(&q->blkg_list[0]); + INIT_LIST_HEAD(&q->blkg_list[1]); +#endif INIT_LIST_HEAD(&q->flush_queue[0]); INIT_LIST_HEAD(&q->flush_queue[1]); INIT_LIST_HEAD(&q->flush_data_in_flight); diff --git a/block/blk-throttle.c b/block/blk-throttle.c index b2fddaf20b98..c15d38307e1d 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -41,9 +41,6 @@ struct throtl_rb_root { #define rb_entry_tg(node) rb_entry((node), struct throtl_grp, rb_node) struct throtl_grp { - /* List of throtl groups on the request queue*/ - struct hlist_node tg_node; - /* active throtl group service_tree member */ struct rb_node rb_node; @@ -83,9 +80,6 @@ struct throtl_grp { struct throtl_data { - /* List of throtl groups */ - struct hlist_head tg_list; - /* service tree for active throtl groups */ struct throtl_rb_root tg_service_tree; @@ -152,7 +146,6 @@ static void throtl_init_blkio_group(struct blkio_group *blkg) { struct throtl_grp *tg = blkg_to_tg(blkg); - INIT_HLIST_NODE(&tg->tg_node); RB_CLEAR_NODE(&tg->rb_node); bio_list_init(&tg->bio_lists[0]); bio_list_init(&tg->bio_lists[1]); @@ -167,11 +160,9 @@ static void throtl_init_blkio_group(struct blkio_group *blkg) static void throtl_link_blkio_group(struct request_queue *q, struct blkio_group *blkg) { - struct throtl_data *td = q->td; - struct throtl_grp *tg = blkg_to_tg(blkg); - - hlist_add_head(&tg->tg_node, &td->tg_list); - td->nr_undestroyed_grps++; + list_add(&blkg->q_node[BLKIO_POLICY_THROTL], + &q->blkg_list[BLKIO_POLICY_THROTL]); + q->nr_blkgs[BLKIO_POLICY_THROTL]++; } static struct @@ -711,8 +702,8 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl) static void throtl_process_limit_change(struct throtl_data *td) { - struct throtl_grp *tg; - struct hlist_node *pos, *n; + struct request_queue *q = td->queue; + struct blkio_group *blkg, *n; if (!td->limits_changed) return; @@ -721,7 +712,10 @@ static void throtl_process_limit_change(struct throtl_data *td) throtl_log(td, "limits changed"); - hlist_for_each_entry_safe(tg, pos, n, &td->tg_list, tg_node) { + list_for_each_entry_safe(blkg, n, &q->blkg_list[BLKIO_POLICY_THROTL], + q_node[BLKIO_POLICY_THROTL]) { + struct throtl_grp *tg = blkg_to_tg(blkg); + if (!tg->limits_changed) continue; @@ -822,26 +816,31 @@ throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay) static void throtl_destroy_tg(struct throtl_data *td, struct throtl_grp *tg) { + struct blkio_group *blkg = tg_to_blkg(tg); + /* Something wrong if we are trying to remove same group twice */ - BUG_ON(hlist_unhashed(&tg->tg_node)); + WARN_ON_ONCE(list_empty(&blkg->q_node[BLKIO_POLICY_THROTL])); - hlist_del_init(&tg->tg_node); + list_del_init(&blkg->q_node[BLKIO_POLICY_THROTL]); /* * Put the reference taken at the time of creation so that when all * queues are gone, group can be destroyed. */ blkg_put(tg_to_blkg(tg)); - td->nr_undestroyed_grps--; + td->queue->nr_blkgs[BLKIO_POLICY_THROTL]--; } static bool throtl_release_tgs(struct throtl_data *td, bool release_root) { - struct hlist_node *pos, *n; - struct throtl_grp *tg; + struct request_queue *q = td->queue; + struct blkio_group *blkg, *n; bool empty = true; - hlist_for_each_entry_safe(tg, pos, n, &td->tg_list, tg_node) { + list_for_each_entry_safe(blkg, n, &q->blkg_list[BLKIO_POLICY_THROTL], + q_node[BLKIO_POLICY_THROTL]) { + struct throtl_grp *tg = blkg_to_tg(blkg); + /* skip root? */ if (!release_root && tg == td->root_tg) continue; @@ -851,7 +850,7 @@ static bool throtl_release_tgs(struct throtl_data *td, bool release_root) * it from cgroup list, then it will take care of destroying * cfqg also. */ - if (!blkiocg_del_blkio_group(tg_to_blkg(tg))) + if (!blkiocg_del_blkio_group(blkg)) throtl_destroy_tg(td, tg); else empty = false; @@ -1114,7 +1113,6 @@ int blk_throtl_init(struct request_queue *q) if (!td) return -ENOMEM; - INIT_HLIST_HEAD(&td->tg_list); td->tg_service_tree = THROTL_RB_ROOT; td->limits_changed = false; INIT_DELAYED_WORK(&td->throtl_work, blk_throtl_work); @@ -1144,7 +1142,7 @@ int blk_throtl_init(struct request_queue *q) void blk_throtl_exit(struct request_queue *q) { struct throtl_data *td = q->td; - bool wait = false; + bool wait; BUG_ON(!td); @@ -1154,8 +1152,7 @@ void blk_throtl_exit(struct request_queue *q) throtl_release_tgs(td, true); /* If there are other groups */ - if (td->nr_undestroyed_grps > 0) - wait = true; + wait = q->nr_blkgs[BLKIO_POLICY_THROTL]; spin_unlock_irq(q->queue_lock); diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 11dd9d7f2edb..e846803280a6 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -208,9 +208,7 @@ struct cfq_group { unsigned long saved_workload_slice; enum wl_type_t saved_workload; enum wl_prio_t saved_serving_prio; -#ifdef CONFIG_CFQ_GROUP_IOSCHED - struct hlist_node cfqd_node; -#endif + /* number of requests that are on the dispatch list or inside driver */ int dispatched; struct cfq_ttime ttime; @@ -302,12 +300,6 @@ struct cfq_data { struct cfq_queue oom_cfqq; unsigned long last_delayed_sync; - - /* List of cfq groups being managed on this device*/ - struct hlist_head cfqg_list; - - /* Number of groups which are on blkcg->blkg_list */ - unsigned int nr_blkcg_linked_grps; }; static inline struct cfq_group *blkg_to_cfqg(struct blkio_group *blkg) @@ -1056,13 +1048,9 @@ static void cfq_update_blkio_group_weight(struct request_queue *q, static void cfq_link_blkio_group(struct request_queue *q, struct blkio_group *blkg) { - struct cfq_data *cfqd = q->elevator->elevator_data; - struct cfq_group *cfqg = blkg_to_cfqg(blkg); - - cfqd->nr_blkcg_linked_grps++; - - /* Add group on cfqd list */ - hlist_add_head(&cfqg->cfqd_node, &cfqd->cfqg_list); + list_add(&blkg->q_node[BLKIO_POLICY_PROP], + &q->blkg_list[BLKIO_POLICY_PROP]); + q->nr_blkgs[BLKIO_POLICY_PROP]++; } static void cfq_init_blkio_group(struct blkio_group *blkg) @@ -1110,13 +1098,15 @@ static void cfq_link_cfqq_cfqg(struct cfq_queue *cfqq, struct cfq_group *cfqg) static void cfq_destroy_cfqg(struct cfq_data *cfqd, struct cfq_group *cfqg) { + struct blkio_group *blkg = cfqg_to_blkg(cfqg); + /* Something wrong if we are trying to remove same group twice */ - BUG_ON(hlist_unhashed(&cfqg->cfqd_node)); + BUG_ON(list_empty(&blkg->q_node[BLKIO_POLICY_PROP])); - hlist_del_init(&cfqg->cfqd_node); + list_del_init(&blkg->q_node[BLKIO_POLICY_PROP]); - BUG_ON(cfqd->nr_blkcg_linked_grps <= 0); - cfqd->nr_blkcg_linked_grps--; + BUG_ON(cfqd->queue->nr_blkgs[BLKIO_POLICY_PROP] <= 0); + cfqd->queue->nr_blkgs[BLKIO_POLICY_PROP]--; /* * Put the reference taken at the time of creation so that when all @@ -1127,18 +1117,19 @@ static void cfq_destroy_cfqg(struct cfq_data *cfqd, struct cfq_group *cfqg) static bool cfq_release_cfq_groups(struct cfq_data *cfqd) { - struct hlist_node *pos, *n; - struct cfq_group *cfqg; + struct request_queue *q = cfqd->queue; + struct blkio_group *blkg, *n; bool empty = true; - hlist_for_each_entry_safe(cfqg, pos, n, &cfqd->cfqg_list, cfqd_node) { + list_for_each_entry_safe(blkg, n, &q->blkg_list[BLKIO_POLICY_PROP], + q_node[BLKIO_POLICY_PROP]) { /* * If cgroup removal path got to blk_group first and removed * it from cgroup list, then it will take care of destroying * cfqg also. */ - if (!cfq_blkiocg_del_blkio_group(cfqg_to_blkg(cfqg))) - cfq_destroy_cfqg(cfqd, cfqg); + if (!cfq_blkiocg_del_blkio_group(blkg)) + cfq_destroy_cfqg(cfqd, blkg_to_cfqg(blkg)); else empty = false; } @@ -3558,13 +3549,13 @@ static void cfq_exit_queue(struct elevator_queue *e) cfq_put_async_queues(cfqd); cfq_release_cfq_groups(cfqd); +#ifdef CONFIG_BLK_CGROUP /* * If there are groups which we could not unlink from blkcg list, * wait for a rcu period for them to be freed. */ - if (cfqd->nr_blkcg_linked_grps) - wait = true; - + wait = q->nr_blkgs[BLKIO_POLICY_PROP]; +#endif spin_unlock_irq(q->queue_lock); cfq_shutdown_timer_wq(cfqd); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e8c0bbd06b9a..f4e35edea70f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -362,6 +362,11 @@ struct request_queue { struct list_head timeout_list; struct list_head icq_list; +#ifdef CONFIG_BLK_CGROUP + /* XXX: array size hardcoded to avoid include dependency (temporary) */ + struct list_head blkg_list[2]; + int nr_blkgs[2]; +#endif struct queue_limits limits; -- cgit v1.2.3 From 03aa264ac15637b6f98374270bcdf31400965505 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 5 Mar 2012 13:15:19 -0800 Subject: blkcg: let blkcg core manage per-queue blkg list and counter With the previous patch to move blkg list heads and counters to request_queue and blkg, logic to manage them in both policies are almost identical and can be moved to blkcg core. This patch moves blkg link logic into blkg_lookup_create(), implements common blkg unlink code in blkg_destroy(), and updates blkg_destory_all() so that it's policy specific and can skip root group. The updated blkg_destroy_all() is now used to both clear queue for bypassing and elv switching, and release all blkgs on q exit. This patch introduces a race window where policy [de]registration may race against queue blkg clearing. This can only be a problem on cfq unload and shouldn't be a real problem in practice (and we have many other places where this race already exists). Future patches will remove these unlikely races. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 72 +++++++++++++++++++++++++++-------- block/blk-cgroup.h | 15 +++----- block/blk-throttle.c | 99 +----------------------------------------------- block/cfq-iosched.c | 100 +++---------------------------------------------- block/elevator.c | 5 ++- include/linux/blkdev.h | 4 +- 6 files changed, 74 insertions(+), 221 deletions(-) (limited to 'include/linux') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index e940972ccd66..2ca9a15db0f7 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -596,8 +596,11 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, /* insert */ spin_lock(&blkcg->lock); swap(blkg, new_blkg); + hlist_add_head_rcu(&blkg->blkcg_node, &blkcg->blkg_list); - pol->ops.blkio_link_group_fn(q, blkg); + list_add(&blkg->q_node[plid], &q->blkg_list[plid]); + q->nr_blkgs[plid]++; + spin_unlock(&blkcg->lock); out: blkg_free(new_blkg); @@ -646,36 +649,69 @@ struct blkio_group *blkg_lookup(struct blkio_cgroup *blkcg, } EXPORT_SYMBOL_GPL(blkg_lookup); -void blkg_destroy_all(struct request_queue *q) +static void blkg_destroy(struct blkio_group *blkg, enum blkio_policy_id plid) +{ + struct request_queue *q = blkg->q; + + lockdep_assert_held(q->queue_lock); + + /* Something wrong if we are trying to remove same group twice */ + WARN_ON_ONCE(list_empty(&blkg->q_node[plid])); + list_del_init(&blkg->q_node[plid]); + + WARN_ON_ONCE(q->nr_blkgs[plid] <= 0); + q->nr_blkgs[plid]--; + + /* + * Put the reference taken at the time of creation so that when all + * queues are gone, group can be destroyed. + */ + blkg_put(blkg); +} + +void blkg_destroy_all(struct request_queue *q, enum blkio_policy_id plid, + bool destroy_root) { - struct blkio_policy_type *pol; + struct blkio_group *blkg, *n; while (true) { bool done = true; - spin_lock(&blkio_list_lock); spin_lock_irq(q->queue_lock); - /* - * clear_queue_fn() might return with non-empty group list - * if it raced cgroup removal and lost. cgroup removal is - * guaranteed to make forward progress and retrying after a - * while is enough. This ugliness is scheduled to be - * removed after locking update. - */ - list_for_each_entry(pol, &blkio_list, list) - if (!pol->ops.blkio_clear_queue_fn(q)) + list_for_each_entry_safe(blkg, n, &q->blkg_list[plid], + q_node[plid]) { + /* skip root? */ + if (!destroy_root && blkg->blkcg == &blkio_root_cgroup) + continue; + + /* + * If cgroup removal path got to blk_group first + * and removed it from cgroup list, then it will + * take care of destroying cfqg also. + */ + if (!blkiocg_del_blkio_group(blkg)) + blkg_destroy(blkg, plid); + else done = false; + } spin_unlock_irq(q->queue_lock); - spin_unlock(&blkio_list_lock); + /* + * Group list may not be empty if we raced cgroup removal + * and lost. cgroup removal is guaranteed to make forward + * progress and retrying after a while is enough. This + * ugliness is scheduled to be removed after locking + * update. + */ if (done) break; msleep(10); /* just some random duration I like */ } } +EXPORT_SYMBOL_GPL(blkg_destroy_all); static void blkg_rcu_free(struct rcu_head *rcu_head) { @@ -1549,11 +1585,13 @@ static int blkiocg_pre_destroy(struct cgroup_subsys *subsys, * this event. */ spin_lock(&blkio_list_lock); + spin_lock_irqsave(q->queue_lock, flags); list_for_each_entry(blkiop, &blkio_list, list) { if (blkiop->plid != blkg->plid) continue; - blkiop->ops.blkio_unlink_group_fn(q, blkg); + blkg_destroy(blkg, blkiop->plid); } + spin_unlock_irqrestore(q->queue_lock, flags); spin_unlock(&blkio_list_lock); } while (1); @@ -1695,12 +1733,14 @@ static void blkcg_bypass_start(void) __acquires(&all_q_mutex) { struct request_queue *q; + int i; mutex_lock(&all_q_mutex); list_for_each_entry(q, &all_q_list, all_q_node) { blk_queue_bypass_start(q); - blkg_destroy_all(q); + for (i = 0; i < BLKIO_NR_POLICIES; i++) + blkg_destroy_all(q, i, false); } } diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index ae96f196d469..83ce5fa0a604 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -196,11 +196,6 @@ struct blkio_group { }; typedef void (blkio_init_group_fn)(struct blkio_group *blkg); -typedef void (blkio_link_group_fn)(struct request_queue *q, - struct blkio_group *blkg); -typedef void (blkio_unlink_group_fn)(struct request_queue *q, - struct blkio_group *blkg); -typedef bool (blkio_clear_queue_fn)(struct request_queue *q); typedef void (blkio_update_group_weight_fn)(struct request_queue *q, struct blkio_group *blkg, unsigned int weight); typedef void (blkio_update_group_read_bps_fn)(struct request_queue *q, @@ -214,9 +209,6 @@ typedef void (blkio_update_group_write_iops_fn)(struct request_queue *q, struct blkio_policy_ops { blkio_init_group_fn *blkio_init_group_fn; - blkio_link_group_fn *blkio_link_group_fn; - blkio_unlink_group_fn *blkio_unlink_group_fn; - blkio_clear_queue_fn *blkio_clear_queue_fn; blkio_update_group_weight_fn *blkio_update_group_weight_fn; blkio_update_group_read_bps_fn *blkio_update_group_read_bps_fn; blkio_update_group_write_bps_fn *blkio_update_group_write_bps_fn; @@ -238,7 +230,8 @@ extern void blkcg_exit_queue(struct request_queue *q); /* Blkio controller policy registration */ extern void blkio_policy_register(struct blkio_policy_type *); extern void blkio_policy_unregister(struct blkio_policy_type *); -extern void blkg_destroy_all(struct request_queue *q); +extern void blkg_destroy_all(struct request_queue *q, + enum blkio_policy_id plid, bool destroy_root); /** * blkg_to_pdata - get policy private data @@ -319,7 +312,9 @@ static inline void blkcg_drain_queue(struct request_queue *q) { } static inline void blkcg_exit_queue(struct request_queue *q) { } static inline void blkio_policy_register(struct blkio_policy_type *blkiop) { } static inline void blkio_policy_unregister(struct blkio_policy_type *blkiop) { } -static inline void blkg_destroy_all(struct request_queue *q) { } +static inline void blkg_destroy_all(struct request_queue *q, + enum blkio_policy_id plid, + bool destory_root) { } static inline void *blkg_to_pdata(struct blkio_group *blkg, struct blkio_policy_type *pol) { return NULL; } diff --git a/block/blk-throttle.c b/block/blk-throttle.c index c15d38307e1d..132941260e58 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -157,14 +157,6 @@ static void throtl_init_blkio_group(struct blkio_group *blkg) tg->iops[WRITE] = -1; } -static void throtl_link_blkio_group(struct request_queue *q, - struct blkio_group *blkg) -{ - list_add(&blkg->q_node[BLKIO_POLICY_THROTL], - &q->blkg_list[BLKIO_POLICY_THROTL]); - q->nr_blkgs[BLKIO_POLICY_THROTL]++; -} - static struct throtl_grp *throtl_lookup_tg(struct throtl_data *td, struct blkio_cgroup *blkcg) { @@ -813,89 +805,6 @@ throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay) } } -static void -throtl_destroy_tg(struct throtl_data *td, struct throtl_grp *tg) -{ - struct blkio_group *blkg = tg_to_blkg(tg); - - /* Something wrong if we are trying to remove same group twice */ - WARN_ON_ONCE(list_empty(&blkg->q_node[BLKIO_POLICY_THROTL])); - - list_del_init(&blkg->q_node[BLKIO_POLICY_THROTL]); - - /* - * Put the reference taken at the time of creation so that when all - * queues are gone, group can be destroyed. - */ - blkg_put(tg_to_blkg(tg)); - td->queue->nr_blkgs[BLKIO_POLICY_THROTL]--; -} - -static bool throtl_release_tgs(struct throtl_data *td, bool release_root) -{ - struct request_queue *q = td->queue; - struct blkio_group *blkg, *n; - bool empty = true; - - list_for_each_entry_safe(blkg, n, &q->blkg_list[BLKIO_POLICY_THROTL], - q_node[BLKIO_POLICY_THROTL]) { - struct throtl_grp *tg = blkg_to_tg(blkg); - - /* skip root? */ - if (!release_root && tg == td->root_tg) - continue; - - /* - * If cgroup removal path got to blk_group first and removed - * it from cgroup list, then it will take care of destroying - * cfqg also. - */ - if (!blkiocg_del_blkio_group(blkg)) - throtl_destroy_tg(td, tg); - else - empty = false; - } - return empty; -} - -/* - * Blk cgroup controller notification saying that blkio_group object is being - * delinked as associated cgroup object is going away. That also means that - * no new IO will come in this group. So get rid of this group as soon as - * any pending IO in the group is finished. - * - * This function is called under rcu_read_lock(). @q is the rcu protected - * pointer. That means @q is a valid request_queue pointer as long as we - * are rcu read lock. - * - * @q was fetched from blkio_group under blkio_cgroup->lock. That means - * it should not be NULL as even if queue was going away, cgroup deltion - * path got to it first. - */ -void throtl_unlink_blkio_group(struct request_queue *q, - struct blkio_group *blkg) -{ - unsigned long flags; - - spin_lock_irqsave(q->queue_lock, flags); - throtl_destroy_tg(q->td, blkg_to_tg(blkg)); - spin_unlock_irqrestore(q->queue_lock, flags); -} - -static bool throtl_clear_queue(struct request_queue *q) -{ - lockdep_assert_held(q->queue_lock); - - /* - * Clear tgs but leave the root one alone. This is necessary - * because root_tg is expected to be persistent and safe because - * blk-throtl can never be disabled while @q is alive. This is a - * kludge to prepare for unified blkg. This whole function will be - * removed soon. - */ - return throtl_release_tgs(q->td, false); -} - static void throtl_update_blkio_group_common(struct throtl_data *td, struct throtl_grp *tg) { @@ -960,9 +869,6 @@ static void throtl_shutdown_wq(struct request_queue *q) static struct blkio_policy_type blkio_policy_throtl = { .ops = { .blkio_init_group_fn = throtl_init_blkio_group, - .blkio_link_group_fn = throtl_link_blkio_group, - .blkio_unlink_group_fn = throtl_unlink_blkio_group, - .blkio_clear_queue_fn = throtl_clear_queue, .blkio_update_group_read_bps_fn = throtl_update_blkio_group_read_bps, .blkio_update_group_write_bps_fn = @@ -1148,12 +1054,11 @@ void blk_throtl_exit(struct request_queue *q) throtl_shutdown_wq(q); - spin_lock_irq(q->queue_lock); - throtl_release_tgs(td, true); + blkg_destroy_all(q, BLKIO_POLICY_THROTL, true); /* If there are other groups */ + spin_lock_irq(q->queue_lock); wait = q->nr_blkgs[BLKIO_POLICY_THROTL]; - spin_unlock_irq(q->queue_lock); /* diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index e846803280a6..dc73690dec44 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1045,14 +1045,6 @@ static void cfq_update_blkio_group_weight(struct request_queue *q, cfqg->needs_update = true; } -static void cfq_link_blkio_group(struct request_queue *q, - struct blkio_group *blkg) -{ - list_add(&blkg->q_node[BLKIO_POLICY_PROP], - &q->blkg_list[BLKIO_POLICY_PROP]); - q->nr_blkgs[BLKIO_POLICY_PROP]++; -} - static void cfq_init_blkio_group(struct blkio_group *blkg) { struct cfq_group *cfqg = blkg_to_cfqg(blkg); @@ -1096,84 +1088,6 @@ static void cfq_link_cfqq_cfqg(struct cfq_queue *cfqq, struct cfq_group *cfqg) blkg_get(cfqg_to_blkg(cfqg)); } -static void cfq_destroy_cfqg(struct cfq_data *cfqd, struct cfq_group *cfqg) -{ - struct blkio_group *blkg = cfqg_to_blkg(cfqg); - - /* Something wrong if we are trying to remove same group twice */ - BUG_ON(list_empty(&blkg->q_node[BLKIO_POLICY_PROP])); - - list_del_init(&blkg->q_node[BLKIO_POLICY_PROP]); - - BUG_ON(cfqd->queue->nr_blkgs[BLKIO_POLICY_PROP] <= 0); - cfqd->queue->nr_blkgs[BLKIO_POLICY_PROP]--; - - /* - * Put the reference taken at the time of creation so that when all - * queues are gone, group can be destroyed. - */ - blkg_put(cfqg_to_blkg(cfqg)); -} - -static bool cfq_release_cfq_groups(struct cfq_data *cfqd) -{ - struct request_queue *q = cfqd->queue; - struct blkio_group *blkg, *n; - bool empty = true; - - list_for_each_entry_safe(blkg, n, &q->blkg_list[BLKIO_POLICY_PROP], - q_node[BLKIO_POLICY_PROP]) { - /* - * If cgroup removal path got to blk_group first and removed - * it from cgroup list, then it will take care of destroying - * cfqg also. - */ - if (!cfq_blkiocg_del_blkio_group(blkg)) - cfq_destroy_cfqg(cfqd, blkg_to_cfqg(blkg)); - else - empty = false; - } - return empty; -} - -/* - * Blk cgroup controller notification saying that blkio_group object is being - * delinked as associated cgroup object is going away. That also means that - * no new IO will come in this group. So get rid of this group as soon as - * any pending IO in the group is finished. - * - * This function is called under rcu_read_lock(). key is the rcu protected - * pointer. That means @q is a valid request_queue pointer as long as we - * are rcu read lock. - * - * @q was fetched from blkio_group under blkio_cgroup->lock. That means - * it should not be NULL as even if elevator was exiting, cgroup deltion - * path got to it first. - */ -static void cfq_unlink_blkio_group(struct request_queue *q, - struct blkio_group *blkg) -{ - struct cfq_data *cfqd = q->elevator->elevator_data; - unsigned long flags; - - spin_lock_irqsave(q->queue_lock, flags); - cfq_destroy_cfqg(cfqd, blkg_to_cfqg(blkg)); - spin_unlock_irqrestore(q->queue_lock, flags); -} - -static struct elevator_type iosched_cfq; - -static bool cfq_clear_queue(struct request_queue *q) -{ - lockdep_assert_held(q->queue_lock); - - /* shoot down blkgs iff the current elevator is cfq */ - if (!q->elevator || q->elevator->type != &iosched_cfq) - return true; - - return cfq_release_cfq_groups(q->elevator->elevator_data); -} - #else /* GROUP_IOSCHED */ static struct cfq_group *cfq_lookup_create_cfqg(struct cfq_data *cfqd, struct blkio_cgroup *blkcg) @@ -1186,8 +1100,6 @@ cfq_link_cfqq_cfqg(struct cfq_queue *cfqq, struct cfq_group *cfqg) { cfqq->cfqg = cfqg; } -static void cfq_release_cfq_groups(struct cfq_data *cfqd) {} - #endif /* GROUP_IOSCHED */ /* @@ -3547,17 +3459,20 @@ static void cfq_exit_queue(struct elevator_queue *e) __cfq_slice_expired(cfqd, cfqd->active_queue, 0); cfq_put_async_queues(cfqd); - cfq_release_cfq_groups(cfqd); + + spin_unlock_irq(q->queue_lock); + + blkg_destroy_all(q, BLKIO_POLICY_PROP, true); #ifdef CONFIG_BLK_CGROUP /* * If there are groups which we could not unlink from blkcg list, * wait for a rcu period for them to be freed. */ + spin_lock_irq(q->queue_lock); wait = q->nr_blkgs[BLKIO_POLICY_PROP]; -#endif spin_unlock_irq(q->queue_lock); - +#endif cfq_shutdown_timer_wq(cfqd); /* @@ -3794,9 +3709,6 @@ static struct elevator_type iosched_cfq = { static struct blkio_policy_type blkio_policy_cfq = { .ops = { .blkio_init_group_fn = cfq_init_blkio_group, - .blkio_link_group_fn = cfq_link_blkio_group, - .blkio_unlink_group_fn = cfq_unlink_blkio_group, - .blkio_clear_queue_fn = cfq_clear_queue, .blkio_update_group_weight_fn = cfq_update_blkio_group_weight, }, .plid = BLKIO_POLICY_PROP, diff --git a/block/elevator.c b/block/elevator.c index 8c7561fd2c79..d4d39dab841a 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -876,7 +876,7 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) { struct elevator_queue *old = q->elevator; bool registered = old->registered; - int err; + int i, err; /* * Turn on BYPASS and drain all requests w/ elevator private data. @@ -895,7 +895,8 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) ioc_clear_queue(q); spin_unlock_irq(q->queue_lock); - blkg_destroy_all(q); + for (i = 0; i < BLKIO_NR_POLICIES; i++) + blkg_destroy_all(q, i, false); /* allocate, init and register new elevator */ err = -ENOMEM; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index f4e35edea70f..b4d1d4bfc168 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -364,8 +364,8 @@ struct request_queue { struct list_head icq_list; #ifdef CONFIG_BLK_CGROUP /* XXX: array size hardcoded to avoid include dependency (temporary) */ - struct list_head blkg_list[2]; - int nr_blkgs[2]; + struct list_head blkg_list; + int nr_blkgs; #endif struct queue_limits limits; -- cgit v1.2.3 From c875f4d0250a1f070fa26087a73bdd8f54c48100 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 5 Mar 2012 13:15:22 -0800 Subject: blkcg: drop unnecessary RCU locking Now that blkg additions / removals are always done under both q and blkcg locks, the only places RCU locking is necessary are blkg_lookup[_create]() for lookup w/o blkcg lock. This patch drops unncessary RCU locking replacing it with plain blkcg locking as necessary. * blkiocg_pre_destroy() already perform proper locking and don't need RCU. Dropped. * blkio_read_blkg_stats() now uses blkcg->lock instead of RCU read lock. This isn't a hot path. * Now unnecessary synchronize_rcu() from queue exit paths removed. This makes q->nr_blkgs unnecessary. Dropped. * RCU annotation on blkg->q removed. -v2: Vivek pointed out that blkg_lookup_create() still needs to be called under rcu_read_lock(). Updated. -v3: After the update, stats_lock locking in blkio_read_blkg_stats() shouldn't be using _irq variant as it otherwise ends up enabling irq while blkcg->lock is locked. Fixed. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 24 +++++++++--------------- block/blk-cgroup.h | 4 ++-- block/blk-throttle.c | 33 +-------------------------------- block/cfq-iosched.c | 24 ------------------------ include/linux/blkdev.h | 1 - 5 files changed, 12 insertions(+), 74 deletions(-) (limited to 'include/linux') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index e9e3b038c702..27d39a810cb6 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -500,7 +500,7 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, return NULL; spin_lock_init(&blkg->stats_lock); - rcu_assign_pointer(blkg->q, q); + blkg->q = q; INIT_LIST_HEAD(&blkg->q_node); blkg->blkcg = blkcg; blkg->refcnt = 1; @@ -611,7 +611,6 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, hlist_add_head_rcu(&blkg->blkcg_node, &blkcg->blkg_list); list_add(&blkg->q_node, &q->blkg_list); - q->nr_blkgs++; spin_unlock(&blkcg->lock); out: @@ -648,9 +647,6 @@ static void blkg_destroy(struct blkio_group *blkg) list_del_init(&blkg->q_node); hlist_del_init_rcu(&blkg->blkcg_node); - WARN_ON_ONCE(q->nr_blkgs <= 0); - q->nr_blkgs--; - /* * Put the reference taken at the time of creation so that when all * queues are gone, group can be destroyed. @@ -1232,8 +1228,9 @@ static int blkio_read_blkg_stats(struct blkio_cgroup *blkcg, struct hlist_node *n; uint64_t cgroup_total = 0; - rcu_read_lock(); - hlist_for_each_entry_rcu(blkg, n, &blkcg->blkg_list, blkcg_node) { + spin_lock_irq(&blkcg->lock); + + hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) { const char *dname = blkg_dev_name(blkg); int plid = BLKIOFILE_POLICY(cft->private); @@ -1243,15 +1240,16 @@ static int blkio_read_blkg_stats(struct blkio_cgroup *blkcg, cgroup_total += blkio_get_stat_cpu(blkg, plid, cb, dname, type); } else { - spin_lock_irq(&blkg->stats_lock); + spin_lock(&blkg->stats_lock); cgroup_total += blkio_get_stat(blkg, plid, cb, dname, type); - spin_unlock_irq(&blkg->stats_lock); + spin_unlock(&blkg->stats_lock); } } if (show_total) cb->fill(cb, "Total", cgroup_total); - rcu_read_unlock(); + + spin_unlock_irq(&blkcg->lock); return 0; } @@ -1583,28 +1581,24 @@ static int blkiocg_pre_destroy(struct cgroup_subsys *subsys, { struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgroup); - rcu_read_lock(); spin_lock_irq(&blkcg->lock); while (!hlist_empty(&blkcg->blkg_list)) { struct blkio_group *blkg = hlist_entry(blkcg->blkg_list.first, struct blkio_group, blkcg_node); - struct request_queue *q = rcu_dereference(blkg->q); + struct request_queue *q = blkg->q; if (spin_trylock(q->queue_lock)) { blkg_destroy(blkg); spin_unlock(q->queue_lock); } else { spin_unlock_irq(&blkcg->lock); - rcu_read_unlock(); cpu_relax(); - rcu_read_lock(); spin_lock(&blkcg->lock); } } spin_unlock_irq(&blkcg->lock); - rcu_read_unlock(); return 0; } diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index df73040a6a5f..66eaefefcbd2 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -176,8 +176,8 @@ struct blkg_policy_data { }; struct blkio_group { - /* Pointer to the associated request_queue, RCU protected */ - struct request_queue __rcu *q; + /* Pointer to the associated request_queue */ + struct request_queue *q; struct list_head q_node; struct hlist_node blkcg_node; struct blkio_cgroup *blkcg; diff --git a/block/blk-throttle.c b/block/blk-throttle.c index e35ee7aeea69..bfa5168249eb 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1046,39 +1046,8 @@ int blk_throtl_init(struct request_queue *q) void blk_throtl_exit(struct request_queue *q) { - struct throtl_data *td = q->td; - bool wait; - - BUG_ON(!td); - + BUG_ON(!q->td); throtl_shutdown_wq(q); - - /* If there are other groups */ - spin_lock_irq(q->queue_lock); - wait = q->nr_blkgs; - spin_unlock_irq(q->queue_lock); - - /* - * Wait for tg_to_blkg(tg)->q accessors to exit their grace periods. - * Do this wait only if there are other undestroyed groups out - * there (other than root group). This can happen if cgroup deletion - * path claimed the responsibility of cleaning up a group before - * queue cleanup code get to the group. - * - * Do not call synchronize_rcu() unconditionally as there are drivers - * which create/delete request queue hundreds of times during scan/boot - * and synchronize_rcu() can take significant time and slow down boot. - */ - if (wait) - synchronize_rcu(); - - /* - * Just being safe to make sure after previous flush if some body did - * update limits through cgroup and another work got queued, cancel - * it. - */ - throtl_shutdown_wq(q); - kfree(q->td); } diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 393eaa59913b..9e386d9bcb79 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3449,7 +3449,6 @@ static void cfq_exit_queue(struct elevator_queue *e) { struct cfq_data *cfqd = e->elevator_data; struct request_queue *q = cfqd->queue; - bool wait = false; cfq_shutdown_timer_wq(cfqd); @@ -3462,31 +3461,8 @@ static void cfq_exit_queue(struct elevator_queue *e) spin_unlock_irq(q->queue_lock); -#ifdef CONFIG_BLK_CGROUP - /* - * If there are groups which we could not unlink from blkcg list, - * wait for a rcu period for them to be freed. - */ - spin_lock_irq(q->queue_lock); - wait = q->nr_blkgs; - spin_unlock_irq(q->queue_lock); -#endif cfq_shutdown_timer_wq(cfqd); - /* - * Wait for cfqg->blkg->key accessors to exit their grace periods. - * Do this wait only if there are other unlinked groups out - * there. This can happen if cgroup deletion path claimed the - * responsibility of cleaning up a group before queue cleanup code - * get to the group. - * - * Do not call synchronize_rcu() unconditionally as there are drivers - * which create/delete request queue hundreds of times during scan/boot - * and synchronize_rcu() can take significant time and slow down boot. - */ - if (wait) - synchronize_rcu(); - #ifndef CONFIG_CFQ_GROUP_IOSCHED kfree(cfqd->root_group); #endif diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index b4d1d4bfc168..33f1b29e53f4 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -365,7 +365,6 @@ struct request_queue { #ifdef CONFIG_BLK_CGROUP /* XXX: array size hardcoded to avoid include dependency (temporary) */ struct list_head blkg_list; - int nr_blkgs; #endif struct queue_limits limits; -- cgit v1.2.3 From 3d48749d93a3dce732dd30a14002ab90ec4355f3 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 5 Mar 2012 13:15:25 -0800 Subject: block: ioc_task_link() can't fail ioc_task_link() is used to share %current's ioc on clone. If %current->io_context is set, %current is guaranteed to have refcount on the ioc and, thus, ioc_task_link() can't fail. Replace error checking in ioc_task_link() with WARN_ON_ONCE() and make it just increment refcount and nr_tasks. -v2: Description typo fix (Vivek). Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- include/linux/iocontext.h | 16 +++++----------- kernel/fork.c | 5 ++--- 2 files changed, 7 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index 1a3018063034..81a8870ac224 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -120,18 +120,12 @@ struct io_context { struct work_struct release_work; }; -static inline struct io_context *ioc_task_link(struct io_context *ioc) +static inline void ioc_task_link(struct io_context *ioc) { - /* - * if ref count is zero, don't allow sharing (ioc is going away, it's - * a race). - */ - if (ioc && atomic_long_inc_not_zero(&ioc->refcount)) { - atomic_inc(&ioc->nr_tasks); - return ioc; - } - - return NULL; + WARN_ON_ONCE(atomic_long_read(&ioc->refcount) <= 0); + WARN_ON_ONCE(atomic_read(&ioc->nr_tasks) <= 0); + atomic_long_inc(&ioc->refcount); + atomic_inc(&ioc->nr_tasks); } struct task_struct; diff --git a/kernel/fork.c b/kernel/fork.c index b77fd559c78e..a1b632713e43 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -901,9 +901,8 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk) * Share io context with parent, if CLONE_IO is set */ if (clone_flags & CLONE_IO) { - tsk->io_context = ioc_task_link(ioc); - if (unlikely(!tsk->io_context)) - return -ENOMEM; + ioc_task_link(ioc); + tsk->io_context = ioc; } else if (ioprio_valid(ioc->ioprio)) { new_ioc = get_task_io_context(tsk, GFP_KERNEL, NUMA_NO_NODE); if (unlikely(!new_ioc)) -- cgit v1.2.3 From f6e8d01bee036460e03bd4f6a79d014f98ba712e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 5 Mar 2012 13:15:26 -0800 Subject: block: add io_context->active_ref Currently ioc->nr_tasks is used to decide two things - whether an ioc is done issuing IOs and whether it's shared by multiple tasks. This patch separate out the first into ioc->active_ref, which is acquired and released using {get|put}_io_context_active() respectively. This will be used to associate bio's with a given task. This patch doesn't introduce any visible behavior change. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-ioc.c | 36 +++++++++++++++++++++++++----------- block/cfq-iosched.c | 4 ++-- include/linux/iocontext.h | 22 ++++++++++++++++++++-- 3 files changed, 47 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/block/blk-ioc.c b/block/blk-ioc.c index 10928740b5da..439ec21fd787 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -149,20 +149,20 @@ void put_io_context(struct io_context *ioc) } EXPORT_SYMBOL(put_io_context); -/* Called by the exiting task */ -void exit_io_context(struct task_struct *task) +/** + * put_io_context_active - put active reference on ioc + * @ioc: ioc of interest + * + * Undo get_io_context_active(). If active reference reaches zero after + * put, @ioc can never issue further IOs and ioscheds are notified. + */ +void put_io_context_active(struct io_context *ioc) { - struct io_context *ioc; - struct io_cq *icq; struct hlist_node *n; unsigned long flags; + struct io_cq *icq; - task_lock(task); - ioc = task->io_context; - task->io_context = NULL; - task_unlock(task); - - if (!atomic_dec_and_test(&ioc->nr_tasks)) { + if (!atomic_dec_and_test(&ioc->active_ref)) { put_io_context(ioc); return; } @@ -191,6 +191,20 @@ retry: put_io_context(ioc); } +/* Called by the exiting task */ +void exit_io_context(struct task_struct *task) +{ + struct io_context *ioc; + + task_lock(task); + ioc = task->io_context; + task->io_context = NULL; + task_unlock(task); + + atomic_dec(&ioc->nr_tasks); + put_io_context_active(ioc); +} + /** * ioc_clear_queue - break any ioc association with the specified queue * @q: request_queue being cleared @@ -223,7 +237,7 @@ int create_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node) /* initialize */ atomic_long_set(&ioc->refcount, 1); - atomic_set(&ioc->nr_tasks, 1); + atomic_set(&ioc->active_ref, 1); spin_lock_init(&ioc->lock); INIT_RADIX_TREE(&ioc->icq_tree, GFP_ATOMIC | __GFP_HIGH); INIT_HLIST_HEAD(&ioc->icq_list); diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 9e386d9bcb79..9a4eac490e0b 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1865,7 +1865,7 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd) * task has exited, don't wait */ cic = cfqd->active_cic; - if (!cic || !atomic_read(&cic->icq.ioc->nr_tasks)) + if (!cic || !atomic_read(&cic->icq.ioc->active_ref)) return; /* @@ -2841,7 +2841,7 @@ cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq, if (cfqq->next_rq && (cfqq->next_rq->cmd_flags & REQ_NOIDLE)) enable_idle = 0; - else if (!atomic_read(&cic->icq.ioc->nr_tasks) || + else if (!atomic_read(&cic->icq.ioc->active_ref) || !cfqd->cfq_slice_idle || (!cfq_cfqq_deep(cfqq) && CFQQ_SEEKY(cfqq))) enable_idle = 0; diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index 81a8870ac224..6f1a2608e91f 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -100,6 +100,7 @@ struct io_cq { */ struct io_context { atomic_long_t refcount; + atomic_t active_ref; atomic_t nr_tasks; /* all the fields below are protected by this lock */ @@ -120,17 +121,34 @@ struct io_context { struct work_struct release_work; }; -static inline void ioc_task_link(struct io_context *ioc) +/** + * get_io_context_active - get active reference on ioc + * @ioc: ioc of interest + * + * Only iocs with active reference can issue new IOs. This function + * acquires an active reference on @ioc. The caller must already have an + * active reference on @ioc. + */ +static inline void get_io_context_active(struct io_context *ioc) { WARN_ON_ONCE(atomic_long_read(&ioc->refcount) <= 0); - WARN_ON_ONCE(atomic_read(&ioc->nr_tasks) <= 0); + WARN_ON_ONCE(atomic_read(&ioc->active_ref) <= 0); atomic_long_inc(&ioc->refcount); + atomic_inc(&ioc->active_ref); +} + +static inline void ioc_task_link(struct io_context *ioc) +{ + get_io_context_active(ioc); + + WARN_ON_ONCE(atomic_read(&ioc->nr_tasks) <= 0); atomic_inc(&ioc->nr_tasks); } struct task_struct; #ifdef CONFIG_BLOCK void put_io_context(struct io_context *ioc); +void put_io_context_active(struct io_context *ioc); void exit_io_context(struct task_struct *task); struct io_context *get_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node); -- cgit v1.2.3 From 852c788f8365062c8a383c5a93f7f7289977cb50 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 5 Mar 2012 13:15:27 -0800 Subject: block: implement bio_associate_current() IO scheduling and cgroup are tied to the issuing task via io_context and cgroup of %current. Unfortunately, there are cases where IOs need to be routed via a different task which makes scheduling and cgroup limit enforcement applied completely incorrectly. For example, all bios delayed by blk-throttle end up being issued by a delayed work item and get assigned the io_context of the worker task which happens to serve the work item and dumped to the default block cgroup. This is double confusing as bios which aren't delayed end up in the correct cgroup and makes using blk-throttle and cfq propio together impossible. Any code which punts IO issuing to another task is affected which is getting more and more common (e.g. btrfs). As both io_context and cgroup are firmly tied to task including userland visible APIs to manipulate them, it makes a lot of sense to match up tasks to bios. This patch implements bio_associate_current() which associates the specified bio with %current. The bio will record the associated ioc and blkcg at that point and block layer will use the recorded ones regardless of which task actually ends up issuing the bio. bio release puts the associated ioc and blkcg. It grabs and remembers ioc and blkcg instead of the task itself because task may already be dead by the time the bio is issued making ioc and blkcg inaccessible and those are all block layer cares about. elevator_set_req_fn() is updated such that the bio elvdata is being allocated for is available to the elevator. This doesn't update block cgroup policies yet. Further patches will implement the support. -v2: #ifdef CONFIG_BLK_CGROUP added around bio->bi_ioc dereference in rq_ioc() to fix build breakage. Signed-off-by: Tejun Heo Cc: Vivek Goyal Cc: Kent Overstreet Signed-off-by: Jens Axboe --- block/blk-core.c | 32 +++++++++++++++++++------ block/cfq-iosched.c | 3 ++- block/elevator.c | 5 ++-- fs/bio.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/bio.h | 8 +++++++ include/linux/blk_types.h | 10 ++++++++ include/linux/elevator.h | 6 +++-- 7 files changed, 113 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/block/blk-core.c b/block/blk-core.c index b2d0fcd8f87f..991c1d6ef245 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -696,7 +696,7 @@ static inline void blk_free_request(struct request_queue *q, struct request *rq) } static struct request * -blk_alloc_request(struct request_queue *q, struct io_cq *icq, +blk_alloc_request(struct request_queue *q, struct bio *bio, struct io_cq *icq, unsigned int flags, gfp_t gfp_mask) { struct request *rq = mempool_alloc(q->rq.rq_pool, gfp_mask); @@ -710,7 +710,7 @@ blk_alloc_request(struct request_queue *q, struct io_cq *icq, if (flags & REQ_ELVPRIV) { rq->elv.icq = icq; - if (unlikely(elv_set_request(q, rq, gfp_mask))) { + if (unlikely(elv_set_request(q, rq, bio, gfp_mask))) { mempool_free(rq, q->rq.rq_pool); return NULL; } @@ -809,6 +809,22 @@ static bool blk_rq_should_init_elevator(struct bio *bio) return true; } +/** + * rq_ioc - determine io_context for request allocation + * @bio: request being allocated is for this bio (can be %NULL) + * + * Determine io_context to use for request allocation for @bio. May return + * %NULL if %current->io_context doesn't exist. + */ +static struct io_context *rq_ioc(struct bio *bio) +{ +#ifdef CONFIG_BLK_CGROUP + if (bio && bio->bi_ioc) + return bio->bi_ioc; +#endif + return current->io_context; +} + /** * get_request - get a free request * @q: request_queue to allocate request from @@ -836,7 +852,7 @@ static struct request *get_request(struct request_queue *q, int rw_flags, int may_queue; retry: et = q->elevator->type; - ioc = current->io_context; + ioc = rq_ioc(bio); if (unlikely(blk_queue_dead(q))) return NULL; @@ -919,14 +935,16 @@ retry: /* create icq if missing */ if ((rw_flags & REQ_ELVPRIV) && unlikely(et->icq_cache && !icq)) { - ioc = create_io_context(gfp_mask, q->node); - if (ioc) - icq = ioc_create_icq(ioc, q, gfp_mask); + create_io_context(gfp_mask, q->node); + ioc = rq_ioc(bio); + if (!ioc) + goto fail_alloc; + icq = ioc_create_icq(ioc, q, gfp_mask); if (!icq) goto fail_alloc; } - rq = blk_alloc_request(q, icq, rw_flags, gfp_mask); + rq = blk_alloc_request(q, bio, icq, rw_flags, gfp_mask); if (unlikely(!rq)) goto fail_alloc; diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 9a4eac490e0b..abac87337d70 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3299,7 +3299,8 @@ split_cfqq(struct cfq_io_cq *cic, struct cfq_queue *cfqq) * Allocate cfq data structures associated with this request. */ static int -cfq_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask) +cfq_set_request(struct request_queue *q, struct request *rq, struct bio *bio, + gfp_t gfp_mask) { struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_io_cq *cic = icq_to_cic(rq->elv.icq); diff --git a/block/elevator.c b/block/elevator.c index 451654fadab0..be3ab6df0fea 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -663,12 +663,13 @@ struct request *elv_former_request(struct request_queue *q, struct request *rq) return NULL; } -int elv_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask) +int elv_set_request(struct request_queue *q, struct request *rq, + struct bio *bio, gfp_t gfp_mask) { struct elevator_queue *e = q->elevator; if (e->type->ops.elevator_set_req_fn) - return e->type->ops.elevator_set_req_fn(q, rq, gfp_mask); + return e->type->ops.elevator_set_req_fn(q, rq, bio, gfp_mask); return 0; } diff --git a/fs/bio.c b/fs/bio.c index b980ecde026a..142214b80039 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -19,12 +19,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include /* for struct sg_iovec */ #include @@ -418,6 +420,7 @@ void bio_put(struct bio *bio) * last put frees it */ if (atomic_dec_and_test(&bio->bi_cnt)) { + bio_disassociate_task(bio); bio->bi_next = NULL; bio->bi_destructor(bio); } @@ -1641,6 +1644,64 @@ bad: } EXPORT_SYMBOL(bioset_create); +#ifdef CONFIG_BLK_CGROUP +/** + * bio_associate_current - associate a bio with %current + * @bio: target bio + * + * Associate @bio with %current if it hasn't been associated yet. Block + * layer will treat @bio as if it were issued by %current no matter which + * task actually issues it. + * + * This function takes an extra reference of @task's io_context and blkcg + * which will be put when @bio is released. The caller must own @bio, + * ensure %current->io_context exists, and is responsible for synchronizing + * calls to this function. + */ +int bio_associate_current(struct bio *bio) +{ + struct io_context *ioc; + struct cgroup_subsys_state *css; + + if (bio->bi_ioc) + return -EBUSY; + + ioc = current->io_context; + if (!ioc) + return -ENOENT; + + /* acquire active ref on @ioc and associate */ + get_io_context_active(ioc); + bio->bi_ioc = ioc; + + /* associate blkcg if exists */ + rcu_read_lock(); + css = task_subsys_state(current, blkio_subsys_id); + if (css && css_tryget(css)) + bio->bi_css = css; + rcu_read_unlock(); + + return 0; +} + +/** + * bio_disassociate_task - undo bio_associate_current() + * @bio: target bio + */ +void bio_disassociate_task(struct bio *bio) +{ + if (bio->bi_ioc) { + put_io_context(bio->bi_ioc); + bio->bi_ioc = NULL; + } + if (bio->bi_css) { + css_put(bio->bi_css); + bio->bi_css = NULL; + } +} + +#endif /* CONFIG_BLK_CGROUP */ + static void __init biovec_init_slabs(void) { int i; diff --git a/include/linux/bio.h b/include/linux/bio.h index 129a9c097958..692d3d5b49f5 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -268,6 +268,14 @@ extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set extern void bvec_free_bs(struct bio_set *, struct bio_vec *, unsigned int); extern unsigned int bvec_nr_vecs(unsigned short idx); +#ifdef CONFIG_BLK_CGROUP +int bio_associate_current(struct bio *bio); +void bio_disassociate_task(struct bio *bio); +#else /* CONFIG_BLK_CGROUP */ +static inline int bio_associate_current(struct bio *bio) { return -ENOENT; } +static inline void bio_disassociate_task(struct bio *bio) { } +#endif /* CONFIG_BLK_CGROUP */ + /* * bio_set is used to allow other portions of the IO system to * allocate their own private memory pools for bio and iovec structures. diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 4053cbd4490e..0edb65dd8edd 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -14,6 +14,8 @@ struct bio; struct bio_integrity_payload; struct page; struct block_device; +struct io_context; +struct cgroup_subsys_state; typedef void (bio_end_io_t) (struct bio *, int); typedef void (bio_destructor_t) (struct bio *); @@ -66,6 +68,14 @@ struct bio { bio_end_io_t *bi_end_io; void *bi_private; +#ifdef CONFIG_BLK_CGROUP + /* + * Optional ioc and css associated with this bio. Put on bio + * release. Read comment on top of bio_associate_current(). + */ + struct io_context *bi_ioc; + struct cgroup_subsys_state *bi_css; +#endif #if defined(CONFIG_BLK_DEV_INTEGRITY) struct bio_integrity_payload *bi_integrity; /* data integrity */ #endif diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 97fb2557a18c..c03af7687bb4 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -28,7 +28,8 @@ typedef int (elevator_may_queue_fn) (struct request_queue *, int); typedef void (elevator_init_icq_fn) (struct io_cq *); typedef void (elevator_exit_icq_fn) (struct io_cq *); -typedef int (elevator_set_req_fn) (struct request_queue *, struct request *, gfp_t); +typedef int (elevator_set_req_fn) (struct request_queue *, struct request *, + struct bio *, gfp_t); typedef void (elevator_put_req_fn) (struct request *); typedef void (elevator_activate_req_fn) (struct request_queue *, struct request *); typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct request *); @@ -129,7 +130,8 @@ extern void elv_unregister_queue(struct request_queue *q); extern int elv_may_queue(struct request_queue *, int); extern void elv_abort_queue(struct request_queue *); extern void elv_completed_request(struct request_queue *, struct request *); -extern int elv_set_request(struct request_queue *, struct request *, gfp_t); +extern int elv_set_request(struct request_queue *q, struct request *rq, + struct bio *bio, gfp_t gfp_mask); extern void elv_put_request(struct request_queue *, struct request *); extern void elv_drain_elevator(struct request_queue *); -- cgit v1.2.3 From 900771a483ef28915a48066d7895d8252315607a Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Mon, 12 Mar 2012 14:55:14 +0530 Subject: uprobes/core: Make macro names consistent Rename macros that refer to individual uprobe to start with UPROBE_ instead of UPROBES_. This is pure cleanup, no functional change intended. Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120312092514.5379.36595.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uprobes.h | 6 +++--- arch/x86/kernel/uprobes.c | 18 +++++++++--------- include/linux/uprobes.h | 4 ++-- kernel/events/uprobes.c | 18 +++++++++--------- 4 files changed, 23 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index f7ce310a429d..5c399e446512 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -26,10 +26,10 @@ typedef u8 uprobe_opcode_t; #define MAX_UINSN_BYTES 16 -#define UPROBES_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ +#define UPROBE_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ -#define UPROBES_BKPT_INSN 0xcc -#define UPROBES_BKPT_INSN_SIZE 1 +#define UPROBE_BKPT_INSN 0xcc +#define UPROBE_BKPT_INSN_SIZE 1 struct arch_uprobe { u16 fixups; diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 04dfcef2d028..6dfa89e6f24a 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -31,14 +31,14 @@ /* Post-execution fixups. */ /* No fixup needed */ -#define UPROBES_FIX_NONE 0x0 +#define UPROBE_FIX_NONE 0x0 /* Adjust IP back to vicinity of actual insn */ -#define UPROBES_FIX_IP 0x1 +#define UPROBE_FIX_IP 0x1 /* Adjust the return address of a call insn */ -#define UPROBES_FIX_CALL 0x2 +#define UPROBE_FIX_CALL 0x2 -#define UPROBES_FIX_RIP_AX 0x8000 -#define UPROBES_FIX_RIP_CX 0x4000 +#define UPROBE_FIX_RIP_AX 0x8000 +#define UPROBE_FIX_RIP_CX 0x4000 /* Adaptations for mhiramat x86 decoder v14. */ #define OPCODE1(insn) ((insn)->opcode.bytes[0]) @@ -269,9 +269,9 @@ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) break; } if (fix_ip) - auprobe->fixups |= UPROBES_FIX_IP; + auprobe->fixups |= UPROBE_FIX_IP; if (fix_call) - auprobe->fixups |= UPROBES_FIX_CALL; + auprobe->fixups |= UPROBE_FIX_CALL; } #ifdef CONFIG_X86_64 @@ -341,12 +341,12 @@ static void handle_riprel_insn(struct mm_struct *mm, struct arch_uprobe *auprobe * is NOT the register operand, so we use %rcx (register * #1) for the scratch register. */ - auprobe->fixups = UPROBES_FIX_RIP_CX; + auprobe->fixups = UPROBE_FIX_RIP_CX; /* Change modrm from 00 000 101 to 00 000 001. */ *cursor = 0x1; } else { /* Use %rax (register #0) for the scratch register. */ - auprobe->fixups = UPROBES_FIX_RIP_AX; + auprobe->fixups = UPROBE_FIX_RIP_AX; /* Change modrm from 00 xxx 101 to 00 xxx 000 */ *cursor = (reg << 3); } diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index f85797e1ccd4..838fb312926a 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -35,10 +35,10 @@ struct vm_area_struct; /* flags that denote/change uprobes behaviour */ /* Have a copy of original instruction */ -#define UPROBES_COPY_INSN 0x1 +#define UPROBE_COPY_INSN 0x1 /* Dont run handlers when first register/ last unregister in progress*/ -#define UPROBES_RUN_HANDLER 0x2 +#define UPROBE_RUN_HANDLER 0x2 struct uprobe_consumer { int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 5ce32e3ae9e9..0d36bf3920ba 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -177,7 +177,7 @@ out: */ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) { - return *insn == UPROBES_BKPT_INSN; + return *insn == UPROBE_BKPT_INSN; } /* @@ -259,8 +259,8 @@ static int write_opcode(struct mm_struct *mm, struct arch_uprobe *auprobe, /* poke the new insn in, ASSUMES we don't cross page boundary */ vaddr &= ~PAGE_MASK; - BUG_ON(vaddr + UPROBES_BKPT_INSN_SIZE > PAGE_SIZE); - memcpy(vaddr_new + vaddr, &opcode, UPROBES_BKPT_INSN_SIZE); + BUG_ON(vaddr + UPROBE_BKPT_INSN_SIZE > PAGE_SIZE); + memcpy(vaddr_new + vaddr, &opcode, UPROBE_BKPT_INSN_SIZE); kunmap_atomic(vaddr_new); kunmap_atomic(vaddr_old); @@ -308,7 +308,7 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_ lock_page(page); vaddr_new = kmap_atomic(page); vaddr &= ~PAGE_MASK; - memcpy(opcode, vaddr_new + vaddr, UPROBES_BKPT_INSN_SIZE); + memcpy(opcode, vaddr_new + vaddr, UPROBE_BKPT_INSN_SIZE); kunmap_atomic(vaddr_new); unlock_page(page); @@ -352,7 +352,7 @@ int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned if (result) return result; - return write_opcode(mm, auprobe, vaddr, UPROBES_BKPT_INSN); + return write_opcode(mm, auprobe, vaddr, UPROBE_BKPT_INSN); } /** @@ -635,7 +635,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, addr = (unsigned long)vaddr; - if (!(uprobe->flags & UPROBES_COPY_INSN)) { + if (!(uprobe->flags & UPROBE_COPY_INSN)) { ret = copy_insn(uprobe, vma, addr); if (ret) return ret; @@ -647,7 +647,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, if (ret) return ret; - uprobe->flags |= UPROBES_COPY_INSN; + uprobe->flags |= UPROBE_COPY_INSN; } ret = set_bkpt(mm, &uprobe->arch, addr); @@ -857,7 +857,7 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer * uprobe->consumers = NULL; __uprobe_unregister(uprobe); } else { - uprobe->flags |= UPROBES_RUN_HANDLER; + uprobe->flags |= UPROBE_RUN_HANDLER; } } @@ -889,7 +889,7 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume if (consumer_del(uprobe, consumer)) { if (!uprobe->consumers) { __uprobe_unregister(uprobe); - uprobe->flags &= ~UPROBES_RUN_HANDLER; + uprobe->flags &= ~UPROBE_RUN_HANDLER; } } -- cgit v1.2.3 From e3343e6a2819ff5d0dfc4bb5c9fb7f9a4d04da73 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Mon, 12 Mar 2012 14:55:30 +0530 Subject: uprobes/core: Make order of function parameters consistent across functions If a function takes struct uprobe or struct arch_uprobe, then it is passed as the first parameter. This is pure cleanup, no functional change intended. Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120312092530.5379.18394.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uprobes.h | 2 +- arch/x86/kernel/uprobes.c | 15 +++---- include/linux/uprobes.h | 12 +++--- kernel/events/uprobes.c | 93 ++++++++++++++++++++++-------------------- 4 files changed, 63 insertions(+), 59 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 5c399e446512..384f1bebf884 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -39,5 +39,5 @@ struct arch_uprobe { #endif }; -extern int arch_uprobes_analyze_insn(struct mm_struct *mm, struct arch_uprobe *arch_uprobe); +extern int arch_uprobes_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm); #endif /* _ASM_UPROBES_H */ diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 6dfa89e6f24a..851a11b0d38c 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -297,7 +297,8 @@ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) * - There's never a SIB byte. * - The displacement is always 4 bytes. */ -static void handle_riprel_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) +static void +handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) { u8 *cursor; u8 reg; @@ -381,19 +382,19 @@ static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn) return -ENOTSUPP; } -static int validate_insn_bits(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) +static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) { if (mm->context.ia32_compat) return validate_insn_32bits(auprobe, insn); return validate_insn_64bits(auprobe, insn); } #else /* 32-bit: */ -static void handle_riprel_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) +static void handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) { /* No RIP-relative addressing on 32-bit */ } -static int validate_insn_bits(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) +static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) { return validate_insn_32bits(auprobe, insn); } @@ -405,17 +406,17 @@ static int validate_insn_bits(struct mm_struct *mm, struct arch_uprobe *auprobe, * @arch_uprobe: the probepoint information. * Return 0 on success or a -ve number on error. */ -int arch_uprobes_analyze_insn(struct mm_struct *mm, struct arch_uprobe *auprobe) +int arch_uprobes_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm) { int ret; struct insn insn; auprobe->fixups = 0; - ret = validate_insn_bits(mm, auprobe, &insn); + ret = validate_insn_bits(auprobe, mm, &insn); if (ret != 0) return ret; - handle_riprel_insn(mm, auprobe, &insn); + handle_riprel_insn(auprobe, mm, &insn); prepare_fixups(auprobe, &insn); return 0; diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 838fb312926a..58699182e9a7 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -52,20 +52,20 @@ struct uprobe_consumer { }; #ifdef CONFIG_UPROBES -extern int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr); -extern int __weak set_orig_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr, bool verify); +extern int __weak set_bkpt(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); +extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); extern bool __weak is_bkpt_insn(uprobe_opcode_t *insn); -extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); -extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); +extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); +extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_mmap(struct vm_area_struct *vma); #else /* CONFIG_UPROBES is not defined */ static inline int -uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) +uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { return -ENOSYS; } static inline void -uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) +uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { } static inline int uprobe_mmap(struct vm_area_struct *vma) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 0d36bf3920ba..9c5ddff1c8da 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -192,8 +192,8 @@ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) /* * write_opcode - write the opcode at a given virtual address. + * @auprobe: arch breakpointing information. * @mm: the probed process address space. - * @arch_uprobe: the breakpointing information. * @vaddr: the virtual address to store the opcode. * @opcode: opcode to be written at @vaddr. * @@ -203,7 +203,7 @@ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) * For mm @mm, write the opcode at @vaddr. * Return 0 (success) or a negative errno. */ -static int write_opcode(struct mm_struct *mm, struct arch_uprobe *auprobe, +static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t opcode) { struct page *old_page, *new_page; @@ -334,14 +334,14 @@ static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) /** * set_bkpt - store breakpoint at a given address. + * @auprobe: arch specific probepoint information. * @mm: the probed process address space. - * @uprobe: the probepoint information. * @vaddr: the virtual address to insert the opcode. * * For mm @mm, store the breakpoint instruction at @vaddr. * Return 0 (success) or a negative errno. */ -int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr) +int __weak set_bkpt(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) { int result; @@ -352,13 +352,13 @@ int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned if (result) return result; - return write_opcode(mm, auprobe, vaddr, UPROBE_BKPT_INSN); + return write_opcode(auprobe, mm, vaddr, UPROBE_BKPT_INSN); } /** * set_orig_insn - Restore the original instruction. * @mm: the probed process address space. - * @uprobe: the probepoint information. + * @auprobe: arch specific probepoint information. * @vaddr: the virtual address to insert the opcode. * @verify: if true, verify existance of breakpoint instruction. * @@ -366,7 +366,7 @@ int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned * Return 0 (success) or a negative errno. */ int __weak -set_orig_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr, bool verify) +set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, bool verify) { if (verify) { int result; @@ -378,7 +378,7 @@ set_orig_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long v if (result != 1) return result; } - return write_opcode(mm, auprobe, vaddr, *(uprobe_opcode_t *)auprobe->insn); + return write_opcode(auprobe, mm, vaddr, *(uprobe_opcode_t *)auprobe->insn); } static int match_uprobe(struct uprobe *l, struct uprobe *r) @@ -525,30 +525,30 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) /* Returns the previous consumer */ static struct uprobe_consumer * -consumer_add(struct uprobe *uprobe, struct uprobe_consumer *consumer) +consumer_add(struct uprobe *uprobe, struct uprobe_consumer *uc) { down_write(&uprobe->consumer_rwsem); - consumer->next = uprobe->consumers; - uprobe->consumers = consumer; + uc->next = uprobe->consumers; + uprobe->consumers = uc; up_write(&uprobe->consumer_rwsem); - return consumer->next; + return uc->next; } /* - * For uprobe @uprobe, delete the consumer @consumer. - * Return true if the @consumer is deleted successfully + * For uprobe @uprobe, delete the consumer @uc. + * Return true if the @uc is deleted successfully * or return false. */ -static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *consumer) +static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc) { struct uprobe_consumer **con; bool ret = false; down_write(&uprobe->consumer_rwsem); for (con = &uprobe->consumers; *con; con = &(*con)->next) { - if (*con == consumer) { - *con = consumer->next; + if (*con == uc) { + *con = uc->next; ret = true; break; } @@ -558,8 +558,8 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *consumer return ret; } -static int __copy_insn(struct address_space *mapping, - struct vm_area_struct *vma, char *insn, +static int +__copy_insn(struct address_space *mapping, struct vm_area_struct *vma, char *insn, unsigned long nbytes, unsigned long offset) { struct file *filp = vma->vm_file; @@ -590,7 +590,8 @@ static int __copy_insn(struct address_space *mapping, return 0; } -static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr) +static int +copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr) { struct address_space *mapping; unsigned long nbytes; @@ -617,8 +618,9 @@ static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned return __copy_insn(mapping, vma, uprobe->arch.insn, bytes, uprobe->offset); } -static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, - struct vm_area_struct *vma, loff_t vaddr) +static int +install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, + struct vm_area_struct *vma, loff_t vaddr) { unsigned long addr; int ret; @@ -643,20 +645,21 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, if (is_bkpt_insn((uprobe_opcode_t *)uprobe->arch.insn)) return -EEXIST; - ret = arch_uprobes_analyze_insn(mm, &uprobe->arch); + ret = arch_uprobes_analyze_insn(&uprobe->arch, mm); if (ret) return ret; uprobe->flags |= UPROBE_COPY_INSN; } - ret = set_bkpt(mm, &uprobe->arch, addr); + ret = set_bkpt(&uprobe->arch, mm, addr); return ret; } -static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, loff_t vaddr) +static void +remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, loff_t vaddr) { - set_orig_insn(mm, &uprobe->arch, (unsigned long)vaddr, true); + set_orig_insn(&uprobe->arch, mm, (unsigned long)vaddr, true); } static void delete_uprobe(struct uprobe *uprobe) @@ -671,9 +674,9 @@ static void delete_uprobe(struct uprobe *uprobe) atomic_dec(&uprobe_events); } -static struct vma_info *__find_next_vma_info(struct list_head *head, - loff_t offset, struct address_space *mapping, - struct vma_info *vi, bool is_register) +static struct vma_info * +__find_next_vma_info(struct address_space *mapping, struct list_head *head, + struct vma_info *vi, loff_t offset, bool is_register) { struct prio_tree_iter iter; struct vm_area_struct *vma; @@ -719,8 +722,8 @@ static struct vma_info *__find_next_vma_info(struct list_head *head, * yet been inserted. */ static struct vma_info * -find_next_vma_info(struct list_head *head, loff_t offset, struct address_space *mapping, - bool is_register) +find_next_vma_info(struct address_space *mapping, struct list_head *head, + loff_t offset, bool is_register) { struct vma_info *vi, *retvi; @@ -729,7 +732,7 @@ find_next_vma_info(struct list_head *head, loff_t offset, struct address_space * return ERR_PTR(-ENOMEM); mutex_lock(&mapping->i_mmap_mutex); - retvi = __find_next_vma_info(head, offset, mapping, vi, is_register); + retvi = __find_next_vma_info(mapping, head, vi, offset, is_register); mutex_unlock(&mapping->i_mmap_mutex); if (!retvi) @@ -754,7 +757,7 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) ret = 0; for (;;) { - vi = find_next_vma_info(&try_list, uprobe->offset, mapping, is_register); + vi = find_next_vma_info(mapping, &try_list, uprobe->offset, is_register); if (!vi) break; @@ -784,9 +787,9 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) } if (is_register) - ret = install_breakpoint(mm, uprobe, vma, vi->vaddr); + ret = install_breakpoint(uprobe, mm, vma, vi->vaddr); else - remove_breakpoint(mm, uprobe, vi->vaddr); + remove_breakpoint(uprobe, mm, vi->vaddr); up_read(&mm->mmap_sem); mmput(mm); @@ -823,25 +826,25 @@ static void __uprobe_unregister(struct uprobe *uprobe) * uprobe_register - register a probe * @inode: the file in which the probe has to be placed. * @offset: offset from the start of the file. - * @consumer: information on howto handle the probe.. + * @uc: information on howto handle the probe.. * * Apart from the access refcount, uprobe_register() takes a creation * refcount (thro alloc_uprobe) if and only if this @uprobe is getting * inserted into the rbtree (i.e first consumer for a @inode:@offset * tuple). Creation refcount stops uprobe_unregister from freeing the * @uprobe even before the register operation is complete. Creation - * refcount is released when the last @consumer for the @uprobe + * refcount is released when the last @uc for the @uprobe * unregisters. * * Return errno if it cannot successully install probes * else return 0 (success) */ -int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) +int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { struct uprobe *uprobe; int ret; - if (!inode || !consumer || consumer->next) + if (!inode || !uc || uc->next) return -EINVAL; if (offset > i_size_read(inode)) @@ -851,7 +854,7 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer * mutex_lock(uprobes_hash(inode)); uprobe = alloc_uprobe(inode, offset); - if (uprobe && !consumer_add(uprobe, consumer)) { + if (uprobe && !consumer_add(uprobe, uc)) { ret = __uprobe_register(uprobe); if (ret) { uprobe->consumers = NULL; @@ -871,13 +874,13 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer * * uprobe_unregister - unregister a already registered probe. * @inode: the file in which the probe has to be removed. * @offset: offset from the start of the file. - * @consumer: identify which probe if multiple probes are colocated. + * @uc: identify which probe if multiple probes are colocated. */ -void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) +void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { struct uprobe *uprobe; - if (!inode || !consumer) + if (!inode || !uc) return; uprobe = find_uprobe(inode, offset); @@ -886,7 +889,7 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume mutex_lock(uprobes_hash(inode)); - if (consumer_del(uprobe, consumer)) { + if (consumer_del(uprobe, uc)) { if (!uprobe->consumers) { __uprobe_unregister(uprobe); uprobe->flags &= ~UPROBE_RUN_HANDLER; @@ -993,7 +996,7 @@ int uprobe_mmap(struct vm_area_struct *vma) if (!ret) { vaddr = vma_address(vma, uprobe->offset); if (vaddr >= vma->vm_start && vaddr < vma->vm_end) { - ret = install_breakpoint(vma->vm_mm, uprobe, vma, vaddr); + ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); /* Ignore double add: */ if (ret == -EEXIST) ret = 0; -- cgit v1.2.3 From 5cb4ac3a583d4ee18c8682ab857e093c4a0d0895 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Mon, 12 Mar 2012 14:55:45 +0530 Subject: uprobes/core: Rename bkpt to swbp bkpt doesnt seem to be a correct abbrevation for breakpoint. Choice was between bp and breakpoint. Since bp can refer to things other than breakpoint, use swbp to refer to breakpoints. This is pure cleanup, no functional change intended. Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120312092545.5379.91251.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uprobes.h | 4 ++-- include/linux/uprobes.h | 4 ++-- kernel/events/uprobes.c | 34 +++++++++++++++++----------------- 3 files changed, 21 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 384f1bebf884..0500391f57d0 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -28,8 +28,8 @@ typedef u8 uprobe_opcode_t; #define MAX_UINSN_BYTES 16 #define UPROBE_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ -#define UPROBE_BKPT_INSN 0xcc -#define UPROBE_BKPT_INSN_SIZE 1 +#define UPROBE_SWBP_INSN 0xcc +#define UPROBE_SWBP_INSN_SIZE 1 struct arch_uprobe { u16 fixups; diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 58699182e9a7..eac525f41b94 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -52,9 +52,9 @@ struct uprobe_consumer { }; #ifdef CONFIG_UPROBES -extern int __weak set_bkpt(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); +extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); -extern bool __weak is_bkpt_insn(uprobe_opcode_t *insn); +extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_mmap(struct vm_area_struct *vma); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 9c5ddff1c8da..e56e56aa7535 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -170,14 +170,14 @@ out: } /** - * is_bkpt_insn - check if instruction is breakpoint instruction. + * is_swbp_insn - check if instruction is breakpoint instruction. * @insn: instruction to be checked. - * Default implementation of is_bkpt_insn + * Default implementation of is_swbp_insn * Returns true if @insn is a breakpoint instruction. */ -bool __weak is_bkpt_insn(uprobe_opcode_t *insn) +bool __weak is_swbp_insn(uprobe_opcode_t *insn) { - return *insn == UPROBE_BKPT_INSN; + return *insn == UPROBE_SWBP_INSN; } /* @@ -227,7 +227,7 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, * adding probes in write mapped pages since the breakpoints * might end up in the file copy. */ - if (!valid_vma(vma, is_bkpt_insn(&opcode))) + if (!valid_vma(vma, is_swbp_insn(&opcode))) goto put_out; uprobe = container_of(auprobe, struct uprobe, arch); @@ -259,8 +259,8 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, /* poke the new insn in, ASSUMES we don't cross page boundary */ vaddr &= ~PAGE_MASK; - BUG_ON(vaddr + UPROBE_BKPT_INSN_SIZE > PAGE_SIZE); - memcpy(vaddr_new + vaddr, &opcode, UPROBE_BKPT_INSN_SIZE); + BUG_ON(vaddr + UPROBE_SWBP_INSN_SIZE > PAGE_SIZE); + memcpy(vaddr_new + vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); kunmap_atomic(vaddr_new); kunmap_atomic(vaddr_old); @@ -308,7 +308,7 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_ lock_page(page); vaddr_new = kmap_atomic(page); vaddr &= ~PAGE_MASK; - memcpy(opcode, vaddr_new + vaddr, UPROBE_BKPT_INSN_SIZE); + memcpy(opcode, vaddr_new + vaddr, UPROBE_SWBP_INSN_SIZE); kunmap_atomic(vaddr_new); unlock_page(page); @@ -317,7 +317,7 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_ return 0; } -static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) +static int is_swbp_at_addr(struct mm_struct *mm, unsigned long vaddr) { uprobe_opcode_t opcode; int result; @@ -326,14 +326,14 @@ static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) if (result) return result; - if (is_bkpt_insn(&opcode)) + if (is_swbp_insn(&opcode)) return 1; return 0; } /** - * set_bkpt - store breakpoint at a given address. + * set_swbp - store breakpoint at a given address. * @auprobe: arch specific probepoint information. * @mm: the probed process address space. * @vaddr: the virtual address to insert the opcode. @@ -341,18 +341,18 @@ static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) * For mm @mm, store the breakpoint instruction at @vaddr. * Return 0 (success) or a negative errno. */ -int __weak set_bkpt(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) +int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) { int result; - result = is_bkpt_at_addr(mm, vaddr); + result = is_swbp_at_addr(mm, vaddr); if (result == 1) return -EEXIST; if (result) return result; - return write_opcode(auprobe, mm, vaddr, UPROBE_BKPT_INSN); + return write_opcode(auprobe, mm, vaddr, UPROBE_SWBP_INSN); } /** @@ -371,7 +371,7 @@ set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long v if (verify) { int result; - result = is_bkpt_at_addr(mm, vaddr); + result = is_swbp_at_addr(mm, vaddr); if (!result) return -EINVAL; @@ -642,7 +642,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, if (ret) return ret; - if (is_bkpt_insn((uprobe_opcode_t *)uprobe->arch.insn)) + if (is_swbp_insn((uprobe_opcode_t *)uprobe->arch.insn)) return -EEXIST; ret = arch_uprobes_analyze_insn(&uprobe->arch, mm); @@ -651,7 +651,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, uprobe->flags |= UPROBE_COPY_INSN; } - ret = set_bkpt(&uprobe->arch, mm, addr); + ret = set_swbp(&uprobe->arch, mm, addr); return ret; } -- cgit v1.2.3 From 0326f5a94ddea33fa331b2519f4172f4fb387baa Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Tue, 13 Mar 2012 23:30:11 +0530 Subject: uprobes/core: Handle breakpoint and singlestep exceptions Uprobes uses exception notifiers to get to know if a thread hit a breakpoint or a singlestep exception. When a thread hits a uprobe or is singlestepping post a uprobe hit, the uprobe exception notifier sets its TIF_UPROBE bit, which will then be checked on its return to userspace path (do_notify_resume() ->uprobe_notify_resume()), where the consumers handlers are run (in task context) based on the defined filters. Uprobe hits are thread specific and hence we need to maintain information about if a task hit a uprobe, what uprobe was hit, the slot where the original instruction was copied for xol so that it can be singlestepped with appropriate fixups. In some cases, special care is needed for instructions that are executed out of line (xol). These are architecture specific artefacts, such as handling RIP relative instructions on x86_64. Since the instruction at which the uprobe was inserted is executed out of line, architecture specific fixups are added so that the thread continues normal execution in the presence of a uprobe. Postpone the signals until we execute the probed insn. post_xol() path does a recalc_sigpending() before return to user-mode, this ensures the signal can't be lost. Uprobes relies on DIE_DEBUG notification to notify if a singlestep is complete. Adds x86 specific uprobe exception notifiers and appropriate hooks needed to determine a uprobe hit and subsequent post processing. Add requisite x86 fixups for xol for uprobes. Specific cases needing fixups include relative jumps (x86_64), calls, etc. Where possible, we check and skip singlestepping the breakpointed instructions. For now we skip single byte as well as few multibyte nop instructions. However this can be extended to other instructions too. Credits to Oleg Nesterov for suggestions/patches related to signal, breakpoint, singlestep handling code. Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120313180011.29771.89027.sendpatchset@srdronam.in.ibm.com [ Performed various cleanliness edits ] Signed-off-by: Ingo Molnar --- arch/x86/include/asm/thread_info.h | 2 + arch/x86/include/asm/uprobes.h | 16 +- arch/x86/kernel/signal.c | 6 + arch/x86/kernel/uprobes.c | 265 +++++++++++++++++++++++++++++- include/linux/sched.h | 4 + include/linux/uprobes.h | 55 ++++++- kernel/events/uprobes.c | 323 ++++++++++++++++++++++++++++++++++++- kernel/fork.c | 4 + kernel/signal.c | 4 + 9 files changed, 664 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index ad6df8ccd715..0710c11305d4 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -85,6 +85,7 @@ struct thread_info { #define TIF_SECCOMP 8 /* secure computing */ #define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */ #define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */ +#define TIF_UPROBE 12 /* breakpointed or singlestepping */ #define TIF_NOTSC 16 /* TSC is not accessible in userland */ #define TIF_IA32 17 /* IA32 compatibility process */ #define TIF_FORK 18 /* ret_from_fork */ @@ -109,6 +110,7 @@ struct thread_info { #define _TIF_SECCOMP (1 << TIF_SECCOMP) #define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY) #define _TIF_USER_RETURN_NOTIFY (1 << TIF_USER_RETURN_NOTIFY) +#define _TIF_UPROBE (1 << TIF_UPROBE) #define _TIF_NOTSC (1 << TIF_NOTSC) #define _TIF_IA32 (1 << TIF_IA32) #define _TIF_FORK (1 << TIF_FORK) diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 0500391f57d0..1e9bed14f7ae 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -23,6 +23,8 @@ * Jim Keniston */ +#include + typedef u8 uprobe_opcode_t; #define MAX_UINSN_BYTES 16 @@ -39,5 +41,17 @@ struct arch_uprobe { #endif }; -extern int arch_uprobes_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm); +struct arch_uprobe_task { + unsigned long saved_trap_nr; +#ifdef CONFIG_X86_64 + unsigned long saved_scratch_register; +#endif +}; + +extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm); +extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); +extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); +extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); +extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); +extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); #endif /* _ASM_UPROBES_H */ diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index 9c73acc1c860..b3cd6913ceea 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -823,6 +824,11 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) mce_notify_process(); #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ + if (thread_info_flags & _TIF_UPROBE) { + clear_thread_flag(TIF_UPROBE); + uprobe_notify_resume(regs); + } + /* deal with pending signal delivery */ if (thread_info_flags & _TIF_SIGPENDING) do_signal(regs); diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 851a11b0d38c..dc4e910a7d96 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -24,22 +24,28 @@ #include #include #include +#include #include +#include #include /* Post-execution fixups. */ /* No fixup needed */ -#define UPROBE_FIX_NONE 0x0 +#define UPROBE_FIX_NONE 0x0 + /* Adjust IP back to vicinity of actual insn */ #define UPROBE_FIX_IP 0x1 + /* Adjust the return address of a call insn */ #define UPROBE_FIX_CALL 0x2 #define UPROBE_FIX_RIP_AX 0x8000 #define UPROBE_FIX_RIP_CX 0x4000 +#define UPROBE_TRAP_NR UINT_MAX + /* Adaptations for mhiramat x86 decoder v14. */ #define OPCODE1(insn) ((insn)->opcode.bytes[0]) #define OPCODE2(insn) ((insn)->opcode.bytes[1]) @@ -221,10 +227,9 @@ static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn) } /* - * Figure out which fixups post_xol() will need to perform, and annotate - * arch_uprobe->fixups accordingly. To start with, - * arch_uprobe->fixups is either zero or it reflects rip-related - * fixups. + * Figure out which fixups arch_uprobe_post_xol() will need to perform, and + * annotate arch_uprobe->fixups accordingly. To start with, + * arch_uprobe->fixups is either zero or it reflects rip-related fixups. */ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) { @@ -401,12 +406,12 @@ static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, #endif /* CONFIG_X86_64 */ /** - * arch_uprobes_analyze_insn - instruction analysis including validity and fixups. + * arch_uprobe_analyze_insn - instruction analysis including validity and fixups. * @mm: the probed address space. * @arch_uprobe: the probepoint information. * Return 0 on success or a -ve number on error. */ -int arch_uprobes_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm) +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm) { int ret; struct insn insn; @@ -421,3 +426,249 @@ int arch_uprobes_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm) return 0; } + +#ifdef CONFIG_X86_64 +/* + * If we're emulating a rip-relative instruction, save the contents + * of the scratch register and store the target address in that register. + */ +static void +pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs, + struct arch_uprobe_task *autask) +{ + if (auprobe->fixups & UPROBE_FIX_RIP_AX) { + autask->saved_scratch_register = regs->ax; + regs->ax = current->utask->vaddr; + regs->ax += auprobe->rip_rela_target_address; + } else if (auprobe->fixups & UPROBE_FIX_RIP_CX) { + autask->saved_scratch_register = regs->cx; + regs->cx = current->utask->vaddr; + regs->cx += auprobe->rip_rela_target_address; + } +} +#else +static void +pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs, + struct arch_uprobe_task *autask) +{ + /* No RIP-relative addressing on 32-bit */ +} +#endif + +/* + * arch_uprobe_pre_xol - prepare to execute out of line. + * @auprobe: the probepoint information. + * @regs: reflects the saved user state of current task. + */ +int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct arch_uprobe_task *autask; + + autask = ¤t->utask->autask; + autask->saved_trap_nr = current->thread.trap_nr; + current->thread.trap_nr = UPROBE_TRAP_NR; + regs->ip = current->utask->xol_vaddr; + pre_xol_rip_insn(auprobe, regs, autask); + + return 0; +} + +/* + * This function is called by arch_uprobe_post_xol() to adjust the return + * address pushed by a call instruction executed out of line. + */ +static int adjust_ret_addr(unsigned long sp, long correction) +{ + int rasize, ncopied; + long ra = 0; + + if (is_ia32_task()) + rasize = 4; + else + rasize = 8; + + ncopied = copy_from_user(&ra, (void __user *)sp, rasize); + if (unlikely(ncopied)) + return -EFAULT; + + ra += correction; + ncopied = copy_to_user((void __user *)sp, &ra, rasize); + if (unlikely(ncopied)) + return -EFAULT; + + return 0; +} + +#ifdef CONFIG_X86_64 +static bool is_riprel_insn(struct arch_uprobe *auprobe) +{ + return ((auprobe->fixups & (UPROBE_FIX_RIP_AX | UPROBE_FIX_RIP_CX)) != 0); +} + +static void +handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction) +{ + if (is_riprel_insn(auprobe)) { + struct arch_uprobe_task *autask; + + autask = ¤t->utask->autask; + if (auprobe->fixups & UPROBE_FIX_RIP_AX) + regs->ax = autask->saved_scratch_register; + else + regs->cx = autask->saved_scratch_register; + + /* + * The original instruction includes a displacement, and so + * is 4 bytes longer than what we've just single-stepped. + * Fall through to handle stuff like "jmpq *...(%rip)" and + * "callq *...(%rip)". + */ + if (correction) + *correction += 4; + } +} +#else +static void +handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction) +{ + /* No RIP-relative addressing on 32-bit */ +} +#endif + +/* + * If xol insn itself traps and generates a signal(Say, + * SIGILL/SIGSEGV/etc), then detect the case where a singlestepped + * instruction jumps back to its own address. It is assumed that anything + * like do_page_fault/do_trap/etc sets thread.trap_nr != -1. + * + * arch_uprobe_pre_xol/arch_uprobe_post_xol save/restore thread.trap_nr, + * arch_uprobe_xol_was_trapped() simply checks that ->trap_nr is not equal to + * UPROBE_TRAP_NR == -1 set by arch_uprobe_pre_xol(). + */ +bool arch_uprobe_xol_was_trapped(struct task_struct *t) +{ + if (t->thread.trap_nr != UPROBE_TRAP_NR) + return true; + + return false; +} + +/* + * Called after single-stepping. To avoid the SMP problems that can + * occur when we temporarily put back the original opcode to + * single-step, we single-stepped a copy of the instruction. + * + * This function prepares to resume execution after the single-step. + * We have to fix things up as follows: + * + * Typically, the new ip is relative to the copied instruction. We need + * to make it relative to the original instruction (FIX_IP). Exceptions + * are return instructions and absolute or indirect jump or call instructions. + * + * If the single-stepped instruction was a call, the return address that + * is atop the stack is the address following the copied instruction. We + * need to make it the address following the original instruction (FIX_CALL). + * + * If the original instruction was a rip-relative instruction such as + * "movl %edx,0xnnnn(%rip)", we have instead executed an equivalent + * instruction using a scratch register -- e.g., "movl %edx,(%rax)". + * We need to restore the contents of the scratch register and adjust + * the ip, keeping in mind that the instruction we executed is 4 bytes + * shorter than the original instruction (since we squeezed out the offset + * field). (FIX_RIP_AX or FIX_RIP_CX) + */ +int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask; + long correction; + int result = 0; + + WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR); + + utask = current->utask; + current->thread.trap_nr = utask->autask.saved_trap_nr; + correction = (long)(utask->vaddr - utask->xol_vaddr); + handle_riprel_post_xol(auprobe, regs, &correction); + if (auprobe->fixups & UPROBE_FIX_IP) + regs->ip += correction; + + if (auprobe->fixups & UPROBE_FIX_CALL) + result = adjust_ret_addr(regs->sp, correction); + + return result; +} + +/* callback routine for handling exceptions. */ +int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data) +{ + struct die_args *args = data; + struct pt_regs *regs = args->regs; + int ret = NOTIFY_DONE; + + /* We are only interested in userspace traps */ + if (regs && !user_mode_vm(regs)) + return NOTIFY_DONE; + + switch (val) { + case DIE_INT3: + if (uprobe_pre_sstep_notifier(regs)) + ret = NOTIFY_STOP; + + break; + + case DIE_DEBUG: + if (uprobe_post_sstep_notifier(regs)) + ret = NOTIFY_STOP; + + default: + break; + } + + return ret; +} + +/* + * This function gets called when XOL instruction either gets trapped or + * the thread has a fatal signal, so reset the instruction pointer to its + * probed address. + */ +void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + current->thread.trap_nr = utask->autask.saved_trap_nr; + handle_riprel_post_xol(auprobe, regs, NULL); + instruction_pointer_set(regs, utask->vaddr); +} + +/* + * Skip these instructions as per the currently known x86 ISA. + * 0x66* { 0x90 | 0x0f 0x1f | 0x0f 0x19 | 0x87 0xc0 } + */ +bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + int i; + + for (i = 0; i < MAX_UINSN_BYTES; i++) { + if ((auprobe->insn[i] == 0x66)) + continue; + + if (auprobe->insn[i] == 0x90) + return true; + + if (i == (MAX_UINSN_BYTES - 1)) + break; + + if ((auprobe->insn[i] == 0x0f) && (auprobe->insn[i+1] == 0x1f)) + return true; + + if ((auprobe->insn[i] == 0x0f) && (auprobe->insn[i+1] == 0x19)) + return true; + + if ((auprobe->insn[i] == 0x87) && (auprobe->insn[i+1] == 0xc0)) + return true; + + break; + } + return false; +} diff --git a/include/linux/sched.h b/include/linux/sched.h index 7d379a6bfd88..8379e3771690 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1590,6 +1590,10 @@ struct task_struct { #ifdef CONFIG_HAVE_HW_BREAKPOINT atomic_t ptrace_bp_refcnt; #endif +#ifdef CONFIG_UPROBES + struct uprobe_task *utask; + int uprobe_srcu_id; +#endif }; /* Future-safe accessor for struct task_struct's cpus_allowed. */ diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index eac525f41b94..5ec778fdce6f 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -28,8 +28,9 @@ #include struct vm_area_struct; + #ifdef CONFIG_ARCH_SUPPORTS_UPROBES -#include +# include #endif /* flags that denote/change uprobes behaviour */ @@ -39,6 +40,8 @@ struct vm_area_struct; /* Dont run handlers when first register/ last unregister in progress*/ #define UPROBE_RUN_HANDLER 0x2 +/* Can skip singlestep */ +#define UPROBE_SKIP_SSTEP 0x4 struct uprobe_consumer { int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); @@ -52,13 +55,42 @@ struct uprobe_consumer { }; #ifdef CONFIG_UPROBES +enum uprobe_task_state { + UTASK_RUNNING, + UTASK_BP_HIT, + UTASK_SSTEP, + UTASK_SSTEP_ACK, + UTASK_SSTEP_TRAPPED, +}; + +/* + * uprobe_task: Metadata of a task while it singlesteps. + */ +struct uprobe_task { + enum uprobe_task_state state; + struct arch_uprobe_task autask; + + struct uprobe *active_uprobe; + + unsigned long xol_vaddr; + unsigned long vaddr; +}; + extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_mmap(struct vm_area_struct *vma); -#else /* CONFIG_UPROBES is not defined */ +extern void uprobe_free_utask(struct task_struct *t); +extern void uprobe_copy_process(struct task_struct *t); +extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); +extern int uprobe_post_sstep_notifier(struct pt_regs *regs); +extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); +extern void uprobe_notify_resume(struct pt_regs *regs); +extern bool uprobe_deny_signal(void); +extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); +#else /* !CONFIG_UPROBES */ static inline int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { @@ -72,5 +104,22 @@ static inline int uprobe_mmap(struct vm_area_struct *vma) { return 0; } -#endif /* CONFIG_UPROBES */ +static inline void uprobe_notify_resume(struct pt_regs *regs) +{ +} +static inline bool uprobe_deny_signal(void) +{ + return false; +} +static inline unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) +{ + return 0; +} +static inline void uprobe_free_utask(struct task_struct *t) +{ +} +static inline void uprobe_copy_process(struct task_struct *t) +{ +} +#endif /* !CONFIG_UPROBES */ #endif /* _LINUX_UPROBES_H */ diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index e56e56aa7535..b807d1566b64 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -30,9 +30,12 @@ #include /* anon_vma_prepare */ #include /* set_pte_at_notify */ #include /* try_to_free_swap */ +#include /* user_enable_single_step */ +#include /* notifier mechanism */ #include +static struct srcu_struct uprobes_srcu; static struct rb_root uprobes_tree = RB_ROOT; static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ @@ -486,6 +489,9 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe) u = __insert_uprobe(uprobe); spin_unlock_irqrestore(&uprobes_treelock, flags); + /* For now assume that the instruction need not be single-stepped */ + uprobe->flags |= UPROBE_SKIP_SSTEP; + return u; } @@ -523,6 +529,21 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) return uprobe; } +static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs) +{ + struct uprobe_consumer *uc; + + if (!(uprobe->flags & UPROBE_RUN_HANDLER)) + return; + + down_read(&uprobe->consumer_rwsem); + for (uc = uprobe->consumers; uc; uc = uc->next) { + if (!uc->filter || uc->filter(uc, current)) + uc->handler(uc, regs); + } + up_read(&uprobe->consumer_rwsem); +} + /* Returns the previous consumer */ static struct uprobe_consumer * consumer_add(struct uprobe *uprobe, struct uprobe_consumer *uc) @@ -645,7 +666,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, if (is_swbp_insn((uprobe_opcode_t *)uprobe->arch.insn)) return -EEXIST; - ret = arch_uprobes_analyze_insn(&uprobe->arch, mm); + ret = arch_uprobe_analyze_insn(&uprobe->arch, mm); if (ret) return ret; @@ -662,10 +683,21 @@ remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, loff_t vaddr) set_orig_insn(&uprobe->arch, mm, (unsigned long)vaddr, true); } +/* + * There could be threads that have hit the breakpoint and are entering the + * notifier code and trying to acquire the uprobes_treelock. The thread + * calling delete_uprobe() that is removing the uprobe from the rb_tree can + * race with these threads and might acquire the uprobes_treelock compared + * to some of the breakpoint hit threads. In such a case, the breakpoint + * hit threads will not find the uprobe. The current unregistering thread + * waits till all other threads have hit a breakpoint, to acquire the + * uprobes_treelock before the uprobe is removed from the rbtree. + */ static void delete_uprobe(struct uprobe *uprobe) { unsigned long flags; + synchronize_srcu(&uprobes_srcu); spin_lock_irqsave(&uprobes_treelock, flags); rb_erase(&uprobe->rb_node, &uprobes_tree); spin_unlock_irqrestore(&uprobes_treelock, flags); @@ -1010,6 +1042,288 @@ int uprobe_mmap(struct vm_area_struct *vma) return ret; } +/** + * uprobe_get_swbp_addr - compute address of swbp given post-swbp regs + * @regs: Reflects the saved state of the task after it has hit a breakpoint + * instruction. + * Return the address of the breakpoint instruction. + */ +unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs) +{ + return instruction_pointer(regs) - UPROBE_SWBP_INSN_SIZE; +} + +/* + * Called with no locks held. + * Called in context of a exiting or a exec-ing thread. + */ +void uprobe_free_utask(struct task_struct *t) +{ + struct uprobe_task *utask = t->utask; + + if (t->uprobe_srcu_id != -1) + srcu_read_unlock_raw(&uprobes_srcu, t->uprobe_srcu_id); + + if (!utask) + return; + + if (utask->active_uprobe) + put_uprobe(utask->active_uprobe); + + kfree(utask); + t->utask = NULL; +} + +/* + * Called in context of a new clone/fork from copy_process. + */ +void uprobe_copy_process(struct task_struct *t) +{ + t->utask = NULL; + t->uprobe_srcu_id = -1; +} + +/* + * Allocate a uprobe_task object for the task. + * Called when the thread hits a breakpoint for the first time. + * + * Returns: + * - pointer to new uprobe_task on success + * - NULL otherwise + */ +static struct uprobe_task *add_utask(void) +{ + struct uprobe_task *utask; + + utask = kzalloc(sizeof *utask, GFP_KERNEL); + if (unlikely(!utask)) + return NULL; + + utask->active_uprobe = NULL; + current->utask = utask; + return utask; +} + +/* Prepare to single-step probed instruction out of line. */ +static int +pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long vaddr) +{ + return -EFAULT; +} + +/* + * If we are singlestepping, then ensure this thread is not connected to + * non-fatal signals until completion of singlestep. When xol insn itself + * triggers the signal, restart the original insn even if the task is + * already SIGKILL'ed (since coredump should report the correct ip). This + * is even more important if the task has a handler for SIGSEGV/etc, The + * _same_ instruction should be repeated again after return from the signal + * handler, and SSTEP can never finish in this case. + */ +bool uprobe_deny_signal(void) +{ + struct task_struct *t = current; + struct uprobe_task *utask = t->utask; + + if (likely(!utask || !utask->active_uprobe)) + return false; + + WARN_ON_ONCE(utask->state != UTASK_SSTEP); + + if (signal_pending(t)) { + spin_lock_irq(&t->sighand->siglock); + clear_tsk_thread_flag(t, TIF_SIGPENDING); + spin_unlock_irq(&t->sighand->siglock); + + if (__fatal_signal_pending(t) || arch_uprobe_xol_was_trapped(t)) { + utask->state = UTASK_SSTEP_TRAPPED; + set_tsk_thread_flag(t, TIF_UPROBE); + set_tsk_thread_flag(t, TIF_NOTIFY_RESUME); + } + } + + return true; +} + +/* + * Avoid singlestepping the original instruction if the original instruction + * is a NOP or can be emulated. + */ +static bool can_skip_sstep(struct uprobe *uprobe, struct pt_regs *regs) +{ + if (arch_uprobe_skip_sstep(&uprobe->arch, regs)) + return true; + + uprobe->flags &= ~UPROBE_SKIP_SSTEP; + return false; +} + +/* + * Run handler and ask thread to singlestep. + * Ensure all non-fatal signals cannot interrupt thread while it singlesteps. + */ +static void handle_swbp(struct pt_regs *regs) +{ + struct vm_area_struct *vma; + struct uprobe_task *utask; + struct uprobe *uprobe; + struct mm_struct *mm; + unsigned long bp_vaddr; + + uprobe = NULL; + bp_vaddr = uprobe_get_swbp_addr(regs); + mm = current->mm; + down_read(&mm->mmap_sem); + vma = find_vma(mm, bp_vaddr); + + if (vma && vma->vm_start <= bp_vaddr && valid_vma(vma, false)) { + struct inode *inode; + loff_t offset; + + inode = vma->vm_file->f_mapping->host; + offset = bp_vaddr - vma->vm_start; + offset += (vma->vm_pgoff << PAGE_SHIFT); + uprobe = find_uprobe(inode, offset); + } + + srcu_read_unlock_raw(&uprobes_srcu, current->uprobe_srcu_id); + current->uprobe_srcu_id = -1; + up_read(&mm->mmap_sem); + + if (!uprobe) { + /* No matching uprobe; signal SIGTRAP. */ + send_sig(SIGTRAP, current, 0); + return; + } + + utask = current->utask; + if (!utask) { + utask = add_utask(); + /* Cannot allocate; re-execute the instruction. */ + if (!utask) + goto cleanup_ret; + } + utask->active_uprobe = uprobe; + handler_chain(uprobe, regs); + if (uprobe->flags & UPROBE_SKIP_SSTEP && can_skip_sstep(uprobe, regs)) + goto cleanup_ret; + + utask->state = UTASK_SSTEP; + if (!pre_ssout(uprobe, regs, bp_vaddr)) { + user_enable_single_step(current); + return; + } + +cleanup_ret: + if (utask) { + utask->active_uprobe = NULL; + utask->state = UTASK_RUNNING; + } + if (uprobe) { + if (!(uprobe->flags & UPROBE_SKIP_SSTEP)) + + /* + * cannot singlestep; cannot skip instruction; + * re-execute the instruction. + */ + instruction_pointer_set(regs, bp_vaddr); + + put_uprobe(uprobe); + } +} + +/* + * Perform required fix-ups and disable singlestep. + * Allow pending signals to take effect. + */ +static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs) +{ + struct uprobe *uprobe; + + uprobe = utask->active_uprobe; + if (utask->state == UTASK_SSTEP_ACK) + arch_uprobe_post_xol(&uprobe->arch, regs); + else if (utask->state == UTASK_SSTEP_TRAPPED) + arch_uprobe_abort_xol(&uprobe->arch, regs); + else + WARN_ON_ONCE(1); + + put_uprobe(uprobe); + utask->active_uprobe = NULL; + utask->state = UTASK_RUNNING; + user_disable_single_step(current); + + spin_lock_irq(¤t->sighand->siglock); + recalc_sigpending(); /* see uprobe_deny_signal() */ + spin_unlock_irq(¤t->sighand->siglock); +} + +/* + * On breakpoint hit, breakpoint notifier sets the TIF_UPROBE flag. (and on + * subsequent probe hits on the thread sets the state to UTASK_BP_HIT) and + * allows the thread to return from interrupt. + * + * On singlestep exception, singlestep notifier sets the TIF_UPROBE flag and + * also sets the state to UTASK_SSTEP_ACK and allows the thread to return from + * interrupt. + * + * While returning to userspace, thread notices the TIF_UPROBE flag and calls + * uprobe_notify_resume(). + */ +void uprobe_notify_resume(struct pt_regs *regs) +{ + struct uprobe_task *utask; + + utask = current->utask; + if (!utask || utask->state == UTASK_BP_HIT) + handle_swbp(regs); + else + handle_singlestep(utask, regs); +} + +/* + * uprobe_pre_sstep_notifier gets called from interrupt context as part of + * notifier mechanism. Set TIF_UPROBE flag and indicate breakpoint hit. + */ +int uprobe_pre_sstep_notifier(struct pt_regs *regs) +{ + struct uprobe_task *utask; + + if (!current->mm) + return 0; + + utask = current->utask; + if (utask) + utask->state = UTASK_BP_HIT; + + set_thread_flag(TIF_UPROBE); + current->uprobe_srcu_id = srcu_read_lock_raw(&uprobes_srcu); + + return 1; +} + +/* + * uprobe_post_sstep_notifier gets called in interrupt context as part of notifier + * mechanism. Set TIF_UPROBE flag and indicate completion of singlestep. + */ +int uprobe_post_sstep_notifier(struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + if (!current->mm || !utask || !utask->active_uprobe) + /* task is currently not uprobed */ + return 0; + + utask->state = UTASK_SSTEP_ACK; + set_thread_flag(TIF_UPROBE); + return 1; +} + +static struct notifier_block uprobe_exception_nb = { + .notifier_call = arch_uprobe_exception_notify, + .priority = INT_MAX-1, /* notified after kprobes, kgdb */ +}; + static int __init init_uprobes(void) { int i; @@ -1018,12 +1332,13 @@ static int __init init_uprobes(void) mutex_init(&uprobes_mutex[i]); mutex_init(&uprobes_mmap_mutex[i]); } - return 0; + init_srcu_struct(&uprobes_srcu); + + return register_die_notifier(&uprobe_exception_nb); } +module_init(init_uprobes); static void __exit exit_uprobes(void) { } - -module_init(init_uprobes); module_exit(exit_uprobes); diff --git a/kernel/fork.c b/kernel/fork.c index e2cd3e2a5ae8..eb7b63334009 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include @@ -701,6 +702,8 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm) exit_pi_state_list(tsk); #endif + uprobe_free_utask(tsk); + /* Get rid of any cached register state */ deactivate_mm(tsk, mm); @@ -1295,6 +1298,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, INIT_LIST_HEAD(&p->pi_state_list); p->pi_state_cache = NULL; #endif + uprobe_copy_process(p); /* * sigaltstack should be cleared when sharing the same VM */ diff --git a/kernel/signal.c b/kernel/signal.c index 8511e39813c7..e93ff0a719a0 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -29,6 +29,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -2192,6 +2193,9 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct signal_struct *signal = current->signal; int signr; + if (unlikely(uprobe_deny_signal())) + return 0; + relock: /* * We'll jump back here after any time we were stopped in TASK_STOPPED. -- cgit v1.2.3 From 598971bfbdfdc8701337dc1636c7919c44699914 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 19 Mar 2012 15:10:58 -0700 Subject: cfq: don't use icq_get_changed() cfq caches the associated cfqq's for a given cic. The cache needs to be flushed if the cic's ioprio or blkcg has changed. It is currently done by requiring the changing action to set the respective ICQ_*_CHANGED bit in the icq and testing it from cfq_set_request(), which involves iterating through all the affected icqs. All cfq wants to know is whether ioprio and/or blkcg have changed since the last flush and can be easily achieved by just remembering the current ioprio and blkcg ID in cic. This patch adds cic->{ioprio|blkcg_id}, updates all ioprio users to use the remembered value instead, and updates cfq_set_request() path such that, instead of using icq_get_changed(), the current values are compared against the remembered ones and trigger appropriate flush action if not. Condition tests are moved inside both _changed functions which are now named check_ioprio_changed() and check_blkcg_changed(). ioprio.h::task_ioprio*() can't be used anymore and replaced with open-coded IOPRIO_CLASS_NONE case in cfq_async_queue_prio(). Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/cfq-iosched.c | 63 ++++++++++++++++++++++++++++++++------------------ include/linux/ioprio.h | 22 ++++-------------- 2 files changed, 45 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 9e8624e9e246..7c3893d4447a 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -218,6 +218,10 @@ struct cfq_io_cq { struct io_cq icq; /* must be the first member */ struct cfq_queue *cfqq[2]; struct cfq_ttime ttime; + int ioprio; /* the current ioprio */ +#ifdef CONFIG_CFQ_GROUP_IOSCHED + uint64_t blkcg_id; /* the current blkcg ID */ +#endif }; /* @@ -2568,7 +2572,7 @@ static void cfq_init_prio_data(struct cfq_queue *cfqq, struct cfq_io_cq *cic) if (!cfq_cfqq_prio_changed(cfqq)) return; - ioprio_class = IOPRIO_PRIO_CLASS(cic->icq.ioc->ioprio); + ioprio_class = IOPRIO_PRIO_CLASS(cic->ioprio); switch (ioprio_class) { default: printk(KERN_ERR "cfq: bad prio %x\n", ioprio_class); @@ -2580,11 +2584,11 @@ static void cfq_init_prio_data(struct cfq_queue *cfqq, struct cfq_io_cq *cic) cfqq->ioprio_class = task_nice_ioclass(tsk); break; case IOPRIO_CLASS_RT: - cfqq->ioprio = task_ioprio(cic->icq.ioc); + cfqq->ioprio = IOPRIO_PRIO_DATA(cic->ioprio); cfqq->ioprio_class = IOPRIO_CLASS_RT; break; case IOPRIO_CLASS_BE: - cfqq->ioprio = task_ioprio(cic->icq.ioc); + cfqq->ioprio = IOPRIO_PRIO_DATA(cic->ioprio); cfqq->ioprio_class = IOPRIO_CLASS_BE; break; case IOPRIO_CLASS_IDLE: @@ -2602,12 +2606,17 @@ static void cfq_init_prio_data(struct cfq_queue *cfqq, struct cfq_io_cq *cic) cfq_clear_cfqq_prio_changed(cfqq); } -static void changed_ioprio(struct cfq_io_cq *cic, struct bio *bio) +static void check_ioprio_changed(struct cfq_io_cq *cic, struct bio *bio) { + int ioprio = cic->icq.ioc->ioprio; struct cfq_data *cfqd = cic_to_cfqd(cic); struct cfq_queue *cfqq; - if (unlikely(!cfqd)) + /* + * Check whether ioprio has changed. The condition may trigger + * spuriously on a newly created cic but there's no harm. + */ + if (unlikely(!cfqd) || likely(cic->ioprio == ioprio)) return; cfqq = cic->cfqq[BLK_RW_ASYNC]; @@ -2624,6 +2633,8 @@ static void changed_ioprio(struct cfq_io_cq *cic, struct bio *bio) cfqq = cic->cfqq[BLK_RW_SYNC]; if (cfqq) cfq_mark_cfqq_prio_changed(cfqq); + + cic->ioprio = ioprio; } static void cfq_init_cfqq(struct cfq_data *cfqd, struct cfq_queue *cfqq, @@ -2647,17 +2658,24 @@ static void cfq_init_cfqq(struct cfq_data *cfqd, struct cfq_queue *cfqq, } #ifdef CONFIG_CFQ_GROUP_IOSCHED -static void changed_cgroup(struct cfq_io_cq *cic) +static void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio) { - struct cfq_queue *sync_cfqq = cic_to_cfqq(cic, 1); struct cfq_data *cfqd = cic_to_cfqd(cic); - struct request_queue *q; + struct cfq_queue *sync_cfqq; + uint64_t id; - if (unlikely(!cfqd)) - return; + rcu_read_lock(); + id = bio_blkio_cgroup(bio)->id; + rcu_read_unlock(); - q = cfqd->queue; + /* + * Check whether blkcg has changed. The condition may trigger + * spuriously on a newly created cic but there's no harm. + */ + if (unlikely(!cfqd) || likely(cic->blkcg_id == id)) + return; + sync_cfqq = cic_to_cfqq(cic, 1); if (sync_cfqq) { /* * Drop reference to sync queue. A new sync queue will be @@ -2667,7 +2685,11 @@ static void changed_cgroup(struct cfq_io_cq *cic) cic_set_cfqq(cic, NULL, 1); cfq_put_queue(sync_cfqq); } + + cic->blkcg_id = id; } +#else +static inline void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio) { } #endif /* CONFIG_CFQ_GROUP_IOSCHED */ static struct cfq_queue * @@ -2731,6 +2753,9 @@ cfq_async_queue_prio(struct cfq_data *cfqd, int ioprio_class, int ioprio) switch (ioprio_class) { case IOPRIO_CLASS_RT: return &cfqd->async_cfqq[0][ioprio]; + case IOPRIO_CLASS_NONE: + ioprio = IOPRIO_NORM; + /* fall through */ case IOPRIO_CLASS_BE: return &cfqd->async_cfqq[1][ioprio]; case IOPRIO_CLASS_IDLE: @@ -2744,8 +2769,8 @@ static struct cfq_queue * cfq_get_queue(struct cfq_data *cfqd, bool is_sync, struct cfq_io_cq *cic, struct bio *bio, gfp_t gfp_mask) { - const int ioprio = task_ioprio(cic->icq.ioc); - const int ioprio_class = task_ioprio_class(cic->icq.ioc); + const int ioprio_class = IOPRIO_PRIO_CLASS(cic->ioprio); + const int ioprio = IOPRIO_PRIO_DATA(cic->ioprio); struct cfq_queue **async_cfqq = NULL; struct cfq_queue *cfqq = NULL; @@ -3303,21 +3328,13 @@ cfq_set_request(struct request_queue *q, struct request *rq, struct bio *bio, const int rw = rq_data_dir(rq); const bool is_sync = rq_is_sync(rq); struct cfq_queue *cfqq; - unsigned int changed; might_sleep_if(gfp_mask & __GFP_WAIT); spin_lock_irq(q->queue_lock); - /* handle changed notifications */ - changed = icq_get_changed(&cic->icq); - if (unlikely(changed & ICQ_IOPRIO_CHANGED)) - changed_ioprio(cic, bio); -#ifdef CONFIG_CFQ_GROUP_IOSCHED - if (unlikely(changed & ICQ_CGROUP_CHANGED)) - changed_cgroup(cic); -#endif - + check_ioprio_changed(cic, bio); + check_blkcg_changed(cic, bio); new_queue: cfqq = cic_to_cfqq(cic, is_sync); if (!cfqq || cfqq == &cfqd->oom_cfqq) { diff --git a/include/linux/ioprio.h b/include/linux/ioprio.h index 76dad4808847..beb9ce1c2c23 100644 --- a/include/linux/ioprio.h +++ b/include/linux/ioprio.h @@ -42,26 +42,14 @@ enum { }; /* - * if process has set io priority explicitly, use that. if not, convert - * the cpu scheduler nice value to an io priority + * Fallback BE priority */ #define IOPRIO_NORM (4) -static inline int task_ioprio(struct io_context *ioc) -{ - if (ioprio_valid(ioc->ioprio)) - return IOPRIO_PRIO_DATA(ioc->ioprio); - - return IOPRIO_NORM; -} - -static inline int task_ioprio_class(struct io_context *ioc) -{ - if (ioprio_valid(ioc->ioprio)) - return IOPRIO_PRIO_CLASS(ioc->ioprio); - - return IOPRIO_CLASS_BE; -} +/* + * if process has set io priority explicitly, use that. if not, convert + * the cpu scheduler nice value to an io priority + */ static inline int task_nice_ioprio(struct task_struct *task) { return (task_nice(task) + 20) / 5; -- cgit v1.2.3 From 2b566fa55b9a94b53217c2818e6c5e5756eeb1a1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 19 Mar 2012 15:10:59 -0700 Subject: block: remove ioc_*_changed() After the previous patch to cfq, there's no ioc_get_changed() user left. This patch yanks out ioc_{ioprio|cgroup|get}_changed() and all related stuff. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 19 ------------- block/blk-ioc.c | 68 ----------------------------------------------- fs/ioprio.c | 2 +- include/linux/iocontext.h | 7 ----- 4 files changed, 1 insertion(+), 95 deletions(-) (limited to 'include/linux') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 30e07308db24..a74019b67311 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -47,8 +47,6 @@ static struct cgroup_subsys_state *blkiocg_create(struct cgroup_subsys *, struct cgroup *); static int blkiocg_can_attach(struct cgroup_subsys *, struct cgroup *, struct cgroup_taskset *); -static void blkiocg_attach(struct cgroup_subsys *, struct cgroup *, - struct cgroup_taskset *); static int blkiocg_pre_destroy(struct cgroup_subsys *, struct cgroup *); static void blkiocg_destroy(struct cgroup_subsys *, struct cgroup *); static int blkiocg_populate(struct cgroup_subsys *, struct cgroup *); @@ -63,7 +61,6 @@ struct cgroup_subsys blkio_subsys = { .name = "blkio", .create = blkiocg_create, .can_attach = blkiocg_can_attach, - .attach = blkiocg_attach, .pre_destroy = blkiocg_pre_destroy, .destroy = blkiocg_destroy, .populate = blkiocg_populate, @@ -1729,22 +1726,6 @@ static int blkiocg_can_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, return ret; } -static void blkiocg_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, - struct cgroup_taskset *tset) -{ - struct task_struct *task; - struct io_context *ioc; - - cgroup_taskset_for_each(task, cgrp, tset) { - /* we don't lose anything even if ioc allocation fails */ - ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE); - if (ioc) { - ioc_cgroup_changed(ioc); - put_io_context(ioc); - } - } -} - static void blkcg_bypass_start(void) __acquires(&all_q_mutex) { diff --git a/block/blk-ioc.c b/block/blk-ioc.c index 439ec21fd787..3f3dd51a1280 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -388,74 +388,6 @@ struct io_cq *ioc_create_icq(struct io_context *ioc, struct request_queue *q, return icq; } -void ioc_set_icq_flags(struct io_context *ioc, unsigned int flags) -{ - struct io_cq *icq; - struct hlist_node *n; - - hlist_for_each_entry(icq, n, &ioc->icq_list, ioc_node) - icq->flags |= flags; -} - -/** - * ioc_ioprio_changed - notify ioprio change - * @ioc: io_context of interest - * @ioprio: new ioprio - * - * @ioc's ioprio has changed to @ioprio. Set %ICQ_IOPRIO_CHANGED for all - * icq's. iosched is responsible for checking the bit and applying it on - * request issue path. - */ -void ioc_ioprio_changed(struct io_context *ioc, int ioprio) -{ - unsigned long flags; - - spin_lock_irqsave(&ioc->lock, flags); - ioc->ioprio = ioprio; - ioc_set_icq_flags(ioc, ICQ_IOPRIO_CHANGED); - spin_unlock_irqrestore(&ioc->lock, flags); -} - -/** - * ioc_cgroup_changed - notify cgroup change - * @ioc: io_context of interest - * - * @ioc's cgroup has changed. Set %ICQ_CGROUP_CHANGED for all icq's. - * iosched is responsible for checking the bit and applying it on request - * issue path. - */ -void ioc_cgroup_changed(struct io_context *ioc) -{ - unsigned long flags; - - spin_lock_irqsave(&ioc->lock, flags); - ioc_set_icq_flags(ioc, ICQ_CGROUP_CHANGED); - spin_unlock_irqrestore(&ioc->lock, flags); -} -EXPORT_SYMBOL(ioc_cgroup_changed); - -/** - * icq_get_changed - fetch and clear icq changed mask - * @icq: icq of interest - * - * Fetch and clear ICQ_*_CHANGED bits from @icq. Grabs and releases - * @icq->ioc->lock. - */ -unsigned icq_get_changed(struct io_cq *icq) -{ - unsigned int changed = 0; - unsigned long flags; - - if (unlikely(icq->flags & ICQ_CHANGED_MASK)) { - spin_lock_irqsave(&icq->ioc->lock, flags); - changed = icq->flags & ICQ_CHANGED_MASK; - icq->flags &= ~ICQ_CHANGED_MASK; - spin_unlock_irqrestore(&icq->ioc->lock, flags); - } - return changed; -} -EXPORT_SYMBOL(icq_get_changed); - static int __init blk_ioc_init(void) { iocontext_cachep = kmem_cache_create("blkdev_ioc", diff --git a/fs/ioprio.c b/fs/ioprio.c index 0f1b9515213b..48644373de58 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -50,7 +50,7 @@ int set_task_ioprio(struct task_struct *task, int ioprio) ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE); if (ioc) { - ioc_ioprio_changed(ioc, ioprio); + ioc->ioprio = ioprio; put_io_context(ioc); } diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index 6f1a2608e91f..df38db2ef45b 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -6,11 +6,7 @@ #include enum { - ICQ_IOPRIO_CHANGED = 1 << 0, - ICQ_CGROUP_CHANGED = 1 << 1, ICQ_EXITED = 1 << 2, - - ICQ_CHANGED_MASK = ICQ_IOPRIO_CHANGED | ICQ_CGROUP_CHANGED, }; /* @@ -152,9 +148,6 @@ void put_io_context_active(struct io_context *ioc); void exit_io_context(struct task_struct *task); struct io_context *get_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node); -void ioc_ioprio_changed(struct io_context *ioc, int ioprio); -void ioc_cgroup_changed(struct io_context *ioc); -unsigned int icq_get_changed(struct io_cq *icq); #else struct io_context; static inline void put_io_context(struct io_context *ioc) { } -- cgit v1.2.3 From f56f821feb7b36223f309e0ec05986bb137ce418 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 25 Mar 2012 19:47:41 +0200 Subject: mm: extend prefault helpers to fault in more than PAGE_SIZE drm/i915 wants to read/write more than one page in its fastpath and hence needs to prefault more than PAGE_SIZE bytes. Add new functions in filemap.h to make that possible. Also kill a copy&pasted spurious space in both functions while at it. v2: As suggested by Andrew Morton, add a multipage parameter to both functions to avoid the additional branch for the pagemap.c hotpath. My gcc 4.6 here seems to dtrt and indeed reap these branches where not needed. v3: Becaus I couldn't find a way around adding a uaddr += PAGE_SIZE to the filemap.c hotpaths (that the compiler couldn't remove again), let's go with separate new functions for the multipage use-case. v4: Adjust comment to CodingStlye and fix spelling. Acked-by: Andrew Morton Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 6 +-- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 2 +- include/linux/pagemap.h | 64 +++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e9cac478cced..6dc832902f53 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -416,7 +416,7 @@ i915_gem_shmem_pread(struct drm_device *dev, mutex_unlock(&dev->struct_mutex); if (!prefaulted) { - ret = fault_in_pages_writeable(user_data, remain); + ret = fault_in_multipages_writeable(user_data, remain); /* Userspace is tricking us, but we've already clobbered * its pages with the prefault and promised to write the * data up to the first fault. Hence ignore any errors @@ -809,8 +809,8 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, args->size)) return -EFAULT; - ret = fault_in_pages_readable((char __user *)(uintptr_t)args->data_ptr, - args->size); + ret = fault_in_multipages_readable((char __user *)(uintptr_t)args->data_ptr, + args->size); if (ret) return -EFAULT; diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index eb85860001ec..8e0b686d3afb 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -997,7 +997,7 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec, if (!access_ok(VERIFY_WRITE, ptr, length)) return -EFAULT; - if (fault_in_pages_readable(ptr, length)) + if (fault_in_multipages_readable(ptr, length)) return -EFAULT; } diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index cfaaa6949b8b..c93a9a9bcd35 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -426,7 +426,7 @@ static inline int fault_in_pages_writeable(char __user *uaddr, int size) */ if (((unsigned long)uaddr & PAGE_MASK) != ((unsigned long)end & PAGE_MASK)) - ret = __put_user(0, end); + ret = __put_user(0, end); } return ret; } @@ -445,13 +445,73 @@ static inline int fault_in_pages_readable(const char __user *uaddr, int size) if (((unsigned long)uaddr & PAGE_MASK) != ((unsigned long)end & PAGE_MASK)) { - ret = __get_user(c, end); + ret = __get_user(c, end); (void)c; } } return ret; } +/* + * Multipage variants of the above prefault helpers, useful if more than + * PAGE_SIZE of data needs to be prefaulted. These are separate from the above + * functions (which only handle up to PAGE_SIZE) to avoid clobbering the + * filemap.c hotpaths. + */ +static inline int fault_in_multipages_writeable(char __user *uaddr, int size) +{ + int ret; + const char __user *end = uaddr + size - 1; + + if (unlikely(size == 0)) + return 0; + + /* + * Writing zeroes into userspace here is OK, because we know that if + * the zero gets there, we'll be overwriting it. + */ + while (uaddr <= end) { + ret = __put_user(0, uaddr); + if (ret != 0) + return ret; + uaddr += PAGE_SIZE; + } + + /* Check whether the range spilled into the next page. */ + if (((unsigned long)uaddr & PAGE_MASK) == + ((unsigned long)end & PAGE_MASK)) + ret = __put_user(0, end); + + return ret; +} + +static inline int fault_in_multipages_readable(const char __user *uaddr, + int size) +{ + volatile char c; + int ret; + const char __user *end = uaddr + size - 1; + + if (unlikely(size == 0)) + return 0; + + while (uaddr <= end) { + ret = __get_user(c, uaddr); + if (ret != 0) + return ret; + uaddr += PAGE_SIZE; + } + + /* Check whether the range spilled into the next page. */ + if (((unsigned long)uaddr & PAGE_MASK) == + ((unsigned long)end & PAGE_MASK)) { + ret = __get_user(c, end); + (void)c; + } + + return ret; +} + int add_to_page_cache_locked(struct page *page, struct address_space *mapping, pgoff_t index, gfp_t gfp_mask); int add_to_page_cache_lru(struct page *page, struct address_space *mapping, -- cgit v1.2.3 From d4b3b6384f98f8692ad0209891ccdbc7e78bbefe Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Fri, 30 Mar 2012 23:56:31 +0530 Subject: uprobes/core: Allocate XOL slots for uprobes use Uprobes executes the original instruction at a probed location out of line. For this, we allocate a page (per mm) upon the first uprobe hit, in the process user address space, divide it into slots that are used to store the actual instructions to be singlestepped. These slots are known as xol (execution out of line) slots. Care is taken to ensure that the allocation is in an unmapped area as close to the top of the user address space as possible, with appropriate permission settings to keep selinux like frameworks happy. Upon a uprobe hit, a free slot is acquired, and is released after the singlestep completes. Lots of improvements courtesy suggestions/inputs from Peter and Oleg. [ Folded a fix for build issue on powerpc fixed and reported by Stephen Rothwell. ] Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Anton Arapov Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120330182631.10018.48175.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- include/linux/mm_types.h | 2 + include/linux/uprobes.h | 34 ++++++++ kernel/events/uprobes.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/fork.c | 2 + 4 files changed, 253 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 3cc3062b3767..26574c726121 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -388,6 +389,7 @@ struct mm_struct { #ifdef CONFIG_CPUMASK_OFFSTACK struct cpumask cpumask_allocation; #endif + struct uprobes_state uprobes_state; }; static inline void mm_init_cpumask(struct mm_struct *mm) diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 5ec778fdce6f..a111460c07d5 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -28,6 +28,8 @@ #include struct vm_area_struct; +struct mm_struct; +struct inode; #ifdef CONFIG_ARCH_SUPPORTS_UPROBES # include @@ -76,6 +78,28 @@ struct uprobe_task { unsigned long vaddr; }; +/* + * On a breakpoint hit, thread contests for a slot. It frees the + * slot after singlestep. Currently a fixed number of slots are + * allocated. + */ +struct xol_area { + wait_queue_head_t wq; /* if all slots are busy */ + atomic_t slot_count; /* number of in-use slots */ + unsigned long *bitmap; /* 0 = free slot */ + struct page *page; + + /* + * We keep the vma's vm_start rather than a pointer to the vma + * itself. The probed process or a naughty kernel module could make + * the vma go away, and we must handle that reasonably gracefully. + */ + unsigned long vaddr; /* Page(s) of instruction slots */ +}; + +struct uprobes_state { + struct xol_area *xol_area; +}; extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); @@ -90,7 +114,11 @@ extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); extern void uprobe_notify_resume(struct pt_regs *regs); extern bool uprobe_deny_signal(void); extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); +extern void uprobe_clear_state(struct mm_struct *mm); +extern void uprobe_reset_state(struct mm_struct *mm); #else /* !CONFIG_UPROBES */ +struct uprobes_state { +}; static inline int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { @@ -121,5 +149,11 @@ static inline void uprobe_free_utask(struct task_struct *t) static inline void uprobe_copy_process(struct task_struct *t) { } +static inline void uprobe_clear_state(struct mm_struct *mm) +{ +} +static inline void uprobe_reset_state(struct mm_struct *mm) +{ +} #endif /* !CONFIG_UPROBES */ #endif /* _LINUX_UPROBES_H */ diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index b807d1566b64..b395edb97f53 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -35,6 +35,9 @@ #include +#define UINSNS_PER_PAGE (PAGE_SIZE/UPROBE_XOL_SLOT_BYTES) +#define MAX_UPROBE_XOL_SLOTS UINSNS_PER_PAGE + static struct srcu_struct uprobes_srcu; static struct rb_root uprobes_tree = RB_ROOT; @@ -1042,6 +1045,213 @@ int uprobe_mmap(struct vm_area_struct *vma) return ret; } +/* Slot allocation for XOL */ +static int xol_add_vma(struct xol_area *area) +{ + struct mm_struct *mm; + int ret; + + area->page = alloc_page(GFP_HIGHUSER); + if (!area->page) + return -ENOMEM; + + ret = -EALREADY; + mm = current->mm; + + down_write(&mm->mmap_sem); + if (mm->uprobes_state.xol_area) + goto fail; + + ret = -ENOMEM; + + /* Try to map as high as possible, this is only a hint. */ + area->vaddr = get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE, PAGE_SIZE, 0, 0); + if (area->vaddr & ~PAGE_MASK) { + ret = area->vaddr; + goto fail; + } + + ret = install_special_mapping(mm, area->vaddr, PAGE_SIZE, + VM_EXEC|VM_MAYEXEC|VM_DONTCOPY|VM_IO, &area->page); + if (ret) + goto fail; + + smp_wmb(); /* pairs with get_xol_area() */ + mm->uprobes_state.xol_area = area; + ret = 0; + +fail: + up_write(&mm->mmap_sem); + if (ret) + __free_page(area->page); + + return ret; +} + +static struct xol_area *get_xol_area(struct mm_struct *mm) +{ + struct xol_area *area; + + area = mm->uprobes_state.xol_area; + smp_read_barrier_depends(); /* pairs with wmb in xol_add_vma() */ + + return area; +} + +/* + * xol_alloc_area - Allocate process's xol_area. + * This area will be used for storing instructions for execution out of + * line. + * + * Returns the allocated area or NULL. + */ +static struct xol_area *xol_alloc_area(void) +{ + struct xol_area *area; + + area = kzalloc(sizeof(*area), GFP_KERNEL); + if (unlikely(!area)) + return NULL; + + area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long), GFP_KERNEL); + + if (!area->bitmap) + goto fail; + + init_waitqueue_head(&area->wq); + if (!xol_add_vma(area)) + return area; + +fail: + kfree(area->bitmap); + kfree(area); + + return get_xol_area(current->mm); +} + +/* + * uprobe_clear_state - Free the area allocated for slots. + */ +void uprobe_clear_state(struct mm_struct *mm) +{ + struct xol_area *area = mm->uprobes_state.xol_area; + + if (!area) + return; + + put_page(area->page); + kfree(area->bitmap); + kfree(area); +} + +/* + * uprobe_reset_state - Free the area allocated for slots. + */ +void uprobe_reset_state(struct mm_struct *mm) +{ + mm->uprobes_state.xol_area = NULL; +} + +/* + * - search for a free slot. + */ +static unsigned long xol_take_insn_slot(struct xol_area *area) +{ + unsigned long slot_addr; + int slot_nr; + + do { + slot_nr = find_first_zero_bit(area->bitmap, UINSNS_PER_PAGE); + if (slot_nr < UINSNS_PER_PAGE) { + if (!test_and_set_bit(slot_nr, area->bitmap)) + break; + + slot_nr = UINSNS_PER_PAGE; + continue; + } + wait_event(area->wq, (atomic_read(&area->slot_count) < UINSNS_PER_PAGE)); + } while (slot_nr >= UINSNS_PER_PAGE); + + slot_addr = area->vaddr + (slot_nr * UPROBE_XOL_SLOT_BYTES); + atomic_inc(&area->slot_count); + + return slot_addr; +} + +/* + * xol_get_insn_slot - If was not allocated a slot, then + * allocate a slot. + * Returns the allocated slot address or 0. + */ +static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot_addr) +{ + struct xol_area *area; + unsigned long offset; + void *vaddr; + + area = get_xol_area(current->mm); + if (!area) { + area = xol_alloc_area(); + if (!area) + return 0; + } + current->utask->xol_vaddr = xol_take_insn_slot(area); + + /* + * Initialize the slot if xol_vaddr points to valid + * instruction slot. + */ + if (unlikely(!current->utask->xol_vaddr)) + return 0; + + current->utask->vaddr = slot_addr; + offset = current->utask->xol_vaddr & ~PAGE_MASK; + vaddr = kmap_atomic(area->page); + memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES); + kunmap_atomic(vaddr); + + return current->utask->xol_vaddr; +} + +/* + * xol_free_insn_slot - If slot was earlier allocated by + * @xol_get_insn_slot(), make the slot available for + * subsequent requests. + */ +static void xol_free_insn_slot(struct task_struct *tsk) +{ + struct xol_area *area; + unsigned long vma_end; + unsigned long slot_addr; + + if (!tsk->mm || !tsk->mm->uprobes_state.xol_area || !tsk->utask) + return; + + slot_addr = tsk->utask->xol_vaddr; + + if (unlikely(!slot_addr || IS_ERR_VALUE(slot_addr))) + return; + + area = tsk->mm->uprobes_state.xol_area; + vma_end = area->vaddr + PAGE_SIZE; + if (area->vaddr <= slot_addr && slot_addr < vma_end) { + unsigned long offset; + int slot_nr; + + offset = slot_addr - area->vaddr; + slot_nr = offset / UPROBE_XOL_SLOT_BYTES; + if (slot_nr >= UINSNS_PER_PAGE) + return; + + clear_bit(slot_nr, area->bitmap); + atomic_dec(&area->slot_count); + if (waitqueue_active(&area->wq)) + wake_up(&area->wq); + + tsk->utask->xol_vaddr = 0; + } +} + /** * uprobe_get_swbp_addr - compute address of swbp given post-swbp regs * @regs: Reflects the saved state of the task after it has hit a breakpoint @@ -1070,6 +1280,7 @@ void uprobe_free_utask(struct task_struct *t) if (utask->active_uprobe) put_uprobe(utask->active_uprobe); + xol_free_insn_slot(t); kfree(utask); t->utask = NULL; } @@ -1108,6 +1319,9 @@ static struct uprobe_task *add_utask(void) static int pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long vaddr) { + if (xol_get_insn_slot(uprobe, vaddr) && !arch_uprobe_pre_xol(&uprobe->arch, regs)) + return 0; + return -EFAULT; } @@ -1252,6 +1466,7 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs) utask->active_uprobe = NULL; utask->state = UTASK_RUNNING; user_disable_single_step(current); + xol_free_insn_slot(current); spin_lock_irq(¤t->sighand->siglock); recalc_sigpending(); /* see uprobe_deny_signal() */ diff --git a/kernel/fork.c b/kernel/fork.c index eb7b63334009..3133b9da59d5 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -554,6 +554,7 @@ void mmput(struct mm_struct *mm) might_sleep(); if (atomic_dec_and_test(&mm->mm_users)) { + uprobe_clear_state(mm); exit_aio(mm); ksm_exit(mm); khugepaged_exit(mm); /* must run before exit_mmap */ @@ -760,6 +761,7 @@ struct mm_struct *dup_mm(struct task_struct *tsk) #ifdef CONFIG_TRANSPARENT_HUGEPAGE mm->pmd_huge_pte = NULL; #endif + uprobe_reset_state(mm); if (!mm_init(mm, tsk)) goto fail_nomem; -- cgit v1.2.3 From 682968e0c425c60f0dde37977e5beb2b12ddc4cc Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Fri, 30 Mar 2012 23:56:46 +0530 Subject: uprobes/core: Optimize probe hits with the help of a counter Maintain a per-mm counter: number of uprobes that are inserted on this process address space. This counter can be used at probe hit time to determine if we need a lookup in the uprobes rbtree. Everytime a probe gets inserted successfully, the probe count is incremented and everytime a probe gets removed, the probe count is decremented. The new uprobe_munmap hook ensures the count is correct on a unmap or remap of a region. We expect that once a uprobe_munmap() is called, the vma goes away. So uprobe_unregister() finding a probe to unregister would either mean unmap event hasnt occurred yet or a mmap event on the same executable file occured after a unmap event. Additionally, uprobe_mmap hook now also gets called: a. on every executable vma that is COWed at fork. b. a vma of interest is newly mapped; breakpoint insertion also happens at the required address. On process creation, make sure the probes count in the child is set correctly. Special cases that are taken care include: a. mremap b. VM_DONTCOPY vmas on fork() c. insertion/removal races in the parent during fork(). Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Anton Arapov Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120330182646.10018.85805.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- include/linux/uprobes.h | 5 ++ kernel/events/uprobes.c | 119 ++++++++++++++++++++++++++++++++++++++++++++---- kernel/fork.c | 3 ++ mm/mmap.c | 10 +++- 4 files changed, 128 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index a111460c07d5..d594d3b3ad4c 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -99,6 +99,7 @@ struct xol_area { struct uprobes_state { struct xol_area *xol_area; + atomic_t count; }; extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); @@ -106,6 +107,7 @@ extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_mmap(struct vm_area_struct *vma); +extern void uprobe_munmap(struct vm_area_struct *vma); extern void uprobe_free_utask(struct task_struct *t); extern void uprobe_copy_process(struct task_struct *t); extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); @@ -132,6 +134,9 @@ static inline int uprobe_mmap(struct vm_area_struct *vma) { return 0; } +static inline void uprobe_munmap(struct vm_area_struct *vma) +{ +} static inline void uprobe_notify_resume(struct pt_regs *regs) { } diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index b395edb97f53..29e881b0137d 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -642,6 +642,29 @@ copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr) return __copy_insn(mapping, vma, uprobe->arch.insn, bytes, uprobe->offset); } +/* + * How mm->uprobes_state.count gets updated + * uprobe_mmap() increments the count if + * - it successfully adds a breakpoint. + * - it cannot add a breakpoint, but sees that there is a underlying + * breakpoint (via a is_swbp_at_addr()). + * + * uprobe_munmap() decrements the count if + * - it sees a underlying breakpoint, (via is_swbp_at_addr) + * (Subsequent uprobe_unregister wouldnt find the breakpoint + * unless a uprobe_mmap kicks in, since the old vma would be + * dropped just after uprobe_munmap.) + * + * uprobe_register increments the count if: + * - it successfully adds a breakpoint. + * + * uprobe_unregister decrements the count if: + * - it sees a underlying breakpoint and removes successfully. + * (via is_swbp_at_addr) + * (Subsequent uprobe_munmap wouldnt find the breakpoint + * since there is no underlying breakpoint after the + * breakpoint removal.) + */ static int install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, struct vm_area_struct *vma, loff_t vaddr) @@ -675,7 +698,19 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, uprobe->flags |= UPROBE_COPY_INSN; } + + /* + * Ideally, should be updating the probe count after the breakpoint + * has been successfully inserted. However a thread could hit the + * breakpoint we just inserted even before the probe count is + * incremented. If this is the first breakpoint placed, breakpoint + * notifier might ignore uprobes and pass the trap to the thread. + * Hence increment before and decrement on failure. + */ + atomic_inc(&mm->uprobes_state.count); ret = set_swbp(&uprobe->arch, mm, addr); + if (ret) + atomic_dec(&mm->uprobes_state.count); return ret; } @@ -683,7 +718,8 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, static void remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, loff_t vaddr) { - set_orig_insn(&uprobe->arch, mm, (unsigned long)vaddr, true); + if (!set_orig_insn(&uprobe->arch, mm, (unsigned long)vaddr, true)) + atomic_dec(&mm->uprobes_state.count); } /* @@ -1009,7 +1045,7 @@ int uprobe_mmap(struct vm_area_struct *vma) struct list_head tmp_list; struct uprobe *uprobe, *u; struct inode *inode; - int ret; + int ret, count; if (!atomic_read(&uprobe_events) || !valid_vma(vma, true)) return 0; @@ -1023,6 +1059,7 @@ int uprobe_mmap(struct vm_area_struct *vma) build_probe_list(inode, &tmp_list); ret = 0; + count = 0; list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { loff_t vaddr; @@ -1030,21 +1067,85 @@ int uprobe_mmap(struct vm_area_struct *vma) list_del(&uprobe->pending_list); if (!ret) { vaddr = vma_address(vma, uprobe->offset); - if (vaddr >= vma->vm_start && vaddr < vma->vm_end) { - ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); - /* Ignore double add: */ - if (ret == -EEXIST) - ret = 0; + + if (vaddr < vma->vm_start || vaddr >= vma->vm_end) { + put_uprobe(uprobe); + continue; } + + ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); + + /* Ignore double add: */ + if (ret == -EEXIST) { + ret = 0; + + if (!is_swbp_at_addr(vma->vm_mm, vaddr)) + continue; + + /* + * Unable to insert a breakpoint, but + * breakpoint lies underneath. Increment the + * probe count. + */ + atomic_inc(&vma->vm_mm->uprobes_state.count); + } + + if (!ret) + count++; } put_uprobe(uprobe); } mutex_unlock(uprobes_mmap_hash(inode)); + if (ret) + atomic_sub(count, &vma->vm_mm->uprobes_state.count); + return ret; } +/* + * Called in context of a munmap of a vma. + */ +void uprobe_munmap(struct vm_area_struct *vma) +{ + struct list_head tmp_list; + struct uprobe *uprobe, *u; + struct inode *inode; + + if (!atomic_read(&uprobe_events) || !valid_vma(vma, false)) + return; + + if (!atomic_read(&vma->vm_mm->uprobes_state.count)) + return; + + inode = vma->vm_file->f_mapping->host; + if (!inode) + return; + + INIT_LIST_HEAD(&tmp_list); + mutex_lock(uprobes_mmap_hash(inode)); + build_probe_list(inode, &tmp_list); + + list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { + loff_t vaddr; + + list_del(&uprobe->pending_list); + vaddr = vma_address(vma, uprobe->offset); + + if (vaddr >= vma->vm_start && vaddr < vma->vm_end) { + /* + * An unregister could have removed the probe before + * unmap. So check before we decrement the count. + */ + if (is_swbp_at_addr(vma->vm_mm, vaddr) == 1) + atomic_dec(&vma->vm_mm->uprobes_state.count); + } + put_uprobe(uprobe); + } + mutex_unlock(uprobes_mmap_hash(inode)); +} + /* Slot allocation for XOL */ static int xol_add_vma(struct xol_area *area) { @@ -1150,6 +1251,7 @@ void uprobe_clear_state(struct mm_struct *mm) void uprobe_reset_state(struct mm_struct *mm) { mm->uprobes_state.xol_area = NULL; + atomic_set(&mm->uprobes_state.count, 0); } /* @@ -1504,7 +1606,8 @@ int uprobe_pre_sstep_notifier(struct pt_regs *regs) { struct uprobe_task *utask; - if (!current->mm) + if (!current->mm || !atomic_read(¤t->mm->uprobes_state.count)) + /* task is currently not uprobed */ return 0; utask = current->utask; diff --git a/kernel/fork.c b/kernel/fork.c index 3133b9da59d5..26a8f5c25805 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -421,6 +421,9 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) if (retval) goto out; + + if (file && uprobe_mmap(tmp)) + goto out; } /* a new mm has just been created */ arch_dup_mmap(oldmm, mm); diff --git a/mm/mmap.c b/mm/mmap.c index 5a863d328a44..7c112fbca405 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -218,6 +218,7 @@ void unlink_file_vma(struct vm_area_struct *vma) mutex_lock(&mapping->i_mmap_mutex); __remove_shared_vm_struct(vma, file, mapping); mutex_unlock(&mapping->i_mmap_mutex); + uprobe_munmap(vma); } } @@ -546,8 +547,14 @@ again: remove_next = 1 + (end > next->vm_end); if (file) { mapping = file->f_mapping; - if (!(vma->vm_flags & VM_NONLINEAR)) + if (!(vma->vm_flags & VM_NONLINEAR)) { root = &mapping->i_mmap; + uprobe_munmap(vma); + + if (adjust_next) + uprobe_munmap(next); + } + mutex_lock(&mapping->i_mmap_mutex); if (insert) { /* @@ -626,6 +633,7 @@ again: remove_next = 1 + (end > next->vm_end); if (remove_next) { if (file) { + uprobe_munmap(next); fput(file); if (next->vm_flags & VM_EXECUTABLE) removed_exe_file_vma(mm); -- cgit v1.2.3 From 62fe8cd50175ca797dcf3d39a7206da6ac0f6c50 Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Fri, 9 Mar 2012 17:53:53 +0900 Subject: mfd: Add s5m regulator operation mode This patch add variables for opmode of s5m series. S5M series have 4 operation modes. Off mode is always regulator off mode. On mode is always regulator on mode. Lowpower mode is that regualtor operate in low-power. Suspend mode is that regulator operation depends on AP suspend mode. Signed-off-by: Sangbeom Kim Signed-off-by: Mark Brown --- include/linux/mfd/s5m87xx/s5m-core.h | 1 + include/linux/mfd/s5m87xx/s5m-pmic.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mfd/s5m87xx/s5m-core.h b/include/linux/mfd/s5m87xx/s5m-core.h index a7480b57f92d..21603b42f22f 100644 --- a/include/linux/mfd/s5m87xx/s5m-core.h +++ b/include/linux/mfd/s5m87xx/s5m-core.h @@ -335,6 +335,7 @@ extern int s5m_reg_update(struct s5m87xx_dev *s5m87xx, u8 reg, u8 val, u8 mask); struct s5m_platform_data { struct s5m_regulator_data *regulators; + struct s5m_opmode_data *opmode; int device_type; int num_regulators; diff --git a/include/linux/mfd/s5m87xx/s5m-pmic.h b/include/linux/mfd/s5m87xx/s5m-pmic.h index a72a5d27e62e..7c719f20f58a 100644 --- a/include/linux/mfd/s5m87xx/s5m-pmic.h +++ b/include/linux/mfd/s5m87xx/s5m-pmic.h @@ -58,6 +58,8 @@ enum s5m8767_regulators { S5M8767_REG_MAX, }; +#define S5M8767_ENCTRL_SHIFT 6 + /* S5M8763 regulator ids */ enum s5m8763_regulators { S5M8763_LDO1, @@ -97,4 +99,31 @@ struct s5m_regulator_data { struct regulator_init_data *initdata; }; +/* + * s5m_opmode_data - regulator operation mode data + * @id: regulator id + * @mode: regulator operation mode + */ +struct s5m_opmode_data { + int id; + int mode; +}; + +/* + * s5m regulator operation mode + * S5M_OPMODE_OFF Regulator always OFF + * S5M_OPMODE_ON Regulator always ON + * S5M_OPMODE_LOWPOWER Regulator is on in low-power mode + * S5M_OPMODE_SUSPEND Regulator is changed by PWREN pin + * If PWREN is high, regulator is on + * If PWREN is low, regulator is off + */ + +enum s5m_opmode { + S5M_OPMODE_OFF, + S5M_OPMODE_ON, + S5M_OPMODE_LOWPOWER, + S5M_OPMODE_SUSPEND, +}; + #endif /* __LINUX_MFD_S5M_PMIC_H */ -- cgit v1.2.3 From 452534e50780697a7e1d3cf87cdfdd2b5a0d3c6b Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 22 Mar 2012 18:34:09 +0530 Subject: regulator: Add TPS65090 regulator driver Add TPS65090 regulator driver TPS65090 PMIC from TI consists of 3 step down converters, 2 always on LDOs and 7 current limited load switches. The output voltages are ON/OFF controllable and are meant to supply power to the components on target board. Signed-off-by: Venu Byravarasu Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/tps65090-regulator.c | 197 +++++++++++++++++++++++++++ include/linux/regulator/tps65090-regulator.h | 50 +++++++ 4 files changed, 255 insertions(+) create mode 100644 drivers/regulator/tps65090-regulator.c create mode 100644 include/linux/regulator/tps65090-regulator.h (limited to 'include/linux') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 36db5a441eba..ed37125b8877 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -294,6 +294,13 @@ config REGULATOR_TPS6507X three step-down converters and two general-purpose LDO voltage regulators. It supports TI's software based Class-2 SmartReflex implementation. +config REGULATOR_TPS65090 + tristate "TI TPS65090 Power regulator" + depends on MFD_TPS65090 + help + This driver provides support for the voltage regulators on the + TI TPS65090 PMIC. + config REGULATOR_TPS65217 tristate "TI TPS65217 Power regulators" depends on MFD_TPS65217 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 94b52745e957..2cc91d1201db 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o +obj-$(CONFIG_REGULATOR_TPS65090) += tps65090-regulator.o obj-$(CONFIG_REGULATOR_TPS65217) += tps65217-regulator.o obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c new file mode 100644 index 000000000000..6c28e3a3f664 --- /dev/null +++ b/drivers/regulator/tps65090-regulator.c @@ -0,0 +1,197 @@ +/* + * Regulator driver for tps65090 power management chip. + * + * Copyright (c) 2012, 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 + +struct tps65090_regulator { + int id; + /* Regulator register address.*/ + u8 reg_en_reg; + u8 en_bit; + + /* used by regulator core */ + struct regulator_desc desc; + + /* Device */ + struct device *dev; +}; + +static inline struct device *to_tps65090_dev(struct regulator_dev *rdev) +{ + return rdev_get_dev(rdev)->parent->parent; +} + +static int tps65090_reg_is_enabled(struct regulator_dev *rdev) +{ + struct tps65090_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps65090_dev(rdev); + uint8_t control; + int ret; + + ret = tps65090_read(parent, ri->reg_en_reg, &control); + if (ret < 0) { + dev_err(&rdev->dev, "Error in reading reg 0x%x\n", + ri->reg_en_reg); + return ret; + } + return (((control >> ri->en_bit) & 1) == 1); +} + +static int tps65090_reg_enable(struct regulator_dev *rdev) +{ + struct tps65090_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps65090_dev(rdev); + int ret; + + ret = tps65090_set_bits(parent, ri->reg_en_reg, ri->en_bit); + if (ret < 0) + dev_err(&rdev->dev, "Error in updating reg 0x%x\n", + ri->reg_en_reg); + return ret; +} + +static int tps65090_reg_disable(struct regulator_dev *rdev) +{ + struct tps65090_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps65090_dev(rdev); + int ret; + + ret = tps65090_clr_bits(parent, ri->reg_en_reg, ri->en_bit); + if (ret < 0) + dev_err(&rdev->dev, "Error in updating reg 0x%x\n", + ri->reg_en_reg); + + return ret; +} + +static struct regulator_ops tps65090_ops = { + .enable = tps65090_reg_enable, + .disable = tps65090_reg_disable, + .is_enabled = tps65090_reg_is_enabled, +}; + +#define tps65090_REG(_id, _en_reg, _en_bit, _ops) \ +{ \ + .reg_en_reg = _en_reg, \ + .en_bit = _en_bit, \ + .id = TPS65090_ID_##_id, \ + .desc = { \ + .name = tps65090_rails(_id), \ + .id = TPS65090_ID_##_id, \ + .ops = &_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ +} + +static struct tps65090_regulator TPS65090_regulator[] = { + tps65090_REG(DCDC1, 12, 0, tps65090_ops), + tps65090_REG(DCDC2, 13, 0, tps65090_ops), + tps65090_REG(DCDC3, 14, 0, tps65090_ops), + tps65090_REG(FET1, 15, 0, tps65090_ops), + tps65090_REG(FET2, 16, 0, tps65090_ops), + tps65090_REG(FET3, 17, 0, tps65090_ops), + tps65090_REG(FET4, 18, 0, tps65090_ops), + tps65090_REG(FET5, 19, 0, tps65090_ops), + tps65090_REG(FET6, 20, 0, tps65090_ops), + tps65090_REG(FET7, 21, 0, tps65090_ops), +}; + +static inline struct tps65090_regulator *find_regulator_info(int id) +{ + struct tps65090_regulator *ri; + int i; + + for (i = 0; i < ARRAY_SIZE(TPS65090_regulator); i++) { + ri = &TPS65090_regulator[i]; + if (ri->desc.id == id) + return ri; + } + return NULL; +} + +static int __devinit tps65090_regulator_probe(struct platform_device *pdev) +{ + struct tps65090_regulator *ri = NULL; + struct regulator_dev *rdev; + struct tps65090_regulator_platform_data *tps_pdata; + int id = pdev->id; + + dev_dbg(&pdev->dev, "Probing regulator %d\n", id); + + ri = find_regulator_info(id); + if (ri == NULL) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + return -EINVAL; + } + tps_pdata = pdev->dev.platform_data; + ri->dev = &pdev->dev; + + rdev = regulator_register(&ri->desc, &pdev->dev, + &tps_pdata->regulator, ri, NULL); + if (IS_ERR_OR_NULL(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc.name); + return PTR_ERR(rdev); + } + + platform_set_drvdata(pdev, rdev); + return 0; +} + +static int __devexit tps65090_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + return 0; +} + +static struct platform_driver tps65090_regulator_driver = { + .driver = { + .name = "tps65090-regulator", + .owner = THIS_MODULE, + }, + .probe = tps65090_regulator_probe, + .remove = __devexit_p(tps65090_regulator_remove), +}; + +static int __init tps65090_regulator_init(void) +{ + return platform_driver_register(&tps65090_regulator_driver); +} +subsys_initcall(tps65090_regulator_init); + +static void __exit tps65090_regulator_exit(void) +{ + platform_driver_unregister(&tps65090_regulator_driver); +} +module_exit(tps65090_regulator_exit); + +MODULE_DESCRIPTION("tps65090 regulator driver"); +MODULE_AUTHOR("Venu Byravarasu "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/regulator/tps65090-regulator.h b/include/linux/regulator/tps65090-regulator.h new file mode 100644 index 000000000000..0fa04b64db3e --- /dev/null +++ b/include/linux/regulator/tps65090-regulator.h @@ -0,0 +1,50 @@ +/* + * Regulator driver interface for TI TPS65090 PMIC family + * + * Copyright (c) 2012, 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 . + */ + +#ifndef __REGULATOR_TPS65090_H +#define __REGULATOR_TPS65090_H + +#include + +#define tps65090_rails(_name) "tps65090_"#_name + +enum { + TPS65090_ID_DCDC1, + TPS65090_ID_DCDC2, + TPS65090_ID_DCDC3, + TPS65090_ID_FET1, + TPS65090_ID_FET2, + TPS65090_ID_FET3, + TPS65090_ID_FET4, + TPS65090_ID_FET5, + TPS65090_ID_FET6, + TPS65090_ID_FET7, +}; + +/* + * struct tps65090_regulator_platform_data + * + * @regulator: The regulator init data. + * @slew_rate_uV_per_us: Slew rate microvolt per microsec. + */ + +struct tps65090_regulator_platform_data { + struct regulator_init_data regulator; +}; + +#endif /* __REGULATOR_TPS65090_H */ -- cgit v1.2.3 From b0ca5a84fc3ad8f75bb2b7ac3dc6a77151cd3906 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 1 Apr 2012 12:09:54 -0700 Subject: cgroup: build list of all cgroups under a given cgroupfs_root Build a list of all cgroups anchored at cgroupfs_root->allcg_list and going through cgroup->allcg_node. The list is protected by cgroup_mutex and will be used to improve cgroup file handling. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 2 ++ kernel/cgroup.c | 10 ++++++++++ 2 files changed, 12 insertions(+) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 5a85b3415c1b..ad2a14680b7f 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -191,6 +191,8 @@ struct cgroup { */ struct list_head css_sets; + struct list_head allcg_node; /* cgroupfs_root->allcg_list */ + /* * Linked list running through all cgroups that can * potentially be reaped by the release agent. Protected by diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 8f72853131d3..db4319c030d0 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -127,6 +127,9 @@ struct cgroupfs_root { /* A list running through the active hierarchies */ struct list_head root_list; + /* All cgroups on this root, cgroup_mutex protected */ + struct list_head allcg_list; + /* Hierarchy-specific flags */ unsigned long flags; @@ -1350,11 +1353,14 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) static void init_cgroup_root(struct cgroupfs_root *root) { struct cgroup *cgrp = &root->top_cgroup; + INIT_LIST_HEAD(&root->subsys_list); INIT_LIST_HEAD(&root->root_list); + INIT_LIST_HEAD(&root->allcg_list); root->number_of_cgroups = 1; cgrp->root = root; cgrp->top_cgroup = cgrp; + list_add_tail(&cgrp->allcg_node, &root->allcg_list); init_cgroup_housekeeping(cgrp); } @@ -3790,6 +3796,8 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, /* The cgroup directory was pre-locked for us */ BUG_ON(!mutex_is_locked(&cgrp->dentry->d_inode->i_mutex)); + list_add_tail(&cgrp->allcg_node, &root->allcg_list); + err = cgroup_populate_dir(cgrp); /* If err < 0, we have a half-filled directory - oh well ;) */ @@ -3998,6 +4006,8 @@ again: list_del_init(&cgrp->sibling); cgroup_unlock_hierarchy(cgrp->root); + list_del_init(&cgrp->allcg_node); + d = dget(cgrp->dentry); cgroup_d_remove_dir(d); -- cgit v1.2.3 From 8e3f6541d45e1a002801e56a19530a90f775deba Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 1 Apr 2012 12:09:55 -0700 Subject: cgroup: implement cgroup_add_cftypes() and friends Currently, cgroup directories are populated by subsys->populate() callback explicitly creating files on each cgroup creation. This level of flexibility isn't needed or desirable. It provides largely unused flexibility which call for abuses while severely limiting what the core layer can do through the lack of structure and conventions. Per each cgroup file type, the only distinction that cgroup users is making is whether a cgroup is root or not, which can easily be expressed with flags. This patch introduces cgroup_add_cftypes(). These deal with cftypes instead of individual files - controllers indicate that certain types of files exist for certain subsystem. Newly added CFTYPE_*_ON_ROOT flags indicate whether a cftype should be excluded or created only on the root cgroup. cgroup_add_cftypes() can be called any time whether the target subsystem is currently attached or not. cgroup core will create files on the existing cgroups as necessary. Also, cgroup_subsys->base_cftypes is added to ease registration of the base files for the subsystem. If non-NULL on subsys init, the cftypes pointed to by ->base_cftypes are automatically registered on subsys init / load. Further patches will convert the existing users and remove the file based interface. Note that this interface allows dynamic addition of files to an active controller. This will be used for sub-controller modularity and unified hierarchy in the longer term. This patch implements the new mechanism but doesn't apply it to any user. v2: replaced DECLARE_CGROUP_CFTYPES[_COND]() with cgroup_subsys->base_cftypes, which works better for cgroup_subsys which is loaded as module. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 33 ++++++++++++- kernel/cgroup.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 162 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index ad2a14680b7f..af6211c7a42b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -192,6 +192,7 @@ struct cgroup { struct list_head css_sets; struct list_head allcg_node; /* cgroupfs_root->allcg_list */ + struct list_head cft_q_node; /* used during cftype add/rm */ /* * Linked list running through all cgroups that can @@ -277,11 +278,17 @@ struct cgroup_map_cb { * - the 'cftype' of the file is file->f_dentry->d_fsdata */ -#define MAX_CFTYPE_NAME 64 +/* cftype->flags */ +#define CFTYPE_ONLY_ON_ROOT (1U << 0) /* only create on root cg */ +#define CFTYPE_NOT_ON_ROOT (1U << 1) /* don't create onp root cg */ + +#define MAX_CFTYPE_NAME 64 + struct cftype { /* * By convention, the name should begin with the name of the - * subsystem, followed by a period + * subsystem, followed by a period. Zero length string indicates + * end of cftype array. */ char name[MAX_CFTYPE_NAME]; int private; @@ -297,6 +304,9 @@ struct cftype { */ size_t max_write_len; + /* CFTYPE_* flags */ + unsigned int flags; + int (*open)(struct inode *inode, struct file *file); ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, struct file *file, @@ -375,6 +385,16 @@ struct cftype { struct eventfd_ctx *eventfd); }; +/* + * cftype_sets describe cftypes belonging to a subsystem and are chained at + * cgroup_subsys->cftsets. Each cftset points to an array of cftypes + * terminated by zero length name. + */ +struct cftype_set { + struct list_head node; /* chained at subsys->cftsets */ + const struct cftype *cfts; +}; + struct cgroup_scanner { struct cgroup *cg; int (*test_task)(struct task_struct *p, struct cgroup_scanner *scan); @@ -400,6 +420,8 @@ int cgroup_add_files(struct cgroup *cgrp, const struct cftype cft[], int count); +int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts); + int cgroup_is_removed(const struct cgroup *cgrp); int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen); @@ -502,6 +524,13 @@ struct cgroup_subsys { struct idr idr; spinlock_t id_lock; + /* list of cftype_sets */ + struct list_head cftsets; + + /* base cftypes, automatically [de]registered with subsys itself */ + struct cftype *base_cftypes; + struct cftype_set base_cftset; + /* should be defined only by modular subsystems */ struct module *module; }; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index db4319c030d0..df8fb82ef350 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2623,8 +2623,14 @@ int cgroup_add_file(struct cgroup *cgrp, struct dentry *dentry; int error; umode_t mode; - char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 }; + + /* does @cft->flags tell us to skip creation on @cgrp? */ + if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgrp->parent) + return 0; + if ((cft->flags & CFTYPE_ONLY_ON_ROOT) && cgrp->parent) + return 0; + if (subsys && !test_bit(ROOT_NOPREFIX, &cgrp->root->flags)) { strcpy(name, subsys->name); strcat(name, "."); @@ -2660,6 +2666,95 @@ int cgroup_add_files(struct cgroup *cgrp, } EXPORT_SYMBOL_GPL(cgroup_add_files); +static DEFINE_MUTEX(cgroup_cft_mutex); + +static void cgroup_cfts_prepare(void) + __acquires(&cgroup_cft_mutex) __acquires(&cgroup_mutex) +{ + /* + * Thanks to the entanglement with vfs inode locking, we can't walk + * the existing cgroups under cgroup_mutex and create files. + * Instead, we increment reference on all cgroups and build list of + * them using @cgrp->cft_q_node. Grab cgroup_cft_mutex to ensure + * exclusive access to the field. + */ + mutex_lock(&cgroup_cft_mutex); + mutex_lock(&cgroup_mutex); +} + +static void cgroup_cfts_commit(struct cgroup_subsys *ss, + const struct cftype *cfts) + __releases(&cgroup_mutex) __releases(&cgroup_cft_mutex) +{ + LIST_HEAD(pending); + struct cgroup *cgrp, *n; + int count = 0; + + while (cfts[count].name[0] != '\0') + count++; + + /* %NULL @cfts indicates abort and don't bother if @ss isn't attached */ + if (cfts && ss->root != &rootnode) { + list_for_each_entry(cgrp, &ss->root->allcg_list, allcg_node) { + dget(cgrp->dentry); + list_add_tail(&cgrp->cft_q_node, &pending); + } + } + + mutex_unlock(&cgroup_mutex); + + /* + * All new cgroups will see @cfts update on @ss->cftsets. Add/rm + * files for all cgroups which were created before. + */ + list_for_each_entry_safe(cgrp, n, &pending, cft_q_node) { + struct inode *inode = cgrp->dentry->d_inode; + + mutex_lock(&inode->i_mutex); + mutex_lock(&cgroup_mutex); + if (!cgroup_is_removed(cgrp)) + cgroup_add_files(cgrp, ss, cfts, count); + mutex_unlock(&cgroup_mutex); + mutex_unlock(&inode->i_mutex); + + list_del_init(&cgrp->cft_q_node); + dput(cgrp->dentry); + } + + mutex_unlock(&cgroup_cft_mutex); +} + +/** + * cgroup_add_cftypes - add an array of cftypes to a subsystem + * @ss: target cgroup subsystem + * @cfts: zero-length name terminated array of cftypes + * + * Register @cfts to @ss. Files described by @cfts are created for all + * existing cgroups to which @ss is attached and all future cgroups will + * have them too. This function can be called anytime whether @ss is + * attached or not. + * + * Returns 0 on successful registration, -errno on failure. Note that this + * function currently returns 0 as long as @cfts registration is successful + * even if some file creation attempts on existing cgroups fail. + */ +int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts) +{ + struct cftype_set *set; + + set = kzalloc(sizeof(*set), GFP_KERNEL); + if (!set) + return -ENOMEM; + + cgroup_cfts_prepare(); + set->cfts = cfts; + list_add_tail(&set->node, &ss->cftsets); + cgroup_cfts_commit(ss, cfts); + + return 0; +} +EXPORT_SYMBOL_GPL(cgroup_add_cftypes); + /** * cgroup_task_count - count the number of tasks in a cgroup. * @cgrp: the cgroup in question @@ -3660,10 +3755,25 @@ static int cgroup_populate_dir(struct cgroup *cgrp) return err; } + /* process cftsets of each subsystem */ for_each_subsys(cgrp->root, ss) { + struct cftype_set *set; + if (ss->populate && (err = ss->populate(ss, cgrp)) < 0) return err; + + list_for_each_entry(set, &ss->cftsets, node) { + const struct cftype *cft; + + for (cft = set->cfts; cft->name[0] != '\0'; cft++) { + err = cgroup_add_file(cgrp, ss, cft); + if (err) + pr_warning("cgroup_populate_dir: failed to create %s, err=%d\n", + cft->name, err); + } + } } + /* This cgroup is ready now */ for_each_subsys(cgrp->root, ss) { struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; @@ -4034,12 +4144,29 @@ again: return 0; } +static void __init_or_module cgroup_init_cftsets(struct cgroup_subsys *ss) +{ + INIT_LIST_HEAD(&ss->cftsets); + + /* + * base_cftset is embedded in subsys itself, no need to worry about + * deregistration. + */ + if (ss->base_cftypes) { + ss->base_cftset.cfts = ss->base_cftypes; + list_add_tail(&ss->base_cftset.node, &ss->cftsets); + } +} + static void __init cgroup_init_subsys(struct cgroup_subsys *ss) { struct cgroup_subsys_state *css; printk(KERN_INFO "Initializing cgroup subsys %s\n", ss->name); + /* init base cftset */ + cgroup_init_cftsets(ss); + /* Create the top cgroup state for this subsystem */ list_add(&ss->sibling, &rootnode.subsys_list); ss->root = &rootnode; @@ -4109,6 +4236,9 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss) return 0; } + /* init base cftset */ + cgroup_init_cftsets(ss); + /* * need to register a subsys id before anything else - for example, * init_cgroup_css needs it. -- cgit v1.2.3 From db0416b64977cb0f382175608286cc80c7573e38 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 1 Apr 2012 12:09:55 -0700 Subject: cgroup: remove cgroup_add_file[s]() No controller is using cgroup_add_files[s](). Unexport them, and convert cgroup_add_files() to handle NULL entry terminated array instead of taking count explicitly and continue creation on failure for internal use. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 16 ---------------- kernel/cgroup.c | 51 ++++++++++++++++++++------------------------------ 2 files changed, 20 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index af6211c7a42b..7a3d96755114 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -404,22 +404,6 @@ struct cgroup_scanner { void *data; }; -/* - * Add a new file to the given cgroup directory. Should only be - * called by subsystems from within a populate() method - */ -int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys, - const struct cftype *cft); - -/* - * Add a set of new files to the given cgroup directory. Should - * only be called by subsystems from within a populate() method - */ -int cgroup_add_files(struct cgroup *cgrp, - struct cgroup_subsys *subsys, - const struct cftype cft[], - int count); - int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts); int cgroup_is_removed(const struct cgroup *cgrp); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 835ae29e4ea2..fb71a930ec8d 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2615,9 +2615,8 @@ static umode_t cgroup_file_mode(const struct cftype *cft) return mode; } -int cgroup_add_file(struct cgroup *cgrp, - struct cgroup_subsys *subsys, - const struct cftype *cft) +static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys, + const struct cftype *cft) { struct dentry *dir = cgrp->dentry; struct dentry *dentry; @@ -2649,22 +2648,23 @@ int cgroup_add_file(struct cgroup *cgrp, error = PTR_ERR(dentry); return error; } -EXPORT_SYMBOL_GPL(cgroup_add_file); -int cgroup_add_files(struct cgroup *cgrp, - struct cgroup_subsys *subsys, - const struct cftype cft[], - int count) +static int cgroup_add_files(struct cgroup *cgrp, struct cgroup_subsys *subsys, + const struct cftype cfts[]) { - int i, err; - for (i = 0; i < count; i++) { - err = cgroup_add_file(cgrp, subsys, &cft[i]); - if (err) - return err; + const struct cftype *cft; + int err, ret = 0; + + for (cft = cfts; cft->name[0] != '\0'; cft++) { + err = cgroup_add_file(cgrp, subsys, cft); + if (err) { + pr_warning("cgroup_add_files: failed to create %s, err=%d\n", + cft->name, err); + ret = err; + } } - return 0; + return ret; } -EXPORT_SYMBOL_GPL(cgroup_add_files); static DEFINE_MUTEX(cgroup_cft_mutex); @@ -2688,10 +2688,6 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss, { LIST_HEAD(pending); struct cgroup *cgrp, *n; - int count = 0; - - while (cfts[count].name[0] != '\0') - count++; /* %NULL @cfts indicates abort and don't bother if @ss isn't attached */ if (cfts && ss->root != &rootnode) { @@ -2713,7 +2709,7 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss, mutex_lock(&inode->i_mutex); mutex_lock(&cgroup_mutex); if (!cgroup_is_removed(cgrp)) - cgroup_add_files(cgrp, ss, cfts, count); + cgroup_add_files(cgrp, ss, cfts); mutex_unlock(&cgroup_mutex); mutex_unlock(&inode->i_mutex); @@ -3739,6 +3735,7 @@ static struct cftype files[] = { .write_string = cgroup_release_agent_write, .max_write_len = PATH_MAX, }, + { } /* terminate */ }; static int cgroup_populate_dir(struct cgroup *cgrp) @@ -3746,7 +3743,7 @@ static int cgroup_populate_dir(struct cgroup *cgrp) int err; struct cgroup_subsys *ss; - err = cgroup_add_files(cgrp, NULL, files, ARRAY_SIZE(files)); + err = cgroup_add_files(cgrp, NULL, files); if (err < 0) return err; @@ -3757,16 +3754,8 @@ static int cgroup_populate_dir(struct cgroup *cgrp) if (ss->populate && (err = ss->populate(ss, cgrp)) < 0) return err; - list_for_each_entry(set, &ss->cftsets, node) { - const struct cftype *cft; - - for (cft = set->cfts; cft->name[0] != '\0'; cft++) { - err = cgroup_add_file(cgrp, ss, cft); - if (err) - pr_warning("cgroup_populate_dir: failed to create %s, err=%d\n", - cft->name, err); - } - } + list_for_each_entry(set, &ss->cftsets, node) + cgroup_add_files(cgrp, ss, set->cfts); } /* This cgroup is ready now */ -- cgit v1.2.3 From 05ef1d7c4a5b6d09cadd2b8e9b3c395363d1a89c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 1 Apr 2012 12:09:56 -0700 Subject: cgroup: introduce struct cfent This patch adds cfent (cgroup file entry) which is the association between a cgroup and a file. This is in-cgroup representation of files under a cgroup directory. This simplifies walking walking cgroup files and thus cgroup_clear_directory(), which is now implemented in two parts - cgroup_rm_file() and a loop around it. cgroup_rm_file() will be used to implement cftype removal and cfent is scheduled to serve cgroup specific per-file data (e.g. for sysfs-like "sever" semantics). v2: - cfe was freed from cgroup_rm_file() which led to use-after-free if the file had openers at the time of removal. Moved to cgroup_diput(). - cgroup_clear_directory() triggered WARN_ON_ONCE() if d_subdirs wasn't empty after removing all files. This triggered spuriously if some files were open during directory clearing. Removed. v3: - In cgroup_diput(), WARN_ONCE(!list_empty(&cfe->node)) could be spuriously triggered for root cgroups because they don't go through cgroup_clear_directory() on unmount. Don't trigger WARN for root cgroups. Signed-off-by: Tejun Heo Acked-by: Li Zefan Cc: Glauber Costa --- include/linux/cgroup.h | 1 + kernel/cgroup.c | 113 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 78 insertions(+), 36 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 7a3d96755114..87b034ed0c31 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -175,6 +175,7 @@ struct cgroup { */ struct list_head sibling; /* my parent's children */ struct list_head children; /* my children */ + struct list_head files; /* my files */ struct cgroup *parent; /* my parent */ struct dentry __rcu *dentry; /* cgroup fs entry, RCU protected */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 2ab29eb381b7..7d5d1c927d9d 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -147,6 +147,15 @@ struct cgroupfs_root { */ static struct cgroupfs_root rootnode; +/* + * cgroupfs file entry, pointed to from leaf dentry->d_fsdata. + */ +struct cfent { + struct list_head node; + struct dentry *dentry; + struct cftype *type; +}; + /* * CSS ID -- ID per subsys's Cgroup Subsys State(CSS). used only when * cgroup_subsys->use_id != 0. @@ -287,11 +296,16 @@ static inline struct cgroup *__d_cgrp(struct dentry *dentry) return dentry->d_fsdata; } -static inline struct cftype *__d_cft(struct dentry *dentry) +static inline struct cfent *__d_cfe(struct dentry *dentry) { return dentry->d_fsdata; } +static inline struct cftype *__d_cft(struct dentry *dentry) +{ + return __d_cfe(dentry)->type; +} + /* the list of cgroups eligible for automatic release. Protected by * release_list_lock */ static LIST_HEAD(release_list); @@ -877,6 +891,14 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode) BUG_ON(!list_empty(&cgrp->pidlists)); kfree_rcu(cgrp, rcu_head); + } else { + struct cfent *cfe = __d_cfe(dentry); + struct cgroup *cgrp = dentry->d_parent->d_fsdata; + + WARN_ONCE(!list_empty(&cfe->node) && + cgrp != &cgrp->root->top_cgroup, + "cfe still linked for %s\n", cfe->type->name); + kfree(cfe); } iput(inode); } @@ -895,34 +917,36 @@ static void remove_dir(struct dentry *d) dput(parent); } -static void cgroup_clear_directory(struct dentry *dentry) -{ - struct list_head *node; - - BUG_ON(!mutex_is_locked(&dentry->d_inode->i_mutex)); - spin_lock(&dentry->d_lock); - node = dentry->d_subdirs.next; - while (node != &dentry->d_subdirs) { - struct dentry *d = list_entry(node, struct dentry, d_u.d_child); - - spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); - list_del_init(node); - if (d->d_inode) { - /* This should never be called on a cgroup - * directory with child cgroups */ - BUG_ON(d->d_inode->i_mode & S_IFDIR); - dget_dlock(d); - spin_unlock(&d->d_lock); - spin_unlock(&dentry->d_lock); - d_delete(d); - simple_unlink(dentry->d_inode, d); - dput(d); - spin_lock(&dentry->d_lock); - } else - spin_unlock(&d->d_lock); - node = dentry->d_subdirs.next; +static int cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft) +{ + struct cfent *cfe; + + lockdep_assert_held(&cgrp->dentry->d_inode->i_mutex); + lockdep_assert_held(&cgroup_mutex); + + list_for_each_entry(cfe, &cgrp->files, node) { + struct dentry *d = cfe->dentry; + + if (cft && cfe->type != cft) + continue; + + dget(d); + d_delete(d); + simple_unlink(d->d_inode, d); + list_del_init(&cfe->node); + dput(d); + + return 0; } - spin_unlock(&dentry->d_lock); + return -ENOENT; +} + +static void cgroup_clear_directory(struct dentry *dir) +{ + struct cgroup *cgrp = __d_cgrp(dir); + + while (!list_empty(&cgrp->files)) + cgroup_rm_file(cgrp, NULL); } /* @@ -1352,6 +1376,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) { INIT_LIST_HEAD(&cgrp->sibling); INIT_LIST_HEAD(&cgrp->children); + INIT_LIST_HEAD(&cgrp->files); INIT_LIST_HEAD(&cgrp->css_sets); INIT_LIST_HEAD(&cgrp->release_list); INIT_LIST_HEAD(&cgrp->pidlists); @@ -2619,7 +2644,9 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys, const struct cftype *cft) { struct dentry *dir = cgrp->dentry; + struct cgroup *parent = __d_cgrp(dir); struct dentry *dentry; + struct cfent *cfe; int error; umode_t mode; char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 }; @@ -2635,17 +2662,31 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys, strcat(name, "."); } strcat(name, cft->name); + BUG_ON(!mutex_is_locked(&dir->d_inode->i_mutex)); + + cfe = kzalloc(sizeof(*cfe), GFP_KERNEL); + if (!cfe) + return -ENOMEM; + dentry = lookup_one_len(name, dir, strlen(name)); - if (!IS_ERR(dentry)) { - mode = cgroup_file_mode(cft); - error = cgroup_create_file(dentry, mode | S_IFREG, - cgrp->root->sb); - if (!error) - dentry->d_fsdata = (void *)cft; - dput(dentry); - } else + if (IS_ERR(dentry)) { error = PTR_ERR(dentry); + goto out; + } + + mode = cgroup_file_mode(cft); + error = cgroup_create_file(dentry, mode | S_IFREG, cgrp->root->sb); + if (!error) { + cfe->type = (void *)cft; + cfe->dentry = dentry; + dentry->d_fsdata = cfe; + list_add_tail(&cfe->node, &parent->files); + cfe = NULL; + } + dput(dentry); +out: + kfree(cfe); return error; } -- cgit v1.2.3 From 79578621b4847afdef48d19a28d00e3b188c37e1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 1 Apr 2012 12:09:56 -0700 Subject: cgroup: implement cgroup_rm_cftypes() Implement cgroup_rm_cftypes() which removes an array of cftypes from a subsystem. It can be called whether the target subsys is attached or not. cgroup core will remove the specified file from all existing cgroups. This will be used to improve sub-subsys modularity and will be helpful for unified hierarchy. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 1 + kernel/cgroup.c | 54 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 87b034ed0c31..028478c6e0c5 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -406,6 +406,7 @@ struct cgroup_scanner { }; int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts); +int cgroup_rm_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts); int cgroup_is_removed(const struct cgroup *cgrp); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 7d5d1c927d9d..21bba7722350 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2690,17 +2690,20 @@ out: return error; } -static int cgroup_add_files(struct cgroup *cgrp, struct cgroup_subsys *subsys, - const struct cftype cfts[]) +static int cgroup_addrm_files(struct cgroup *cgrp, struct cgroup_subsys *subsys, + const struct cftype cfts[], bool is_add) { const struct cftype *cft; int err, ret = 0; for (cft = cfts; cft->name[0] != '\0'; cft++) { - err = cgroup_add_file(cgrp, subsys, cft); + if (is_add) + err = cgroup_add_file(cgrp, subsys, cft); + else + err = cgroup_rm_file(cgrp, cft); if (err) { - pr_warning("cgroup_add_files: failed to create %s, err=%d\n", - cft->name, err); + pr_warning("cgroup_addrm_files: failed to %s %s, err=%d\n", + is_add ? "add" : "remove", cft->name, err); ret = err; } } @@ -2724,7 +2727,7 @@ static void cgroup_cfts_prepare(void) } static void cgroup_cfts_commit(struct cgroup_subsys *ss, - const struct cftype *cfts) + const struct cftype *cfts, bool is_add) __releases(&cgroup_mutex) __releases(&cgroup_cft_mutex) { LIST_HEAD(pending); @@ -2750,7 +2753,7 @@ static void cgroup_cfts_commit(struct cgroup_subsys *ss, mutex_lock(&inode->i_mutex); mutex_lock(&cgroup_mutex); if (!cgroup_is_removed(cgrp)) - cgroup_add_files(cgrp, ss, cfts); + cgroup_addrm_files(cgrp, ss, cfts, is_add); mutex_unlock(&cgroup_mutex); mutex_unlock(&inode->i_mutex); @@ -2786,12 +2789,43 @@ int cgroup_add_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts) cgroup_cfts_prepare(); set->cfts = cfts; list_add_tail(&set->node, &ss->cftsets); - cgroup_cfts_commit(ss, cfts); + cgroup_cfts_commit(ss, cfts, true); return 0; } EXPORT_SYMBOL_GPL(cgroup_add_cftypes); +/** + * cgroup_rm_cftypes - remove an array of cftypes from a subsystem + * @ss: target cgroup subsystem + * @cfts: zero-length name terminated array of cftypes + * + * Unregister @cfts from @ss. Files described by @cfts are removed from + * all existing cgroups to which @ss is attached and all future cgroups + * won't have them either. This function can be called anytime whether @ss + * is attached or not. + * + * Returns 0 on successful unregistration, -ENOENT if @cfts is not + * registered with @ss. + */ +int cgroup_rm_cftypes(struct cgroup_subsys *ss, const struct cftype *cfts) +{ + struct cftype_set *set; + + cgroup_cfts_prepare(); + + list_for_each_entry(set, &ss->cftsets, node) { + if (set->cfts == cfts) { + list_del_init(&set->node); + cgroup_cfts_commit(ss, cfts, false); + return 0; + } + } + + cgroup_cfts_commit(ss, NULL, false); + return -ENOENT; +} + /** * cgroup_task_count - count the number of tasks in a cgroup. * @cgrp: the cgroup in question @@ -3784,7 +3818,7 @@ static int cgroup_populate_dir(struct cgroup *cgrp) int err; struct cgroup_subsys *ss; - err = cgroup_add_files(cgrp, NULL, files); + err = cgroup_addrm_files(cgrp, NULL, files, true); if (err < 0) return err; @@ -3796,7 +3830,7 @@ static int cgroup_populate_dir(struct cgroup *cgrp) return err; list_for_each_entry(set, &ss->cftsets, node) - cgroup_add_files(cgrp, ss, set->cfts); + cgroup_addrm_files(cgrp, ss, set->cfts, true); } /* This cgroup is ready now */ -- cgit v1.2.3 From 28b4c27b8e6bb6d7ff2875281a8484f8898a87ef Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 1 Apr 2012 12:09:56 -0700 Subject: cgroup: use negative bias on css->refcnt to block css_tryget() When a cgroup is about to be removed, cgroup_clear_css_refs() is called to check and ensure that there are no active css references. This is currently achieved by dropping the refcnt to zero iff it has only the base ref. If all css refs could be dropped to zero, ref clearing is successful and CSS_REMOVED is set on all css. If not, the base ref is restored. While css ref is zero w/o CSS_REMOVED set, any css_tryget() attempt on it busy loops so that they are atomic w.r.t. the whole css ref clearing. This does work but dropping and re-instating the base ref is somewhat hairy and makes it difficult to add more logic to the put path as there are two of them - the regular css_put() and the reversible base ref clearing. This patch updates css ref clearing such that blocking new css_tryget() and putting the base ref are separate operations. CSS_DEACT_BIAS, defined as INT_MIN, is added to css->refcnt and css_tryget() busy loops while refcnt is negative. After all css refs are deactivated, if they were all one, ref clearing succeeded and CSS_REMOVED is set and the base ref is put using the regular css_put(); otherwise, CSS_DEACT_BIAS is subtracted from the refcnts and the original postive values are restored. css_refcnt() accessor which always returns the unbiased positive reference counts is added and used to simplify refcnt usages. While at it, relocate and reformat comments in cgroup_has_css_refs(). This separates css->refcnt deactivation and putting the base ref, which enables the next patch to make ref clearing optional. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 12 ++--- kernel/cgroup.c | 119 +++++++++++++++++++++++++++++-------------------- 2 files changed, 75 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 028478c6e0c5..be81fafae11f 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -115,16 +115,12 @@ static inline bool css_is_removed(struct cgroup_subsys_state *css) * the css has been destroyed. */ +extern bool __css_tryget(struct cgroup_subsys_state *css); static inline bool css_tryget(struct cgroup_subsys_state *css) { if (test_bit(CSS_ROOT, &css->flags)) return true; - while (!atomic_inc_not_zero(&css->refcnt)) { - if (test_bit(CSS_REMOVED, &css->flags)) - return false; - cpu_relax(); - } - return true; + return __css_tryget(css); } /* @@ -132,11 +128,11 @@ static inline bool css_tryget(struct cgroup_subsys_state *css) * css_get() or css_tryget() */ -extern void __css_put(struct cgroup_subsys_state *css, int count); +extern void __css_put(struct cgroup_subsys_state *css); static inline void css_put(struct cgroup_subsys_state *css) { if (!test_bit(CSS_ROOT, &css->flags)) - __css_put(css, 1); + __css_put(css); } /* bits in struct cgroup flags field */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 21bba7722350..2eade5186604 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -63,6 +63,9 @@ #include +/* css deactivation bias, makes css->refcnt negative to deny new trygets */ +#define CSS_DEACT_BIAS INT_MIN + /* * cgroup_mutex is the master lock. Any modification to cgroup or its * hierarchy must be performed while holding it. @@ -251,6 +254,14 @@ int cgroup_lock_is_held(void) EXPORT_SYMBOL_GPL(cgroup_lock_is_held); +/* the current nr of refs, always >= 0 whether @css is deactivated or not */ +static int css_refcnt(struct cgroup_subsys_state *css) +{ + int v = atomic_read(&css->refcnt); + + return v >= 0 ? v : v - CSS_DEACT_BIAS; +} + /* convenient tests for these bits */ inline int cgroup_is_removed(const struct cgroup *cgrp) { @@ -4006,18 +4017,19 @@ static int cgroup_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) return cgroup_create(c_parent, dentry, mode | S_IFDIR); } +/* + * Check the reference count on each subsystem. Since we already + * established that there are no tasks in the cgroup, if the css refcount + * is also 1, then there should be no outstanding references, so the + * subsystem is safe to destroy. We scan across all subsystems rather than + * using the per-hierarchy linked list of mounted subsystems since we can + * be called via check_for_release() with no synchronization other than + * RCU, and the subsystem linked list isn't RCU-safe. + */ static int cgroup_has_css_refs(struct cgroup *cgrp) { - /* Check the reference count on each subsystem. Since we - * already established that there are no tasks in the - * cgroup, if the css refcount is also 1, then there should - * be no outstanding references, so the subsystem is safe to - * destroy. We scan across all subsystems rather than using - * the per-hierarchy linked list of mounted subsystems since - * we can be called via check_for_release() with no - * synchronization other than RCU, and the subsystem linked - * list isn't RCU-safe */ int i; + /* * We won't need to lock the subsys array, because the subsystems * we're concerned about aren't going anywhere since our cgroup root @@ -4026,17 +4038,21 @@ static int cgroup_has_css_refs(struct cgroup *cgrp) for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) { struct cgroup_subsys *ss = subsys[i]; struct cgroup_subsys_state *css; + /* Skip subsystems not present or not in this hierarchy */ if (ss == NULL || ss->root != cgrp->root) continue; + css = cgrp->subsys[ss->subsys_id]; - /* When called from check_for_release() it's possible + /* + * When called from check_for_release() it's possible * that by this point the cgroup has been removed * and the css deleted. But a false-positive doesn't * matter, since it can only happen if the cgroup * has been deleted and hence no longer needs the - * release agent to be called anyway. */ - if (css && (atomic_read(&css->refcnt) > 1)) + * release agent to be called anyway. + */ + if (css && css_refcnt(css) > 1) return 1; } return 0; @@ -4053,44 +4069,37 @@ static int cgroup_clear_css_refs(struct cgroup *cgrp) struct cgroup_subsys *ss; unsigned long flags; bool failed = false; + local_irq_save(flags); + + /* + * Block new css_tryget() by deactivating refcnt. If all refcnts + * were 1 at the moment of deactivation, we succeeded. + */ for_each_subsys(cgrp->root, ss) { struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; - int refcnt; - while (1) { - /* We can only remove a CSS with a refcnt==1 */ - refcnt = atomic_read(&css->refcnt); - if (refcnt > 1) { - failed = true; - goto done; - } - BUG_ON(!refcnt); - /* - * Drop the refcnt to 0 while we check other - * subsystems. This will cause any racing - * css_tryget() to spin until we set the - * CSS_REMOVED bits or abort - */ - if (atomic_cmpxchg(&css->refcnt, refcnt, 0) == refcnt) - break; - cpu_relax(); - } + + WARN_ON(atomic_read(&css->refcnt) < 0); + atomic_add(CSS_DEACT_BIAS, &css->refcnt); + failed |= css_refcnt(css) != 1; } - done: + + /* + * If succeeded, set REMOVED and put all the base refs; otherwise, + * restore refcnts to positive values. Either way, all in-progress + * css_tryget() will be released. + */ for_each_subsys(cgrp->root, ss) { struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; - if (failed) { - /* - * Restore old refcnt if we previously managed - * to clear it from 1 to 0 - */ - if (!atomic_read(&css->refcnt)) - atomic_set(&css->refcnt, 1); - } else { - /* Commit the fact that the CSS is removed */ + + if (!failed) { set_bit(CSS_REMOVED, &css->flags); + css_put(css); + } else { + atomic_sub(CSS_DEACT_BIAS, &css->refcnt); } } + local_irq_restore(flags); return !failed; } @@ -4887,13 +4896,28 @@ static void check_for_release(struct cgroup *cgrp) } /* Caller must verify that the css is not for root cgroup */ -void __css_put(struct cgroup_subsys_state *css, int count) +bool __css_tryget(struct cgroup_subsys_state *css) +{ + do { + int v = css_refcnt(css); + + if (atomic_cmpxchg(&css->refcnt, v, v + 1) == v) + return true; + cpu_relax(); + } while (!test_bit(CSS_REMOVED, &css->flags)); + + return false; +} +EXPORT_SYMBOL_GPL(__css_tryget); + +/* Caller must verify that the css is not for root cgroup */ +void __css_put(struct cgroup_subsys_state *css) { struct cgroup *cgrp = css->cgroup; - int val; + rcu_read_lock(); - val = atomic_sub_return(count, &css->refcnt); - if (val == 1) { + atomic_dec(&css->refcnt); + if (css_refcnt(css) == 1) { if (notify_on_release(cgrp)) { set_bit(CGRP_RELEASABLE, &cgrp->flags); check_for_release(cgrp); @@ -4901,7 +4925,6 @@ void __css_put(struct cgroup_subsys_state *css, int count) cgroup_wakeup_rmdir_waiter(cgrp); } rcu_read_unlock(); - WARN_ON_ONCE(val < 1); } EXPORT_SYMBOL_GPL(__css_put); @@ -5020,7 +5043,7 @@ unsigned short css_id(struct cgroup_subsys_state *css) * on this or this is under rcu_read_lock(). Once css->id is allocated, * it's unchanged until freed. */ - cssid = rcu_dereference_check(css->id, atomic_read(&css->refcnt)); + cssid = rcu_dereference_check(css->id, css_refcnt(css)); if (cssid) return cssid->id; @@ -5032,7 +5055,7 @@ unsigned short css_depth(struct cgroup_subsys_state *css) { struct css_id *cssid; - cssid = rcu_dereference_check(css->id, atomic_read(&css->refcnt)); + cssid = rcu_dereference_check(css->id, css_refcnt(css)); if (cssid) return cssid->depth; -- cgit v1.2.3 From 48ddbe194623ae089cc0576e60363f2d2e85662a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 1 Apr 2012 12:09:56 -0700 Subject: cgroup: make css->refcnt clearing on cgroup removal optional Currently, cgroup removal tries to drain all css references. If there are active css references, the removal logic waits and retries ->pre_detroy() until either all refs drop to zero or removal is cancelled. This semantics is unusual and adds non-trivial complexity to cgroup core and IMHO is fundamentally misguided in that it couples internal implementation details (references to internal data structure) with externally visible operation (rmdir). To userland, this is a behavior peculiarity which is unnecessary and difficult to expect (css refs is otherwise invisible from userland), and, to policy implementations, this is an unnecessary restriction (e.g. blkcg wants to hold css refs for caching purposes but can't as that becomes visible as rmdir hang). Unfortunately, memcg currently depends on ->pre_destroy() retrials and cgroup removal vetoing and can't be immmediately switched to the new behavior. This patch introduces the new behavior of not waiting for css refs to drain and maintains the old behavior for subsystems which have __DEPRECATED_clear_css_refs set. Once, memcg is updated, we can drop the code paths for the old behavior as proposed in the following patch. Note that the following patch is incorrect in that dput work item is in cgroup and may lose some of dputs when multiples css's are released back-to-back, and __css_put() triggers check_for_release() when refcnt reaches 0 instead of 1; however, it shows what part can be removed. http://thread.gmane.org/gmane.linux.kernel.containers/22559/focus=75251 Note that, in not-too-distant future, cgroup core will start emitting warning messages for subsys which require the old behavior, so please get moving. Signed-off-by: Tejun Heo Acked-by: Li Zefan Cc: Vivek Goyal Cc: Johannes Weiner Cc: Michal Hocko Cc: Balbir Singh Cc: KAMEZAWA Hiroyuki --- include/linux/cgroup.h | 17 ++++++++++++ kernel/cgroup.c | 71 +++++++++++++++++++++++++++++++++++++++++++------- mm/memcontrol.c | 1 + 3 files changed, 80 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index be81fafae11f..565c8034e6c8 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef CONFIG_CGROUPS @@ -76,12 +77,16 @@ struct cgroup_subsys_state { unsigned long flags; /* ID for this css, if possible */ struct css_id __rcu *id; + + /* Used to put @cgroup->dentry on the last css_put() */ + struct work_struct dput_work; }; /* bits in struct cgroup_subsys_state flags field */ enum { CSS_ROOT, /* This CSS is the root of the subsystem */ CSS_REMOVED, /* This CSS is dead */ + CSS_CLEAR_CSS_REFS, /* @ss->__DEPRECATED_clear_css_refs */ }; /* Caller must verify that the css is not for root cgroup */ @@ -480,6 +485,18 @@ struct cgroup_subsys { * (not available in early_init time.) */ bool use_id; + + /* + * If %true, cgroup removal will try to clear css refs by retrying + * ss->pre_destroy() until there's no css ref left. This behavior + * is strictly for backward compatibility and will be removed as + * soon as the current user (memcg) is updated. + * + * If %false, ss->pre_destroy() can't fail and cgroup removal won't + * wait for css refs to drop to zero before proceeding. + */ + bool __DEPRECATED_clear_css_refs; + #define MAX_CGROUP_TYPE_NAMELEN 32 const char *name; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 2eade5186604..2905977e0f33 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -854,12 +854,17 @@ static int cgroup_call_pre_destroy(struct cgroup *cgrp) struct cgroup_subsys *ss; int ret = 0; - for_each_subsys(cgrp->root, ss) - if (ss->pre_destroy) { - ret = ss->pre_destroy(cgrp); - if (ret) - break; + for_each_subsys(cgrp->root, ss) { + if (!ss->pre_destroy) + continue; + + ret = ss->pre_destroy(cgrp); + if (ret) { + /* ->pre_destroy() failure is being deprecated */ + WARN_ON_ONCE(!ss->__DEPRECATED_clear_css_refs); + break; } + } return ret; } @@ -3859,6 +3864,14 @@ static int cgroup_populate_dir(struct cgroup *cgrp) return 0; } +static void css_dput_fn(struct work_struct *work) +{ + struct cgroup_subsys_state *css = + container_of(work, struct cgroup_subsys_state, dput_work); + + dput(css->cgroup->dentry); +} + static void init_cgroup_css(struct cgroup_subsys_state *css, struct cgroup_subsys *ss, struct cgroup *cgrp) @@ -3871,6 +3884,16 @@ static void init_cgroup_css(struct cgroup_subsys_state *css, set_bit(CSS_ROOT, &css->flags); BUG_ON(cgrp->subsys[ss->subsys_id]); cgrp->subsys[ss->subsys_id] = css; + + /* + * If !clear_css_refs, css holds an extra ref to @cgrp->dentry + * which is put on the last css_put(). dput() requires process + * context, which css_put() may be called without. @css->dput_work + * will be used to invoke dput() asynchronously from css_put(). + */ + INIT_WORK(&css->dput_work, css_dput_fn); + if (ss->__DEPRECATED_clear_css_refs) + set_bit(CSS_CLEAR_CSS_REFS, &css->flags); } static void cgroup_lock_hierarchy(struct cgroupfs_root *root) @@ -3973,6 +3996,11 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, if (err < 0) goto err_remove; + /* If !clear_css_refs, each css holds a ref to the cgroup's dentry */ + for_each_subsys(root, ss) + if (!ss->__DEPRECATED_clear_css_refs) + dget(dentry); + /* The cgroup directory was pre-locked for us */ BUG_ON(!mutex_is_locked(&cgrp->dentry->d_inode->i_mutex)); @@ -4062,8 +4090,24 @@ static int cgroup_has_css_refs(struct cgroup *cgrp) * Atomically mark all (or else none) of the cgroup's CSS objects as * CSS_REMOVED. Return true on success, or false if the cgroup has * busy subsystems. Call with cgroup_mutex held + * + * Depending on whether a subsys has __DEPRECATED_clear_css_refs set or + * not, cgroup removal behaves differently. + * + * If clear is set, css refcnt for the subsystem should be zero before + * cgroup removal can be committed. This is implemented by + * CGRP_WAIT_ON_RMDIR and retry logic around ->pre_destroy(), which may be + * called multiple times until all css refcnts reach zero and is allowed to + * veto removal on any invocation. This behavior is deprecated and will be + * removed as soon as the existing user (memcg) is updated. + * + * If clear is not set, each css holds an extra reference to the cgroup's + * dentry and cgroup removal proceeds regardless of css refs. + * ->pre_destroy() will be called at least once and is not allowed to fail. + * On the last put of each css, whenever that may be, the extra dentry ref + * is put so that dentry destruction happens only after all css's are + * released. */ - static int cgroup_clear_css_refs(struct cgroup *cgrp) { struct cgroup_subsys *ss; @@ -4074,14 +4118,17 @@ static int cgroup_clear_css_refs(struct cgroup *cgrp) /* * Block new css_tryget() by deactivating refcnt. If all refcnts - * were 1 at the moment of deactivation, we succeeded. + * for subsystems w/ clear_css_refs set were 1 at the moment of + * deactivation, we succeeded. */ for_each_subsys(cgrp->root, ss) { struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; WARN_ON(atomic_read(&css->refcnt) < 0); atomic_add(CSS_DEACT_BIAS, &css->refcnt); - failed |= css_refcnt(css) != 1; + + if (ss->__DEPRECATED_clear_css_refs) + failed |= css_refcnt(css) != 1; } /* @@ -4917,12 +4964,18 @@ void __css_put(struct cgroup_subsys_state *css) rcu_read_lock(); atomic_dec(&css->refcnt); - if (css_refcnt(css) == 1) { + switch (css_refcnt(css)) { + case 1: if (notify_on_release(cgrp)) { set_bit(CGRP_RELEASABLE, &cgrp->flags); check_for_release(cgrp); } cgroup_wakeup_rmdir_waiter(cgrp); + break; + case 0: + if (!test_bit(CSS_CLEAR_CSS_REFS, &css->flags)) + schedule_work(&css->dput_work); + break; } rcu_read_unlock(); } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index bef114258bbd..d28359cd6b55 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -5635,6 +5635,7 @@ struct cgroup_subsys mem_cgroup_subsys = { .base_cftypes = mem_cgroup_files, .early_init = 0, .use_id = 1, + .__DEPRECATED_clear_css_refs = true, }; #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP -- cgit v1.2.3 From 7cf7899d9ee31c88c86ea8459fc4db4bd11cc240 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 1 Apr 2012 19:54:46 -0400 Subject: ipset: Stop using NLA_PUT*(). These macros contain a hidden goto, and are thus extremely error prone and make code hard to audit. Signed-off-by: David S. Miller --- include/linux/netfilter/ipset/ip_set.h | 46 +++++++++++-------- include/linux/netfilter/ipset/ip_set_ahash.h | 21 +++++---- net/netfilter/ipset/ip_set_bitmap_ip.c | 33 +++++++------ net/netfilter/ipset/ip_set_bitmap_ipmac.c | 43 +++++++++-------- net/netfilter/ipset/ip_set_bitmap_port.c | 29 ++++++------ net/netfilter/ipset/ip_set_core.c | 43 +++++++++-------- net/netfilter/ipset/ip_set_hash_ip.c | 20 ++++---- net/netfilter/ipset/ip_set_hash_ipport.c | 37 ++++++++------- net/netfilter/ipset/ip_set_hash_ipportip.c | 45 +++++++++--------- net/netfilter/ipset/ip_set_hash_ipportnet.c | 69 +++++++++++++++------------- net/netfilter/ipset/ip_set_hash_net.c | 45 +++++++++--------- net/netfilter/ipset/ip_set_hash_netiface.c | 52 +++++++++++---------- net/netfilter/ipset/ip_set_hash_netport.c | 61 ++++++++++++------------ net/netfilter/ipset/ip_set_list_set.c | 23 ++++++---- 14 files changed, 309 insertions(+), 258 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 2f8e18a23227..d6d549cf1f23 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -411,26 +411,32 @@ ip_set_get_h16(const struct nlattr *attr) #define ipset_nest_start(skb, attr) nla_nest_start(skb, attr | NLA_F_NESTED) #define ipset_nest_end(skb, start) nla_nest_end(skb, start) -#define NLA_PUT_IPADDR4(skb, type, ipaddr) \ -do { \ - struct nlattr *__nested = ipset_nest_start(skb, type); \ - \ - if (!__nested) \ - goto nla_put_failure; \ - NLA_PUT_NET32(skb, IPSET_ATTR_IPADDR_IPV4, ipaddr); \ - ipset_nest_end(skb, __nested); \ -} while (0) - -#define NLA_PUT_IPADDR6(skb, type, ipaddrptr) \ -do { \ - struct nlattr *__nested = ipset_nest_start(skb, type); \ - \ - if (!__nested) \ - goto nla_put_failure; \ - NLA_PUT(skb, IPSET_ATTR_IPADDR_IPV6, \ - sizeof(struct in6_addr), ipaddrptr); \ - ipset_nest_end(skb, __nested); \ -} while (0) +static inline int nla_put_ipaddr4(struct sk_buff *skb, int type, __be32 ipaddr) +{ + struct nlattr *__nested = ipset_nest_start(skb, type); + int ret; + + if (!__nested) + return -EMSGSIZE; + ret = nla_put_net32(skb, IPSET_ATTR_IPADDR_IPV4, ipaddr); + if (!ret) + ipset_nest_end(skb, __nested); + return ret; +} + +static inline int nla_put_ipaddr6(struct sk_buff *skb, int type, const struct in6_addr *ipaddrptr) +{ + struct nlattr *__nested = ipset_nest_start(skb, type); + int ret; + + if (!__nested) + return -EMSGSIZE; + ret = nla_put(skb, IPSET_ATTR_IPADDR_IPV6, + sizeof(struct in6_addr), ipaddrptr); + if (!ret) + ipset_nest_end(skb, __nested); + return ret; +} /* Get address from skbuff */ static inline __be32 diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index 05a5d72680be..289b62d9dd1f 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h @@ -594,17 +594,20 @@ type_pf_head(struct ip_set *set, struct sk_buff *skb) nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; - NLA_PUT_NET32(skb, IPSET_ATTR_HASHSIZE, - htonl(jhash_size(h->table->htable_bits))); - NLA_PUT_NET32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)); + if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE, + htonl(jhash_size(h->table->htable_bits))) || + nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) + goto nla_put_failure; #ifdef IP_SET_HASH_WITH_NETMASK - if (h->netmask != HOST_MASK) - NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, h->netmask); + if (h->netmask != HOST_MASK && + nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask)) + goto nla_put_failure; #endif - NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)); - NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)); - if (with_timeout(h->timeout)) - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout)); + if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) || + (with_timeout(h->timeout) && + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout)))) + goto nla_put_failure; ipset_nest_end(skb, nested); return 0; diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index a72a4dff0031..7e1b061aeeba 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -109,8 +109,9 @@ bitmap_ip_list(const struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, - htonl(map->first_ip + id * map->hosts)); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id * map->hosts))) + goto nla_put_failure; ipset_nest_end(skb, nested); } ipset_nest_end(skb, atd); @@ -194,10 +195,11 @@ bitmap_ip_tlist(const struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, - htonl(map->first_ip + id * map->hosts)); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(members[id]))); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id * map->hosts)) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(members[id])))) + goto nla_put_failure; ipset_nest_end(skb, nested); } ipset_nest_end(skb, adt); @@ -334,15 +336,16 @@ bitmap_ip_head(struct ip_set *set, struct sk_buff *skb) nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); - if (map->netmask != 32) - NLA_PUT_U8(skb, IPSET_ATTR_NETMASK, map->netmask); - NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)); - NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, - htonl(sizeof(*map) + map->memsize)); - if (with_timeout(map->timeout)) - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) || + nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)) || + (map->netmask != 32 && + nla_put_u8(skb, IPSET_ATTR_NETMASK, map->netmask)) || + nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->memsize)) || + (with_timeout(map->timeout) && + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)))) + goto nla_put_failure; ipset_nest_end(skb, nested); return 0; diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 81324c12c5be..0bb16c469a89 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -186,11 +186,12 @@ bitmap_ipmac_list(const struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, - htonl(map->first_ip + id)); - if (elem->match == MAC_FILLED) - NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN, - elem->ether); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id)) || + (elem->match == MAC_FILLED && + nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN, + elem->ether))) + goto nla_put_failure; ipset_nest_end(skb, nested); } ipset_nest_end(skb, atd); @@ -314,14 +315,16 @@ bitmap_ipmac_tlist(const struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, - htonl(map->first_ip + id)); - if (elem->match == MAC_FILLED) - NLA_PUT(skb, IPSET_ATTR_ETHER, ETH_ALEN, - elem->ether); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id)) || + (elem->match == MAC_FILLED && + nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN, + elem->ether))) + goto nla_put_failure; timeout = elem->match == MAC_UNSET ? elem->timeout : ip_set_timeout_get(elem->timeout); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(timeout)); + if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(timeout))) + goto nla_put_failure; ipset_nest_end(skb, nested); } ipset_nest_end(skb, atd); @@ -438,14 +441,16 @@ bitmap_ipmac_head(struct ip_set *set, struct sk_buff *skb) nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, htonl(map->first_ip)); - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)); - NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)); - NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, - htonl(sizeof(*map) - + (map->last_ip - map->first_ip + 1) * map->dsize)); - if (with_timeout(map->timeout)) - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip)) || + nla_put_ipaddr4(skb, IPSET_ATTR_IP_TO, htonl(map->last_ip)) || + nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + + ((map->last_ip - map->first_ip + 1) * + map->dsize))) || + (with_timeout(map->timeout) && + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)))) + goto nla_put_failure; ipset_nest_end(skb, nested); return 0; diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index 382ec28ba72e..b9f1fce7053b 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -96,8 +96,9 @@ bitmap_port_list(const struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, - htons(map->first_port + id)); + if (nla_put_net16(skb, IPSET_ATTR_PORT, + htons(map->first_port + id))) + goto nla_put_failure; ipset_nest_end(skb, nested); } ipset_nest_end(skb, atd); @@ -183,10 +184,11 @@ bitmap_port_tlist(const struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, - htons(map->first_port + id)); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(members[id]))); + if (nla_put_net16(skb, IPSET_ATTR_PORT, + htons(map->first_port + id)) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(members[id])))) + goto nla_put_failure; ipset_nest_end(skb, nested); } ipset_nest_end(skb, adt); @@ -320,13 +322,14 @@ bitmap_port_head(struct ip_set *set, struct sk_buff *skb) nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, htons(map->first_port)); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port)); - NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)); - NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, - htonl(sizeof(*map) + map->memsize)); - if (with_timeout(map->timeout)) - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); + if (nla_put_net16(skb, IPSET_ATTR_PORT, htons(map->first_port)) || + nla_put_net16(skb, IPSET_ATTR_PORT_TO, htons(map->last_port)) || + nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->memsize)) || + (with_timeout(map->timeout) && + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)))) + goto nla_put_failure; ipset_nest_end(skb, nested); return 0; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index e6c1c9605a58..eb66b9790a6f 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1092,19 +1092,21 @@ dump_last: ret = -EMSGSIZE; goto release_refcount; } - NLA_PUT_U8(skb, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); - NLA_PUT_STRING(skb, IPSET_ATTR_SETNAME, set->name); + if (nla_put_u8(skb, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL) || + nla_put_string(skb, IPSET_ATTR_SETNAME, set->name)) + goto nla_put_failure; if (dump_flags & IPSET_FLAG_LIST_SETNAME) goto next_set; switch (cb->args[2]) { case 0: /* Core header data */ - NLA_PUT_STRING(skb, IPSET_ATTR_TYPENAME, - set->type->name); - NLA_PUT_U8(skb, IPSET_ATTR_FAMILY, - set->family); - NLA_PUT_U8(skb, IPSET_ATTR_REVISION, - set->revision); + if (nla_put_string(skb, IPSET_ATTR_TYPENAME, + set->type->name) || + nla_put_u8(skb, IPSET_ATTR_FAMILY, + set->family) || + nla_put_u8(skb, IPSET_ATTR_REVISION, + set->revision)) + goto nla_put_failure; ret = set->variant->head(set, skb); if (ret < 0) goto release_refcount; @@ -1410,11 +1412,12 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb, IPSET_CMD_HEADER); if (!nlh2) goto nlmsg_failure; - NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); - NLA_PUT_STRING(skb2, IPSET_ATTR_SETNAME, set->name); - NLA_PUT_STRING(skb2, IPSET_ATTR_TYPENAME, set->type->name); - NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, set->family); - NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, set->revision); + if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL) || + nla_put_string(skb2, IPSET_ATTR_SETNAME, set->name) || + nla_put_string(skb2, IPSET_ATTR_TYPENAME, set->type->name) || + nla_put_u8(skb2, IPSET_ATTR_FAMILY, set->family) || + nla_put_u8(skb2, IPSET_ATTR_REVISION, set->revision)) + goto nla_put_failure; nlmsg_end(skb2, nlh2); ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); @@ -1469,11 +1472,12 @@ ip_set_type(struct sock *ctnl, struct sk_buff *skb, IPSET_CMD_TYPE); if (!nlh2) goto nlmsg_failure; - NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); - NLA_PUT_STRING(skb2, IPSET_ATTR_TYPENAME, typename); - NLA_PUT_U8(skb2, IPSET_ATTR_FAMILY, family); - NLA_PUT_U8(skb2, IPSET_ATTR_REVISION, max); - NLA_PUT_U8(skb2, IPSET_ATTR_REVISION_MIN, min); + if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL) || + nla_put_string(skb2, IPSET_ATTR_TYPENAME, typename) || + nla_put_u8(skb2, IPSET_ATTR_FAMILY, family) || + nla_put_u8(skb2, IPSET_ATTR_REVISION, max) || + nla_put_u8(skb2, IPSET_ATTR_REVISION_MIN, min)) + goto nla_put_failure; nlmsg_end(skb2, nlh2); pr_debug("Send TYPE, nlmsg_len: %u\n", nlh2->nlmsg_len); @@ -1517,7 +1521,8 @@ ip_set_protocol(struct sock *ctnl, struct sk_buff *skb, IPSET_CMD_PROTOCOL); if (!nlh2) goto nlmsg_failure; - NLA_PUT_U8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL); + if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL)) + goto nla_put_failure; nlmsg_end(skb2, nlh2); ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index 5139dea6019e..507fe93794aa 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -81,7 +81,8 @@ hash_ip4_data_zero_out(struct hash_ip4_elem *elem) static inline bool hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *data) { - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip)) + goto nla_put_failure; return 0; nla_put_failure: @@ -94,9 +95,10 @@ hash_ip4_data_tlist(struct sk_buff *skb, const struct hash_ip4_elem *data) const struct hash_ip4_telem *tdata = (const struct hash_ip4_telem *)data; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(tdata->timeout))); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout)))) + goto nla_put_failure; return 0; @@ -262,7 +264,8 @@ ip6_netmask(union nf_inet_addr *ip, u8 prefix) static bool hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *data) { - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6)) + goto nla_put_failure; return 0; nla_put_failure: @@ -275,9 +278,10 @@ hash_ip6_data_tlist(struct sk_buff *skb, const struct hash_ip6_elem *data) const struct hash_ip6_telem *e = (const struct hash_ip6_telem *)data; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(e->timeout))); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout)))) + goto nla_put_failure; return 0; nla_put_failure: diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 9c27e249c171..68f284c97490 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -93,9 +93,10 @@ static bool hash_ipport4_data_list(struct sk_buff *skb, const struct hash_ipport4_elem *data) { - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto)) + goto nla_put_failure; return 0; nla_put_failure: @@ -109,12 +110,12 @@ hash_ipport4_data_tlist(struct sk_buff *skb, const struct hash_ipport4_telem *tdata = (const struct hash_ipport4_telem *)data; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(tdata->timeout))); - + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) || + nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -308,9 +309,10 @@ static bool hash_ipport6_data_list(struct sk_buff *skb, const struct hash_ipport6_elem *data) { - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto)) + goto nla_put_failure; return 0; nla_put_failure: @@ -324,11 +326,12 @@ hash_ipport6_data_tlist(struct sk_buff *skb, const struct hash_ipport6_telem *e = (const struct hash_ipport6_telem *)data; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(e->timeout))); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout)))) + goto nla_put_failure; return 0; nla_put_failure: diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index 9134057c0728..1eec4b9e0dca 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -94,10 +94,11 @@ static bool hash_ipportip4_data_list(struct sk_buff *skb, const struct hash_ipportip4_elem *data) { - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || + nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip2) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto)) + goto nla_put_failure; return 0; nla_put_failure: @@ -111,13 +112,13 @@ hash_ipportip4_data_tlist(struct sk_buff *skb, const struct hash_ipportip4_telem *tdata = (const struct hash_ipportip4_telem *)data; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(tdata->timeout))); - + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) || + nla_put_ipaddr4(skb, IPSET_ATTR_IP2, tdata->ip2) || + nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -319,10 +320,11 @@ static bool hash_ipportip6_data_list(struct sk_buff *skb, const struct hash_ipportip6_elem *data) { - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || + nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto)) + goto nla_put_failure; return 0; nla_put_failure: @@ -336,12 +338,13 @@ hash_ipportip6_data_tlist(struct sk_buff *skb, const struct hash_ipportip6_telem *e = (const struct hash_ipportip6_telem *)data; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(e->timeout))); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) || + nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout)))) + goto nla_put_failure; return 0; nla_put_failure: diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 5d05e6969862..62d66ecef369 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -124,13 +124,14 @@ hash_ipportnet4_data_list(struct sk_buff *skb, { u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || + nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip2) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -145,16 +146,16 @@ hash_ipportnet4_data_tlist(struct sk_buff *skb, (const struct hash_ipportnet4_telem *)data; u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(tdata->timeout))); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); - + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) || + nla_put_ipaddr4(skb, IPSET_ATTR_IP2, tdata->ip2) || + nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) || + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -436,13 +437,14 @@ hash_ipportnet6_data_list(struct sk_buff *skb, { u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || + nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -457,15 +459,16 @@ hash_ipportnet6_data_tlist(struct sk_buff *skb, (const struct hash_ipportnet6_telem *)data; u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr + 1); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(e->timeout))); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) || + nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 7c3d945517cf..6607a814be57 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -111,10 +111,11 @@ hash_net4_data_list(struct sk_buff *skb, const struct hash_net4_elem *data) { u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -128,13 +129,13 @@ hash_net4_data_tlist(struct sk_buff *skb, const struct hash_net4_elem *data) (const struct hash_net4_telem *)data; u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, tdata->cidr); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(tdata->timeout))); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); - + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) || + nla_put_u8(skb, IPSET_ATTR_CIDR, tdata->cidr) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -339,10 +340,11 @@ hash_net6_data_list(struct sk_buff *skb, const struct hash_net6_elem *data) { u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -356,12 +358,13 @@ hash_net6_data_tlist(struct sk_buff *skb, const struct hash_net6_elem *data) (const struct hash_net6_telem *)data; u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, e->cidr); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(e->timeout))); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) || + nla_put_u8(skb, IPSET_ATTR_CIDR, e->cidr) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index f24037ff4322..6093f3daa911 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -252,11 +252,12 @@ hash_netiface4_data_list(struct sk_buff *skb, if (data->nomatch) flags |= IPSET_FLAG_NOMATCH; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); - NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) || + nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -273,13 +274,14 @@ hash_netiface4_data_tlist(struct sk_buff *skb, if (data->nomatch) flags |= IPSET_FLAG_NOMATCH; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); - NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(tdata->timeout))); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) || + nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout)))) + goto nla_put_failure; return 0; @@ -555,11 +557,12 @@ hash_netiface6_data_list(struct sk_buff *skb, if (data->nomatch) flags |= IPSET_FLAG_NOMATCH; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); - NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) || + nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -576,13 +579,14 @@ hash_netiface6_data_tlist(struct sk_buff *skb, if (data->nomatch) flags |= IPSET_FLAG_NOMATCH; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr); - NLA_PUT_STRING(skb, IPSET_ATTR_IFACE, data->iface); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(e->timeout))); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr) || + nla_put_string(skb, IPSET_ATTR_IFACE, data->iface) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags))) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout)))) + goto nla_put_failure; return 0; nla_put_failure: diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index ce2e77100b64..ae3c644adc14 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -124,12 +124,13 @@ hash_netport4_data_list(struct sk_buff *skb, { u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr + 1) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -144,15 +145,15 @@ hash_netport4_data_tlist(struct sk_buff *skb, (const struct hash_netport4_telem *)data; u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(tdata->timeout))); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); - + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, tdata->ip) || + nla_put_net16(skb, IPSET_ATTR_PORT, tdata->port) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr + 1) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(tdata->timeout))) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -402,12 +403,13 @@ hash_netport6_data_list(struct sk_buff *skb, { u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr + 1) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: @@ -422,14 +424,15 @@ hash_netport6_data_tlist(struct sk_buff *skb, (const struct hash_netport6_telem *)data; u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; - NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); - NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); - NLA_PUT_U8(skb, IPSET_ATTR_CIDR, data->cidr + 1); - NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(e->timeout))); - if (flags) - NLA_PUT_NET32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)); + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr + 1) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(e->timeout))) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; return 0; nla_put_failure: diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 7e095f9005f0..6cb1225765f9 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -402,12 +402,13 @@ list_set_head(struct ip_set *set, struct sk_buff *skb) nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; - NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size)); - if (with_timeout(map->timeout)) - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); - NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)); - NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, - htonl(sizeof(*map) + map->size * map->dsize)); + if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) || + (with_timeout(map->timeout) && + nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) || + nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->size * map->dsize))) + goto nla_put_failure; ipset_nest_end(skb, nested); return 0; @@ -442,13 +443,15 @@ list_set_list(const struct ip_set *set, } else goto nla_put_failure; } - NLA_PUT_STRING(skb, IPSET_ATTR_NAME, - ip_set_name_byindex(e->id)); + if (nla_put_string(skb, IPSET_ATTR_NAME, + ip_set_name_byindex(e->id))) + goto nla_put_failure; if (with_timeout(map->timeout)) { const struct set_telem *te = (const struct set_telem *) e; - NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get(te->timeout))); + __be32 to = htonl(ip_set_timeout_get(te->timeout)); + if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, to)) + goto nla_put_failure; } ipset_nest_end(skb, nested); } -- cgit v1.2.3 From edbc0bb3fb72ec4645a242520cf1d0b9b6b02261 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 29 Mar 2012 12:51:30 +0000 Subject: net: Report dev->promiscuity in netlink reports. The standard ways of probing a device's promiscuity (ifi_flags, for instance) does not report the actual state of the device. This patch adds dev->promiscuity to the netlink netdevice report so that users can know for certain if the device is acting PROMISC or not. Signed-off-by: Ben Greear Signed-off-by: David S. Miller --- include/linux/if_link.h | 2 ++ net/core/rtnetlink.c | 3 +++ 2 files changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 4b24ff453aee..2f4fa93454c7 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -138,6 +138,8 @@ enum { IFLA_GROUP, /* Group the device belongs to */ IFLA_NET_NS_FD, IFLA_EXT_MASK, /* Extended info mask, VFs, etc */ + IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */ +#define IFLA_PROMISCUITY IFLA_PROMISCUITY __IFLA_MAX }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 71a1920a23a1..b76f8fa3fc64 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -784,6 +784,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(4) /* IFLA_MTU */ + nla_total_size(4) /* IFLA_LINK */ + nla_total_size(4) /* IFLA_MASTER */ + + nla_total_size(4) /* IFLA_PROMISCUITY */ + nla_total_size(1) /* IFLA_OPERSTATE */ + nla_total_size(1) /* IFLA_LINKMODE */ + nla_total_size(ext_filter_mask @@ -901,6 +902,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, nla_put_u8(skb, IFLA_LINKMODE, dev->link_mode) || nla_put_u32(skb, IFLA_MTU, dev->mtu) || nla_put_u32(skb, IFLA_GROUP, dev->group) || + nla_put_u32(skb, IFLA_PROMISCUITY, dev->promiscuity) || (dev->ifindex != dev->iflink && nla_put_u32(skb, IFLA_LINK, dev->iflink)) || (dev->master && @@ -1117,6 +1119,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_PORT_SELF] = { .type = NLA_NESTED }, [IFLA_AF_SPEC] = { .type = NLA_NESTED }, [IFLA_EXT_MASK] = { .type = NLA_U32 }, + [IFLA_PROMISCUITY] = { .type = NLA_U32 }, }; EXPORT_SYMBOL(ifla_policy); -- cgit v1.2.3 From a4d9f179cc788b7f4b735d32c2e4a3b2562e8240 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 7 Mar 2012 15:58:33 +0530 Subject: regulator: fixed: Support for open drain gpio pin Adding flag on fixed regulator board configuration structure to specify whether gpio is open drain type or not. Passing this information to gpio library when requesting gpio so that gpio driver can set the pin state accordingly, for open drain type: - Pin can be set HIGH as setting as input, PULL UP on pin make this as HIGH. - Pin can be set LOW as setting it as output and drive to LOW. The non-open drain pin can be set HIGH/LOW by setting it to output and driving it to HIGH/LOW. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 23 +++++++++++------------ include/linux/regulator/fixed.h | 7 +++++++ 2 files changed, 18 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 8a1e22acc202..9a7d70a9c8d7 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -202,6 +202,7 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->startup_delay = config->startup_delay; if (gpio_is_valid(config->gpio)) { + int gpio_flag; drvdata->enable_high = config->enable_high; /* FIXME: Remove below print warning @@ -219,27 +220,25 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "using GPIO 0 for regulator enable control\n"); - ret = gpio_request(config->gpio, config->supply_name); - if (ret) { - dev_err(&pdev->dev, - "Could not obtain regulator enable GPIO %d: %d\n", - config->gpio, ret); - goto err_name; - } - - /* set output direction without changing state + /* + * set output direction without changing state * to prevent glitch */ drvdata->is_enabled = config->enabled_at_boot; ret = drvdata->is_enabled ? config->enable_high : !config->enable_high; + gpio_flag = ret ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + + if (config->gpio_is_open_drain) + gpio_flag |= GPIOF_OPEN_DRAIN; - ret = gpio_direction_output(config->gpio, ret); + ret = gpio_request_one(config->gpio, gpio_flag, + config->supply_name); if (ret) { dev_err(&pdev->dev, - "Could not configure regulator enable GPIO %d direction: %d\n", + "Could not obtain regulator enable GPIO %d: %d\n", config->gpio, ret); - goto err_gpio; + goto err_name; } drvdata->desc.ops = &fixed_voltage_gpio_ops; diff --git a/include/linux/regulator/fixed.h b/include/linux/regulator/fixed.h index 936a7d8c11a9..f83f7440b488 100644 --- a/include/linux/regulator/fixed.h +++ b/include/linux/regulator/fixed.h @@ -26,6 +26,12 @@ struct regulator_init_data; * @gpio: GPIO to use for enable control * set to -EINVAL if not used * @startup_delay: Start-up time in microseconds + * @gpio_is_open_drain: Gpio pin is open drain or normal type. + * If it is open drain type then HIGH will be set + * through PULL-UP with setting gpio as input + * and low will be set as gpio-output with driven + * to low. For non-open-drain case, the gpio will + * will be in output and drive to low/high accordingly. * @enable_high: Polarity of enable GPIO * 1 = Active high, 0 = Active low * @enabled_at_boot: Whether regulator has been enabled at @@ -43,6 +49,7 @@ struct fixed_voltage_config { int microvolts; int gpio; unsigned startup_delay; + unsigned gpio_is_open_drain:1; unsigned enable_high:1; unsigned enabled_at_boot:1; struct regulator_init_data *init_data; -- cgit v1.2.3 From 57a39aa3e3ca00e371cec37be4f7c2e950eb1f1f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 22:06:16 -0800 Subject: userns: Kill bogus declaration of function release_uids There is no release_uids function remove the declaration from sched.h Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/sched.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 81a173c0897d..720ce8d98a7d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2184,7 +2184,6 @@ static inline struct user_struct *get_uid(struct user_struct *u) return u; } extern void free_uid(struct user_struct *); -extern void release_uids(struct user_namespace *ns); #include -- cgit v1.2.3 From 33be96e47cc27f2f1a753a0707b02a73df8c8d46 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Tue, 27 Mar 2012 13:20:45 +0000 Subject: net/hyperv: Add flow control based on hi/low watermark In the existing code, we only stop queue when the ringbuffer is full, so the current packet has to be dropped or retried from upper layer. This patch stops the tx queue when available ringbuffer is below the low watermark. So the ringbuffer still has small amount of space available for the current packet. This will reduce the overhead of retries on sending. Signed-off-by: Haiyang Zhang Reviewed-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/hv/ring_buffer.c | 31 ------------------------------- drivers/net/hyperv/netvsc.c | 41 +++++++++++++++++++++++++++++++++++++---- drivers/net/hyperv/netvsc_drv.c | 6 +++++- include/linux/hyperv.h | 27 +++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 36 deletions(-) (limited to 'include/linux') diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 8af25a097d75..7233c88f01b8 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -30,37 +30,6 @@ #include "hyperv_vmbus.h" -/* #defines */ - - -/* Amount of space to write to */ -#define BYTES_AVAIL_TO_WRITE(r, w, z) \ - ((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w)) - - -/* - * - * hv_get_ringbuffer_availbytes() - * - * Get number of bytes available to read and to write to - * for the specified ring buffer - */ -static inline void -hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, - u32 *read, u32 *write) -{ - u32 read_loc, write_loc; - - smp_read_barrier_depends(); - - /* Capture the read/write indices before they changed */ - read_loc = rbi->ring_buffer->read_index; - write_loc = rbi->ring_buffer->write_index; - - *write = BYTES_AVAIL_TO_WRITE(read_loc, write_loc, rbi->ring_datasize); - *read = rbi->ring_datasize - *write; -} - /* * hv_get_next_write_location() * diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index d025c83cd12a..8b919471472f 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -428,6 +428,24 @@ int netvsc_device_remove(struct hv_device *device) return 0; } + +#define RING_AVAIL_PERCENT_HIWATER 20 +#define RING_AVAIL_PERCENT_LOWATER 10 + +/* + * Get the percentage of available bytes to write in the ring. + * The return value is in range from 0 to 100. + */ +static inline u32 hv_ringbuf_avail_percent( + struct hv_ring_buffer_info *ring_info) +{ + u32 avail_read, avail_write; + + hv_get_ringbuffer_availbytes(ring_info, &avail_read, &avail_write); + + return avail_write * 100 / ring_info->ring_datasize; +} + static void netvsc_send_completion(struct hv_device *device, struct vmpacket_descriptor *packet) { @@ -455,6 +473,8 @@ static void netvsc_send_completion(struct hv_device *device, complete(&net_device->channel_init_wait); } else if (nvsp_packet->hdr.msg_type == NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) { + int num_outstanding_sends; + /* Get the send context */ nvsc_packet = (struct hv_netvsc_packet *)(unsigned long) packet->trans_id; @@ -463,10 +483,14 @@ static void netvsc_send_completion(struct hv_device *device, nvsc_packet->completion.send.send_completion( nvsc_packet->completion.send.send_completion_ctx); - atomic_dec(&net_device->num_outstanding_sends); + num_outstanding_sends = + atomic_dec_return(&net_device->num_outstanding_sends); - if (netif_queue_stopped(ndev) && !net_device->start_remove) - netif_wake_queue(ndev); + if (netif_queue_stopped(ndev) && !net_device->start_remove && + (hv_ringbuf_avail_percent(&device->channel->outbound) + > RING_AVAIL_PERCENT_HIWATER || + num_outstanding_sends < 1)) + netif_wake_queue(ndev); } else { netdev_err(ndev, "Unknown send completion packet type- " "%d received!!\n", nvsp_packet->hdr.msg_type); @@ -519,10 +543,19 @@ int netvsc_send(struct hv_device *device, if (ret == 0) { atomic_inc(&net_device->num_outstanding_sends); + if (hv_ringbuf_avail_percent(&device->channel->outbound) < + RING_AVAIL_PERCENT_LOWATER) { + netif_stop_queue(ndev); + if (atomic_read(&net_device-> + num_outstanding_sends) < 1) + netif_wake_queue(ndev); + } } else if (ret == -EAGAIN) { netif_stop_queue(ndev); - if (atomic_read(&net_device->num_outstanding_sends) < 1) + if (atomic_read(&net_device->num_outstanding_sends) < 1) { netif_wake_queue(ndev); + ret = -ENOSPC; + } } else { netdev_err(ndev, "Unable to send packet %p ret %d\n", packet, ret); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index dd294783b5c5..a0cc12786be4 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -224,9 +224,13 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) net->stats.tx_packets++; } else { kfree(packet); + if (ret != -EAGAIN) { + dev_kfree_skb_any(skb); + net->stats.tx_dropped++; + } } - return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK; + return (ret == -EAGAIN) ? NETDEV_TX_BUSY : NETDEV_TX_OK; } /* diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 5852545e6bba..6af8738ae7e9 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -274,6 +274,33 @@ struct hv_ring_buffer_debug_info { u32 bytes_avail_towrite; }; + +/* + * + * hv_get_ringbuffer_availbytes() + * + * Get number of bytes available to read and to write to + * for the specified ring buffer + */ +static inline void +hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, + u32 *read, u32 *write) +{ + u32 read_loc, write_loc, dsize; + + smp_read_barrier_depends(); + + /* Capture the read/write indices before they changed */ + read_loc = rbi->ring_buffer->read_index; + write_loc = rbi->ring_buffer->write_index; + dsize = rbi->ring_datasize; + + *write = write_loc >= read_loc ? dsize - (write_loc - read_loc) : + read_loc - write_loc; + *read = dsize - *write; +} + + /* * We use the same version numbering for all Hyper-V modules. * -- cgit v1.2.3 From 302d663740cfaf2c364df6bb61cd339014ed714c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 31 Mar 2012 11:01:19 +0000 Subject: filter: Allow to create sk-unattached filters Today, BPF filters are bind to sockets. Since BPF machine becomes handy for other purposes, this patch allows to create unattached filter. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/filter.h | 3 +++ net/core/filter.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/filter.h b/include/linux/filter.h index 8eeb205f298b..92dd9933c43d 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -153,6 +153,9 @@ static inline unsigned int sk_filter_len(const struct sk_filter *fp) extern int sk_filter(struct sock *sk, struct sk_buff *skb); extern unsigned int sk_run_filter(const struct sk_buff *skb, const struct sock_filter *filter); +extern int sk_unattached_filter_create(struct sk_filter **pfp, + struct sock_fprog *fprog); +extern void sk_unattached_filter_destroy(struct sk_filter *fp); extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); extern int sk_detach_filter(struct sock *sk); extern int sk_chk_filter(struct sock_filter *filter, unsigned int flen); diff --git a/net/core/filter.c b/net/core/filter.c index 5dea45279215..cfbea889a0eb 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -587,6 +587,67 @@ void sk_filter_release_rcu(struct rcu_head *rcu) } EXPORT_SYMBOL(sk_filter_release_rcu); +static int __sk_prepare_filter(struct sk_filter *fp) +{ + int err; + + fp->bpf_func = sk_run_filter; + + err = sk_chk_filter(fp->insns, fp->len); + if (err) + return err; + + bpf_jit_compile(fp); + return 0; +} + +/** + * sk_unattached_filter_create - create an unattached filter + * @fprog: the filter program + * @sk: the socket to use + * + * Create a filter independent ofr any socket. We first run some + * sanity checks on it to make sure it does not explode on us later. + * If an error occurs or there is insufficient memory for the filter + * a negative errno code is returned. On success the return is zero. + */ +int sk_unattached_filter_create(struct sk_filter **pfp, + struct sock_fprog *fprog) +{ + struct sk_filter *fp; + unsigned int fsize = sizeof(struct sock_filter) * fprog->len; + int err; + + /* Make sure new filter is there and in the right amounts. */ + if (fprog->filter == NULL) + return -EINVAL; + + fp = kmalloc(fsize + sizeof(*fp), GFP_KERNEL); + if (!fp) + return -ENOMEM; + memcpy(fp->insns, fprog->filter, fsize); + + atomic_set(&fp->refcnt, 1); + fp->len = fprog->len; + + err = __sk_prepare_filter(fp); + if (err) + goto free_mem; + + *pfp = fp; + return 0; +free_mem: + kfree(fp); + return err; +} +EXPORT_SYMBOL_GPL(sk_unattached_filter_create); + +void sk_unattached_filter_destroy(struct sk_filter *fp) +{ + sk_filter_release(fp); +} +EXPORT_SYMBOL_GPL(sk_unattached_filter_destroy); + /** * sk_attach_filter - attach a socket filter * @fprog: the filter program @@ -617,16 +678,13 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) atomic_set(&fp->refcnt, 1); fp->len = fprog->len; - fp->bpf_func = sk_run_filter; - err = sk_chk_filter(fp->insns, fp->len); + err = __sk_prepare_filter(fp); if (err) { sk_filter_uncharge(sk, fp); return err; } - bpf_jit_compile(fp); - old_fp = rcu_dereference_protected(sk->sk_filter, sock_owned_by_user(sk)); rcu_assign_pointer(sk->sk_filter, fp); -- cgit v1.2.3 From ffe06c17afbbbd4d73cdc339419be232847d667a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 31 Mar 2012 11:01:20 +0000 Subject: filter: add XOR operation Add XOR instruction fo BPF machine. Needed for computing packet hashes. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/filter.h | 4 +++- net/core/filter.c | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/filter.h b/include/linux/filter.h index 92dd9933c43d..72090994d789 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -126,7 +126,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */ #define SKF_AD_HATYPE 28 #define SKF_AD_RXHASH 32 #define SKF_AD_CPU 36 -#define SKF_AD_MAX 40 +#define SKF_AD_ALU_XOR_X 40 +#define SKF_AD_MAX 44 #define SKF_NET_OFF (-0x100000) #define SKF_LL_OFF (-0x200000) @@ -231,6 +232,7 @@ enum { BPF_S_ANC_HATYPE, BPF_S_ANC_RXHASH, BPF_S_ANC_CPU, + BPF_S_ANC_ALU_XOR_X, }; #endif /* __KERNEL__ */ diff --git a/net/core/filter.c b/net/core/filter.c index cfbea889a0eb..5099c4b4a53f 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -315,6 +315,9 @@ load_b: case BPF_S_ANC_CPU: A = raw_smp_processor_id(); continue; + case BPF_S_ANC_ALU_XOR_X: + A ^= X; + continue; case BPF_S_ANC_NLATTR: { struct nlattr *nla; @@ -559,6 +562,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) ANCILLARY(HATYPE); ANCILLARY(RXHASH); ANCILLARY(CPU); + ANCILLARY(ALU_XOR_X); } } ftest->code = code; -- cgit v1.2.3 From 995a9090b2b7dc734351f3ac0ba8d913ffb87001 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 3 Apr 2012 22:59:16 +0000 Subject: ptp: Add a method for obtaining the device index. This commit adds a method that MAC drivers may call in order to find out the device number of their associated PTP Hardware Clock. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/ptp/ptp_clock.c | 6 ++++++ include/linux/ptp_clock_kernel.h | 8 ++++++++ 2 files changed, 14 insertions(+) (limited to 'include/linux') diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index f519a131238d..1e528b539a07 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -304,6 +304,12 @@ void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event) } EXPORT_SYMBOL(ptp_clock_event); +int ptp_clock_index(struct ptp_clock *ptp) +{ + return ptp->index; +} +EXPORT_SYMBOL(ptp_clock_index); + /* module operations */ static void __exit ptp_exit(void) diff --git a/include/linux/ptp_clock_kernel.h b/include/linux/ptp_clock_kernel.h index dd2e44fba63e..945704c2ed65 100644 --- a/include/linux/ptp_clock_kernel.h +++ b/include/linux/ptp_clock_kernel.h @@ -136,4 +136,12 @@ struct ptp_clock_event { extern void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event); +/** + * ptp_clock_index() - obtain the device index of a PTP clock + * + * @ptp: The clock obtained from ptp_clock_register(). + */ + +extern int ptp_clock_index(struct ptp_clock *ptp); + #endif -- cgit v1.2.3 From c8f3a8c31069137fe0100e6920558f1a7487ef3c Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 3 Apr 2012 22:59:17 +0000 Subject: ethtool: Introduce a method for getting time stamping capabilities. This commit adds a new ethtool ioctl that exposes the SO_TIMESTAMPING capabilities of a network interface. In addition, user space programs can use this ioctl to discover the PTP Hardware Clock (PHC) device associated with the interface. Since software receive time stamps are handled by the stack, the generic ethtool code can answer the query correctly in case the MAC or PHY drivers lack special time stamping features. Signed-off-by: Richard Cochran Reviewed-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/ethtool.h | 28 ++++++++++++++++++++++++++++ include/linux/phy.h | 3 +++ net/core/ethtool.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 76 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index e1d9e0ede309..1769714447b3 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -726,6 +726,29 @@ struct ethtool_sfeatures { struct ethtool_set_features_block features[0]; }; +/** + * struct ethtool_ts_info - holds a device's timestamping and PHC association + * @cmd: command number = %ETHTOOL_GET_TS_INFO + * @so_timestamping: bit mask of the sum of the supported SO_TIMESTAMPING flags + * @phc_index: device index of the associated PHC, or -1 if there is none + * @tx_types: bit mask of the supported hwtstamp_tx_types enumeration values + * @rx_filters: bit mask of the supported hwtstamp_rx_filters enumeration values + * + * The bits in the 'tx_types' and 'rx_filters' fields correspond to + * the 'hwtstamp_tx_types' and 'hwtstamp_rx_filters' enumeration values, + * respectively. For example, if the device supports HWTSTAMP_TX_ON, + * then (1 << HWTSTAMP_TX_ON) in 'tx_types' will be set. + */ +struct ethtool_ts_info { + __u32 cmd; + __u32 so_timestamping; + __s32 phc_index; + __u32 tx_types; + __u32 tx_reserved[3]; + __u32 rx_filters; + __u32 rx_reserved[3]; +}; + /* * %ETHTOOL_SFEATURES changes features present in features[].valid to the * values of corresponding bits in features[].requested. Bits in .requested @@ -893,6 +916,9 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings) * and flag of the device. * @get_dump_data: Get dump data. * @set_dump: Set dump specific flags to the device. + * @get_ts_info: Get the time stamping and PTP hardware clock capabilities. + * Drivers supporting transmit time stamps in software should set this to + * ethtool_op_get_ts_info(). * * All operations are optional (i.e. the function pointer may be set * to %NULL) and callers must take this into account. Callers must @@ -955,6 +981,7 @@ struct ethtool_ops { int (*get_dump_data)(struct net_device *, struct ethtool_dump *, void *); int (*set_dump)(struct net_device *, struct ethtool_dump *); + int (*get_ts_info)(struct net_device *, struct ethtool_ts_info *); }; #endif /* __KERNEL__ */ @@ -1029,6 +1056,7 @@ struct ethtool_ops { #define ETHTOOL_SET_DUMP 0x0000003e /* Set dump settings */ #define ETHTOOL_GET_DUMP_FLAG 0x0000003f /* Get dump settings */ #define ETHTOOL_GET_DUMP_DATA 0x00000040 /* Get dump data */ +#define ETHTOOL_GET_TS_INFO 0x00000041 /* Get time stamping and PHC info */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET diff --git a/include/linux/phy.h b/include/linux/phy.h index 6fe0a37d4abf..f092032f1c98 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -412,6 +412,9 @@ struct phy_driver { /* Clears up any memory if needed */ void (*remove)(struct phy_device *phydev); + /* Handles ethtool queries for hardware time stamping. */ + int (*ts_info)(struct phy_device *phydev, struct ethtool_ts_info *ti); + /* Handles SIOCSHWTSTAMP ioctl for hardware time stamping. */ int (*hwtstamp)(struct phy_device *phydev, struct ifreq *ifr); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 6d6d7d25caaa..a723b1321691 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -1278,6 +1280,40 @@ out: return ret; } +static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr) +{ + int err = 0; + struct ethtool_ts_info info; + const struct ethtool_ops *ops = dev->ethtool_ops; + struct phy_device *phydev = dev->phydev; + + memset(&info, 0, sizeof(info)); + info.cmd = ETHTOOL_GET_TS_INFO; + + if (phydev && phydev->drv && phydev->drv->ts_info) { + + err = phydev->drv->ts_info(phydev, &info); + + } else if (dev->ethtool_ops && dev->ethtool_ops->get_ts_info) { + + err = ops->get_ts_info(dev, &info); + + } else { + info.so_timestamping = + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + info.phc_index = -1; + } + + if (err) + return err; + + if (copy_to_user(useraddr, &info, sizeof(info))) + err = -EFAULT; + + return err; +} + /* The main entry point in this file. Called from net/core/dev.c */ int dev_ethtool(struct net *net, struct ifreq *ifr) @@ -1295,11 +1331,13 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) return -EFAULT; if (!dev->ethtool_ops) { - /* ETHTOOL_GDRVINFO does not require any driver support. - * It is also unprivileged and does not change anything, - * so we can take a shortcut to it. */ + /* A few commands do not require any driver support, + * are unprivileged, and do not change anything, so we + * can take a shortcut to them. */ if (ethcmd == ETHTOOL_GDRVINFO) return ethtool_get_drvinfo(dev, useraddr); + else if (ethcmd == ETHTOOL_GET_TS_INFO) + return ethtool_get_ts_info(dev, useraddr); else return -EOPNOTSUPP; } @@ -1330,6 +1368,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GRXCLSRULE: case ETHTOOL_GRXCLSRLALL: case ETHTOOL_GFEATURES: + case ETHTOOL_GET_TS_INFO: break; default: if (!capable(CAP_NET_ADMIN)) @@ -1496,6 +1535,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GET_DUMP_DATA: rc = ethtool_get_dump_data(dev, useraddr); break; + case ETHTOOL_GET_TS_INFO: + rc = ethtool_get_ts_info(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } -- cgit v1.2.3 From 02eacbd0c405d378d2357e8e0fac5de981bd40f8 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 3 Apr 2012 22:59:22 +0000 Subject: ethtool: Add a common function for drivers with transmit time stamping. Currently, most drivers do not support transmit SO_TIMESTAMPING. For those that do support it, there is one appropriate response to the get_ts_info query. This patch adds a common function providing this response. Signed-off-by: Richard Cochran Reviewed-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/ethtool.h | 1 + net/core/ethtool.c | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 1769714447b3..560a247bde2a 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -811,6 +811,7 @@ struct net_device; /* Some generic methods drivers may use in their ethtool_ops */ u32 ethtool_op_get_link(struct net_device *dev); +int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *eti); /** * ethtool_rxfh_indir_default - get default value for RX flow hash indirection diff --git a/net/core/ethtool.c b/net/core/ethtool.c index a723b1321691..beacdd93cd8f 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -38,6 +38,17 @@ u32 ethtool_op_get_link(struct net_device *dev) } EXPORT_SYMBOL(ethtool_op_get_link); +int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info) +{ + info->so_timestamping = + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + info->phc_index = -1; + return 0; +} +EXPORT_SYMBOL(ethtool_op_get_ts_info); + /* Handlers for each ethtool command */ #define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32) -- cgit v1.2.3 From 65f26846b90611742f3b407cc538a1cad33abde8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 3 Apr 2012 20:46:53 +0100 Subject: regulator: core: Constify the regulator_desc passed in when registering Drivers should be able to declare their descriptors const and the framework shouldn't ever be modifying the desciptor. Make the parameter and the pointer in regulator_dev const to enforce this. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 3 ++- include/linux/regulator/driver.h | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index c056abd7562a..c4b626789f8e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2829,7 +2829,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) * Called by regulator drivers to register a regulator. * Returns 0 on success. */ -struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, +struct regulator_dev * +regulator_register(const struct regulator_desc *regulator_desc, struct device *dev, const struct regulator_init_data *init_data, void *driver_data, struct device_node *of_node) { diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index fa8b55b8191c..1dcdf00e0db2 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -184,7 +184,7 @@ struct regulator_desc { * no other direct access). */ struct regulator_dev { - struct regulator_desc *desc; + const struct regulator_desc *desc; int exclusive; u32 use_count; u32 open_count; @@ -210,7 +210,8 @@ struct regulator_dev { struct dentry *debugfs; }; -struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, +struct regulator_dev * +regulator_register(const struct regulator_desc *regulator_desc, struct device *dev, const struct regulator_init_data *init_data, void *driver_data, struct device_node *of_node); void regulator_unregister(struct regulator_dev *rdev); -- cgit v1.2.3 From 6ffc3270210efa2bea526953a142ffc908f5bd86 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 4 Apr 2012 12:44:00 +0530 Subject: regulator: Add support for RICOH PMIC RC5T583 regulator The RC5T583 PMIC from RICOH consists of 4 DCDC and 10 LDOs. This driver supports the control of different regulator output through regulator interface. This driver depends on MFD driver of RC5T583 and uses mfd rc5t583 apis to communicate to device for accessing different device's registers. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/rc5t583-regulator.c | 367 ++++++++++++++++++++++++++++++++++ include/linux/mfd/rc5t583.h | 29 +++ 4 files changed, 407 insertions(+) create mode 100644 drivers/regulator/rc5t583-regulator.c (limited to 'include/linux') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 8fb5f81c3569..4ad4e8d3c1ee 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -223,6 +223,16 @@ config REGULATOR_PCF50633 Say Y here to support the voltage regulators and convertors on PCF50633 +config REGULATOR_RC5T583 + tristate "RICOH RC5T583 Power regulators" + depends on MFD_RC5T583 + help + Select this option to enable the power regulator of RICOH + PMIC RC5T583. + This driver supports the control of different power rails of device + through regulator interface. The device supports multiple DCDC/LDO + outputs which can be controlled by i2c communication. + config REGULATOR_S5M8767 tristate "Samsung S5M8767A voltage regulator" depends on MFD_S5M_CORE diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 04ff3ead1312..dcc56dcca3a0 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o +obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c new file mode 100644 index 000000000000..37732f7c798d --- /dev/null +++ b/drivers/regulator/rc5t583-regulator.c @@ -0,0 +1,367 @@ +/* + * Regulator driver for RICOH RC5T583 power management chip. + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * Author: Laxman dewangan + * + * based on code + * Copyright (C) 2011 RICOH COMPANY,LTD + * + * + * 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 + +struct rc5t583_regulator_info { + int deepsleep_id; + + /* Regulator register address.*/ + uint8_t reg_en_reg; + uint8_t en_bit; + uint8_t reg_disc_reg; + uint8_t disc_bit; + uint8_t vout_reg; + uint8_t vout_mask; + uint8_t deepsleep_reg; + + /* Chip constraints on regulator behavior */ + int min_uV; + int max_uV; + int step_uV; + int nsteps; + + /* Regulator specific turn-on delay and voltage settling time*/ + int enable_uv_per_us; + int change_uv_per_us; + + /* Used by regulator core */ + struct regulator_desc desc; +}; + +struct rc5t583_regulator { + struct rc5t583_regulator_info *reg_info; + + /* Devices */ + struct device *dev; + struct rc5t583 *mfd; + struct regulator_dev *rdev; +}; + +static int rc5t583_reg_is_enabled(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + uint8_t control; + int ret; + + ret = rc5t583_read(reg->mfd->dev, ri->reg_en_reg, &control); + if (ret < 0) { + dev_err(&rdev->dev, + "Error in reading the control register 0x%02x\n", + ri->reg_en_reg); + return ret; + } + return !!(control & BIT(ri->en_bit)); +} + +static int rc5t583_reg_enable(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + int ret; + + ret = rc5t583_set_bits(reg->mfd->dev, ri->reg_en_reg, + (1 << ri->en_bit)); + if (ret < 0) { + dev_err(&rdev->dev, + "Error in setting bit of STATE register 0x%02x\n", + ri->reg_en_reg); + return ret; + } + return ret; +} + +static int rc5t583_reg_disable(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + int ret; + + ret = rc5t583_clear_bits(reg->mfd->dev, ri->reg_en_reg, + (1 << ri->en_bit)); + if (ret < 0) + dev_err(&rdev->dev, + "Error in clearing bit of STATE register 0x%02x\n", + ri->reg_en_reg); + + return ret; +} + +static int rc5t583_list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + return ri->min_uV + (ri->step_uV * selector); +} + +static int rc5t583_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + int ret; + if (selector > ri->nsteps) { + dev_err(&rdev->dev, "Invalid selector 0x%02x\n", selector); + return -EINVAL; + } + + ret = rc5t583_update(reg->mfd->dev, ri->vout_reg, + selector, ri->vout_mask); + if (ret < 0) + dev_err(&rdev->dev, + "Error in update voltage register 0x%02x\n", ri->vout_reg); + return ret; +} + +static int rc5t583_get_voltage_sel(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + struct rc5t583_regulator_info *ri = reg->reg_info; + uint8_t vsel; + int ret; + ret = rc5t583_read(reg->mfd->dev, ri->vout_reg, &vsel); + if (ret < 0) { + dev_err(&rdev->dev, + "Error in reading voltage register 0x%02x\n", ri->vout_reg); + return ret; + } + return vsel & ri->vout_mask; +} + +static int rc5t583_regulator_enable_time(struct regulator_dev *rdev) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + int vsel = rc5t583_get_voltage_sel(rdev); + int curr_uV = rc5t583_list_voltage(rdev, vsel); + return DIV_ROUND_UP(curr_uV, reg->reg_info->enable_uv_per_us); +} + +static int rc5t583_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); + int old_uV, new_uV; + old_uV = rc5t583_list_voltage(rdev, old_selector); + + if (old_uV < 0) + return old_uV; + + new_uV = rc5t583_list_voltage(rdev, new_selector); + if (new_uV < 0) + return new_uV; + + return DIV_ROUND_UP(abs(old_uV - new_uV), + reg->reg_info->change_uv_per_us); +} + + +static struct regulator_ops rc5t583_ops = { + .is_enabled = rc5t583_reg_is_enabled, + .enable = rc5t583_reg_enable, + .disable = rc5t583_reg_disable, + .enable_time = rc5t583_regulator_enable_time, + .get_voltage_sel = rc5t583_get_voltage_sel, + .set_voltage_sel = rc5t583_set_voltage_sel, + .list_voltage = rc5t583_list_voltage, + .set_voltage_time_sel = rc5t583_set_voltage_time_sel, +}; + +#define RC5T583_REG(_id, _en_reg, _en_bit, _disc_reg, _disc_bit, _vout_reg, \ + _vout_mask, _ds_reg, _min_mv, _max_mv, _step_uV, _nsteps, \ + _enable_mv) \ +{ \ + .reg_en_reg = RC5T583_REG_##_en_reg, \ + .en_bit = _en_bit, \ + .reg_disc_reg = RC5T583_REG_##_disc_reg, \ + .disc_bit = _disc_bit, \ + .vout_reg = RC5T583_REG_##_vout_reg, \ + .vout_mask = _vout_mask, \ + .deepsleep_reg = RC5T583_REG_##_ds_reg, \ + .min_uV = _min_mv * 1000, \ + .max_uV = _max_mv * 1000, \ + .step_uV = _step_uV, \ + .nsteps = _nsteps, \ + .enable_uv_per_us = _enable_mv * 1000, \ + .change_uv_per_us = 40 * 1000, \ + .deepsleep_id = RC5T583_DS_##_id, \ + .desc = { \ + .name = "rc5t583-regulator-"#_id, \ + .id = RC5T583_REGULATOR_##_id, \ + .n_voltages = _nsteps, \ + .ops = &rc5t583_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ +} + +static struct rc5t583_regulator_info rc5t583_reg_info[RC5T583_REGULATOR_MAX] = { + RC5T583_REG(DC0, DC0CTL, 0, DC0CTL, 1, DC0DAC, 0x7F, DC0DAC_DS, + 700, 1500, 12500, 0x41, 4), + RC5T583_REG(DC1, DC1CTL, 0, DC1CTL, 1, DC1DAC, 0x7F, DC1DAC_DS, + 700, 1500, 12500, 0x41, 14), + RC5T583_REG(DC2, DC2CTL, 0, DC2CTL, 1, DC2DAC, 0x7F, DC2DAC_DS, + 900, 2400, 12500, 0x79, 14), + RC5T583_REG(DC3, DC3CTL, 0, DC3CTL, 1, DC3DAC, 0x7F, DC3DAC_DS, + 900, 2400, 12500, 0x79, 14), + RC5T583_REG(LDO0, LDOEN2, 0, LDODIS2, 0, LDO0DAC, 0x7F, LDO0DAC_DS, + 900, 3400, 25000, 0x65, 160), + RC5T583_REG(LDO1, LDOEN2, 1, LDODIS2, 1, LDO1DAC, 0x7F, LDO1DAC_DS, + 900, 3400, 25000, 0x65, 160), + RC5T583_REG(LDO2, LDOEN2, 2, LDODIS2, 2, LDO2DAC, 0x7F, LDO2DAC_DS, + 900, 3400, 25000, 0x65, 160), + RC5T583_REG(LDO3, LDOEN2, 3, LDODIS2, 3, LDO3DAC, 0x7F, LDO3DAC_DS, + 900, 3400, 25000, 0x65, 160), + RC5T583_REG(LDO4, LDOEN2, 4, LDODIS2, 4, LDO4DAC, 0x3F, LDO4DAC_DS, + 750, 1500, 12500, 0x3D, 133), + RC5T583_REG(LDO5, LDOEN2, 5, LDODIS2, 5, LDO5DAC, 0x7F, LDO5DAC_DS, + 900, 3400, 25000, 0x65, 267), + RC5T583_REG(LDO6, LDOEN2, 6, LDODIS2, 6, LDO6DAC, 0x7F, LDO6DAC_DS, + 900, 3400, 25000, 0x65, 133), + RC5T583_REG(LDO7, LDOEN2, 7, LDODIS2, 7, LDO7DAC, 0x7F, LDO7DAC_DS, + 900, 3400, 25000, 0x65, 233), + RC5T583_REG(LDO8, LDOEN1, 0, LDODIS1, 0, LDO8DAC, 0x7F, LDO8DAC_DS, + 900, 3400, 25000, 0x65, 233), + RC5T583_REG(LDO9, LDOEN1, 1, LDODIS1, 1, LDO9DAC, 0x7F, LDO9DAC_DS, + 900, 3400, 25000, 0x65, 133), +}; + +static int __devinit rc5t583_regulator_probe(struct platform_device *pdev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); + struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); + struct regulator_init_data *reg_data; + struct rc5t583_regulator *reg = NULL; + struct rc5t583_regulator *regs; + struct regulator_dev *rdev; + struct rc5t583_regulator_info *ri; + int ret; + int id; + + if (!pdata) { + dev_err(&pdev->dev, "No platform data, exiting...\n"); + return -ENODEV; + } + + regs = devm_kzalloc(&pdev->dev, RC5T583_REGULATOR_MAX * + sizeof(struct rc5t583_regulator), GFP_KERNEL); + if (!regs) { + dev_err(&pdev->dev, "Memory allocation failed exiting..\n"); + return -ENOMEM; + } + + + for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) { + reg_data = pdata->reg_init_data[id]; + + /* No need to register if there is no regulator data */ + if (!reg_data) + continue; + + reg = ®s[id]; + ri = &rc5t583_reg_info[id]; + reg->reg_info = ri; + reg->mfd = rc5t583; + reg->dev = &pdev->dev; + + if (ri->deepsleep_id == RC5T583_DS_NONE) + goto skip_ext_pwr_config; + + ret = rc5t583_ext_power_req_config(rc5t583->dev, + ri->deepsleep_id, + pdata->regulator_ext_pwr_control[id], + pdata->regulator_deepsleep_slot[id]); + /* + * Configuring external control is not a major issue, + * just give warning. + */ + if (ret < 0) + dev_warn(&pdev->dev, + "Failed to configure ext control %d\n", id); + +skip_ext_pwr_config: + rdev = regulator_register(&ri->desc, &pdev->dev, + reg_data, reg, NULL); + if (IS_ERR_OR_NULL(rdev)) { + dev_err(&pdev->dev, "Failed to register regulator %s\n", + ri->desc.name); + ret = PTR_ERR(rdev); + goto clean_exit; + } + reg->rdev = rdev; + } + platform_set_drvdata(pdev, regs); + return 0; + +clean_exit: + while (--id > 0) + regulator_unregister(regs[id].rdev); + + return ret; +} + +static int __devexit rc5t583_regulator_remove(struct platform_device *pdev) +{ + struct rc5t583_regulator *regs = platform_get_drvdata(pdev); + int id; + + for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) + regulator_unregister(regs[id].rdev); + return 0; +} + +static struct platform_driver rc5t583_regulator_driver = { + .driver = { + .name = "rc5t583-regulator", + .owner = THIS_MODULE, + }, + .probe = rc5t583_regulator_probe, + .remove = __devexit_p(rc5t583_regulator_remove), +}; + +static int __init rc5t583_regulator_init(void) +{ + return platform_driver_register(&rc5t583_regulator_driver); +} +subsys_initcall(rc5t583_regulator_init); + +static void __exit rc5t583_regulator_exit(void) +{ + platform_driver_unregister(&rc5t583_regulator_driver); +} +module_exit(rc5t583_regulator_exit); + +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("RC5T583 regulator driver"); +MODULE_ALIAS("platform:rc5t583-regulator"); +MODULE_LICENSE("GPL V2"); diff --git a/include/linux/mfd/rc5t583.h b/include/linux/mfd/rc5t583.h index a2c61609d21d..b2c1f442d4ef 100644 --- a/include/linux/mfd/rc5t583.h +++ b/include/linux/mfd/rc5t583.h @@ -249,6 +249,26 @@ enum { RC5T583_EXT_PWRREQ2_CONTROL = 0x2, }; +enum { + RC5T583_REGULATOR_DC0, + RC5T583_REGULATOR_DC1, + RC5T583_REGULATOR_DC2, + RC5T583_REGULATOR_DC3, + RC5T583_REGULATOR_LDO0, + RC5T583_REGULATOR_LDO1, + RC5T583_REGULATOR_LDO2, + RC5T583_REGULATOR_LDO3, + RC5T583_REGULATOR_LDO4, + RC5T583_REGULATOR_LDO5, + RC5T583_REGULATOR_LDO6, + RC5T583_REGULATOR_LDO7, + RC5T583_REGULATOR_LDO8, + RC5T583_REGULATOR_LDO9, + + /* Should be last entry */ + RC5T583_REGULATOR_MAX, +}; + struct rc5t583 { struct device *dev; struct regmap *regmap; @@ -272,11 +292,20 @@ struct rc5t583 { * The board specific data is provided through this structure. * @irq_base: Irq base number on which this device registers their interrupts. * @enable_shutdown: Enable shutdown through the input pin "shutdown". + * @regulator_deepsleep_slot: The slot number on which device goes to sleep + * in device sleep mode. + * @regulator_ext_pwr_control: External power request regulator control. The + * regulator output enable/disable is controlled by the external + * power request input state. + * @reg_init_data: Regulator init data. */ struct rc5t583_platform_data { int irq_base; bool enable_shutdown; + int regulator_deepsleep_slot[RC5T583_REGULATOR_MAX]; + unsigned long regulator_ext_pwr_control[RC5T583_REGULATOR_MAX]; + struct regulator_init_data *reg_init_data[RC5T583_REGULATOR_MAX]; }; int rc5t583_write(struct device *dev, u8 reg, uint8_t val); -- cgit v1.2.3 From 0bf25a45386f284d591530ef174eaa9e44d84956 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 3 Apr 2012 13:39:44 -0700 Subject: Input: add support for LM8333 keypads This driver adds support for the keypad part of the LM8333 and is prepared for possible GPIO/PWM drivers. Note that this is not a MFD because you cannot disable the keypad functionality which, thus, has to be handled by the core anyhow. Signed-off-by: Wolfram Sang Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 10 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/lm8333.c | 236 ++++++++++++++++++++++++++++++++++++++++ include/linux/input/lm8333.h | 24 ++++ 4 files changed, 271 insertions(+) create mode 100644 drivers/input/keyboard/lm8333.c create mode 100644 include/linux/input/lm8333.h (limited to 'include/linux') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index f354813a13e8..7eaf93fe5128 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -309,6 +309,16 @@ config KEYBOARD_LM8323 To compile this driver as a module, choose M here: the module will be called lm8323. +config KEYBOARD_LM8333 + tristate "LM8333 keypad chip" + depends on I2C + help + If you say yes here you get support for the National Semiconductor + LM8333 keypad controller. + + To compile this driver as a module, choose M here: the + module will be called lm8333. + config KEYBOARD_LOCOMO tristate "LoCoMo Keyboard Support" depends on SHARP_LOCOMO diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index df7061f12918..b03b02456a82 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o +obj-$(CONFIG_KEYBOARD_LM8333) += lm8333.o obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c new file mode 100644 index 000000000000..9a8c4a6cf5c6 --- /dev/null +++ b/drivers/input/keyboard/lm8333.c @@ -0,0 +1,236 @@ +/* + * LM8333 keypad driver + * Copyright (C) 2012 Wolfram Sang, Pengutronix + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LM8333_FIFO_READ 0x20 +#define LM8333_DEBOUNCE 0x22 +#define LM8333_READ_INT 0xD0 +#define LM8333_ACTIVE 0xE4 +#define LM8333_READ_ERROR 0xF0 + +#define LM8333_KEYPAD_IRQ (1 << 0) +#define LM8333_ERROR_IRQ (1 << 3) + +#define LM8333_ERROR_KEYOVR 0x04 +#define LM8333_ERROR_FIFOOVR 0x40 + +#define LM8333_FIFO_TRANSFER_SIZE 16 + +#define LM8333_ROW_SHIFT 4 +#define LM8333_NUM_ROWS 8 + + +struct lm8333 { + struct i2c_client *client; + struct input_dev *input; + unsigned short keycodes[LM8333_NUM_ROWS << LM8333_ROW_SHIFT]; +}; + +/* The accessors try twice because the first access may be needed for wakeup */ +#define LM8333_READ_RETRIES 2 + +int lm8333_read8(struct lm8333 *lm8333, u8 cmd) +{ + int retries = 0, ret; + + do { + ret = i2c_smbus_read_byte_data(lm8333->client, cmd); + } while (ret < 0 && retries++ < LM8333_READ_RETRIES); + + return ret; +} + +int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val) +{ + int retries = 0, ret; + + do { + ret = i2c_smbus_write_byte_data(lm8333->client, cmd, val); + } while (ret < 0 && retries++ < LM8333_READ_RETRIES); + + return ret; +} + +int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf) +{ + int retries = 0, ret; + + do { + ret = i2c_smbus_read_i2c_block_data(lm8333->client, + cmd, len, buf); + } while (ret < 0 && retries++ < LM8333_READ_RETRIES); + + return ret; +} + +static void lm8333_key_handler(struct lm8333 *lm8333) +{ + struct input_dev *input = lm8333->input; + u8 keys[LM8333_FIFO_TRANSFER_SIZE]; + u8 code, pressed; + int i, ret; + + ret = lm8333_read_block(lm8333, LM8333_FIFO_READ, + LM8333_FIFO_TRANSFER_SIZE, keys); + if (ret != LM8333_FIFO_TRANSFER_SIZE) { + dev_err(&lm8333->client->dev, + "Error %d while reading FIFO\n", ret); + return; + } + + for (i = 0; keys[i] && i < LM8333_FIFO_TRANSFER_SIZE; i++) { + pressed = keys[i] & 0x80; + code = keys[i] & 0x7f; + + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, lm8333->keycodes[code], pressed); + } + + input_sync(input); +} + +static irqreturn_t lm8333_irq_thread(int irq, void *data) +{ + struct lm8333 *lm8333 = data; + u8 status = lm8333_read8(lm8333, LM8333_READ_INT); + + if (!status) + return IRQ_NONE; + + if (status & LM8333_ERROR_IRQ) { + u8 err = lm8333_read8(lm8333, LM8333_READ_ERROR); + + if (err & (LM8333_ERROR_KEYOVR | LM8333_ERROR_FIFOOVR)) { + u8 dummy[LM8333_FIFO_TRANSFER_SIZE]; + + lm8333_read_block(lm8333, LM8333_FIFO_READ, + LM8333_FIFO_TRANSFER_SIZE, dummy); + } + dev_err(&lm8333->client->dev, "Got error %02x\n", err); + } + + if (status & LM8333_KEYPAD_IRQ) + lm8333_key_handler(lm8333); + + return IRQ_HANDLED; +} + +static int __devinit lm8333_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct lm8333_platform_data *pdata = client->dev.platform_data; + struct lm8333 *lm8333; + struct input_dev *input; + int err, active_time; + + if (!pdata) + return -EINVAL; + + active_time = pdata->active_time ?: 500; + if (active_time / 3 <= pdata->debounce_time / 3) { + dev_err(&client->dev, "Active time not big enough!\n"); + return -EINVAL; + } + + lm8333 = kzalloc(sizeof(*lm8333), GFP_KERNEL); + input = input_allocate_device(); + if (!lm8333 || !input) { + err = -ENOMEM; + goto free_mem; + } + + lm8333->client = client; + lm8333->input = input; + + input->name = client->name; + input->dev.parent = &client->dev; + input->id.bustype = BUS_I2C; + + input->keycode = lm8333->keycodes; + input->keycodesize = sizeof(lm8333->keycodes[0]); + input->keycodemax = ARRAY_SIZE(lm8333->keycodes); + input->evbit[0] = BIT_MASK(EV_KEY); + input_set_capability(input, EV_MSC, MSC_SCAN); + + matrix_keypad_build_keymap(pdata->matrix_data, LM8333_ROW_SHIFT, + input->keycode, input->keybit); + + if (pdata->debounce_time) { + err = lm8333_write8(lm8333, LM8333_DEBOUNCE, + pdata->debounce_time / 3); + if (err) + dev_warn(&client->dev, "Unable to set debounce time\n"); + } + + if (pdata->active_time) { + err = lm8333_write8(lm8333, LM8333_ACTIVE, + pdata->active_time / 3); + if (err) + dev_warn(&client->dev, "Unable to set active time\n"); + } + + err = request_threaded_irq(client->irq, NULL, lm8333_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "lm8333", lm8333); + if (err) + goto free_mem; + + err = input_register_device(input); + if (err) + goto free_irq; + + i2c_set_clientdata(client, lm8333); + return 0; + + free_irq: + free_irq(client->irq, lm8333); + free_mem: + input_free_device(input); + kfree(lm8333); + return err; +} + +static int __devexit lm8333_remove(struct i2c_client *client) +{ + struct lm8333 *lm8333 = i2c_get_clientdata(client); + + free_irq(client->irq, lm8333); + input_unregister_device(lm8333->input); + kfree(lm8333); + + return 0; +} + +static const struct i2c_device_id lm8333_id[] = { + { "lm8333", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm8333_id); + +static struct i2c_driver lm8333_driver = { + .driver = { + .name = "lm8333", + .owner = THIS_MODULE, + }, + .probe = lm8333_probe, + .remove = __devexit_p(lm8333_remove), + .id_table = lm8333_id, +}; +module_i2c_driver(lm8333_driver); + +MODULE_AUTHOR("Wolfram Sang "); +MODULE_DESCRIPTION("LM8333 keyboard driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/input/lm8333.h b/include/linux/input/lm8333.h new file mode 100644 index 000000000000..79f918c6e8c5 --- /dev/null +++ b/include/linux/input/lm8333.h @@ -0,0 +1,24 @@ +/* + * public include for LM8333 keypad driver - same license as driver + * Copyright (C) 2012 Wolfram Sang, Pengutronix + */ + +#ifndef _LM8333_H +#define _LM8333_H + +struct lm8333; + +struct lm8333_platform_data { + /* Keymap data */ + const struct matrix_keymap_data *matrix_data; + /* Active timeout before enter HALT mode in microseconds */ + unsigned active_time; + /* Debounce interval in microseconds */ + unsigned debounce_time; +}; + +extern int lm8333_read8(struct lm8333 *lm8333, u8 cmd); +extern int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val); +extern int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf); + +#endif /* _LM8333_H */ -- cgit v1.2.3 From fa7f86d157781515b74d658120552eafd890f4de Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Apr 2012 23:50:15 -0700 Subject: Input: serio - add helper macro for serio_driver boilerplate This patch introduces the module_serio_driver macro which is a convenience macro for serio driver modules similar to module_platform_driver. It is intended to be used by drivers which init/exit section does nothing but registers/unregisters the serio driver. By using this macro it is possible to eliminate a few lines of boilerplate code per serio driver. Based on work done by Lars-Peter Clausen for other buses (i2c and spi). Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- include/linux/serio.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/linux') diff --git a/include/linux/serio.h b/include/linux/serio.h index ca82861b0e46..6d6cfd3e94a3 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -96,6 +96,19 @@ int __must_check __serio_register_driver(struct serio_driver *drv, void serio_unregister_driver(struct serio_driver *drv); +/** + * module_serio_driver() - Helper macro for registering a serio driver + * @__serio_driver: serio_driver struct + * + * Helper macro for serio 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_serio_driver(__serio_driver) \ + module_driver(__serio_driver, serio_register_driver, \ + serio_unregister_driver) + static inline int serio_write(struct serio *serio, unsigned char data) { if (serio->write) -- cgit v1.2.3 From 45b2604eaaa105223ce60117b0482ca8a488f9c4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Apr 2012 23:51:08 -0700 Subject: Input: gameport - add helper macro for gameport_driver boilerplate This patch introduces the module_gameport_driver macro which is a convenience macro for gameport driver modules similar to module_platform_driver. It is intended to be used by drivers which init/exit section does nothing but registers/unregisters the gameport driver. By using this macro it is possible to eliminate a few lines of boilerplate code per gameport driver. Based on work done by Lars-Peter Clausen for other buses (i2c and spi). Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- include/linux/gameport.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/linux') diff --git a/include/linux/gameport.h b/include/linux/gameport.h index b456b08d70ed..b986be513406 100644 --- a/include/linux/gameport.h +++ b/include/linux/gameport.h @@ -153,6 +153,19 @@ int __must_check __gameport_register_driver(struct gameport_driver *drv, void gameport_unregister_driver(struct gameport_driver *drv); +/** + * module_gameport_driver() - Helper macro for registering a gameport driver + * @__gameport_driver: gameport_driver struct + * + * Helper macro for gameport 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_gameport_driver(__gameport_driver) \ + module_driver(__gameport_driver, gameport_register_driver, \ + gameport_unregister_driver) + #endif /* __KERNEL__ */ #define GAMEPORT_MODE_DISABLED 0 -- cgit v1.2.3 From f142af2e2064546ac470e8690acbd189b3584e67 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 4 Apr 2012 04:33:19 +0000 Subject: stmmac: Allow stmmac to work with other PHY buses(v3). As stmmac mdio bus name prefix is hardcoded in the driver, this allows only phys on stmmac mdio buses to connect, however stmmac should allow phys on other mdio buses too. This patch adds new variable phy_bus_name to plat_stmmacenet_data struct to let the BSP decide which phy bus to be used by stmmac driver. A typical use-case is to have generic MDIO buses like mdio-gpio on top of stmmac. Signed-off-by: Srinivas Kandagatla Acked-by: Florian Fainelli Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 8 +++++++- include/linux/stmmac.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e85ffbd54830..860519c4d9a1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -307,7 +307,13 @@ static int stmmac_init_phy(struct net_device *dev) priv->speed = 0; priv->oldduplex = -1; - snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", priv->plat->bus_id); + if (priv->plat->phy_bus_name) + snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x", + priv->plat->phy_bus_name, priv->plat->bus_id); + else + snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", + priv->plat->bus_id); + snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, priv->plat->phy_addr); pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id); diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 0dddc9e42b6b..172b5e15df2e 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -39,6 +39,7 @@ struct stmmac_mdio_bus_data { }; struct plat_stmmacenet_data { + char *phy_bus_name; int bus_id; int phy_addr; int interface; -- cgit v1.2.3 From 55f9a4d6facb35198ddb88a8fe21ca2ee753af7a Mon Sep 17 00:00:00 2001 From: Deepak SIKRI Date: Wed, 4 Apr 2012 04:33:20 +0000 Subject: stmmac: Define CSUM offload engine Types This patch explicitly defines the CSUM offload engine type which need (not mandatory) to be passed from the platform code. STMMAC core supports two check sum offload engine types- Type-1 & Type-2. Also, there are STMMAC cores that do not have the check sum offload capabilities. The behaviour of Type-1 & Type-2 cores related to provision of checksum increases the packet length for Type-1 cores by 2, as the checksum is appended at the end of data packet and the same is made accountable in the DMA status. The STMMAC cores beyond Version-3.5 provide HW interface registers which allows the user to read the HW capabilities, while to support the previous cores the information related to HW capabilities has to be provided from the platform code. The Type-1 cores which do not have the HW register interface need this information. This patch also updates the driver's doc. Signed-off-by: Deepak Sikri Hacked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- Documentation/networking/stmmac.txt | 3 +++ include/linux/stmmac.h | 5 +++++ 2 files changed, 8 insertions(+) (limited to 'include/linux') diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt index d0aeeadd264b..61f40a3fa7ea 100644 --- a/Documentation/networking/stmmac.txt +++ b/Documentation/networking/stmmac.txt @@ -116,6 +116,7 @@ and detailed below as well: int has_gmac; int enh_desc; int tx_coe; + int rx_coe; int bugged_jumbo; int pmt; int force_sf_dma_mode; @@ -140,6 +141,8 @@ Where: o has_gmac: uses the GMAC core. o enh_desc: if sets the MAC will use the enhanced descriptor structure. o tx_coe: core is able to perform the tx csum in HW. + o rx_coe: the supports three check sum offloading engine types: + type_1, type_2 (full csum) and no RX coe. o bugged_jumbo: some HWs are not able to perform the csum in HW for over-sized frames due to limited buffer sizes. Setting this flag the csum will be done in SW on diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 172b5e15df2e..a9b4d6cb96e9 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -28,6 +28,10 @@ #include +#define STMMAC_RX_COE_NONE 0 +#define STMMAC_RX_COE_TYPE1 1 +#define STMMAC_RX_COE_TYPE2 2 + /* Platfrom data for platform device structure's platform_data field */ struct stmmac_mdio_bus_data { @@ -49,6 +53,7 @@ struct plat_stmmacenet_data { int has_gmac; int enh_desc; int tx_coe; + int rx_coe; int bugged_jumbo; int pmt; int force_sf_dma_mode; -- cgit v1.2.3 From faeae3fa0f3a243f677cf606aa87d0d99c225165 Mon Sep 17 00:00:00 2001 From: Deepak SIKRI Date: Wed, 4 Apr 2012 04:33:22 +0000 Subject: stmmac: Define MDC clock selection macros The patch adds the macros to be used for MDC clock selection. The MDC clock frequency is based on scaled system clock, and has to be confined to a range of 1-2.5 MHz. Based on the input CSR clock, the scaling factor has to be selected. The platform specific code will provide the default value of this scaling factor, based on the input CSR clock. There is an option to set MDC clock higher than the IEEE 802.3 specified frequency limit of 2.5 MHz. This applies for the interfacing chips that support higher MDC clocks. The resultant higher clock of 12.5 MHz requires additional Macros to be defined for the clock divider corresponding to the to the following selection. ----------------------------------------- Selection MDC Clock ----------------------------------------- 1000 clk_csr_i/4 1001 clk_csr_i/6 1010 clk_csr_i/8 1011 clk_csr_i/10 1100 clk_csr_i/12 1101 clk_csr_i/14 1110 clk_csr_i/16 1111 clk_csr_i/18 This support has to be added both in the include file, as well as driver. The driver need to program the registers based on the interfacing chips. This would be more board specific information and needs to be passed through the platform code to the driver. This work would be carried out in the future patch set release. Signed-off-by: Deepak Sikri Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- include/linux/stmmac.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'include/linux') diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index a9b4d6cb96e9..e5292828b684 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -32,6 +32,34 @@ #define STMMAC_RX_COE_TYPE1 1 #define STMMAC_RX_COE_TYPE2 2 +/* Define the macros for CSR clock range parameters to be passed by + * platform code. + * This could also be configured at run time using CPU freq framework. */ + +/* MDC Clock Selection define*/ +#define STMMAC_CSR_60_100M 0 /* MDC = clk_scr_i/42 */ +#define STMMAC_CSR_100_150M 1 /* MDC = clk_scr_i/62 */ +#define STMMAC_CSR_20_35M 2 /* MDC = clk_scr_i/16 */ +#define STMMAC_CSR_35_60M 3 /* MDC = clk_scr_i/26 */ +#define STMMAC_CSR_150_250M 4 /* MDC = clk_scr_i/102 */ +#define STMMAC_CSR_250_300M 5 /* MDC = clk_scr_i/122 */ + +/* FIXME: The MDC clock could be set higher than the IEEE 802.3 + * specified frequency limit 0f 2.5 MHz, by programming a clock divider + * of value different than the above defined values. The resultant MDIO + * clock frequency of 12.5 MHz is applicable for the interfacing chips + * supporting higher MDC clocks. + * The MDC clock selection macros need to be defined for MDC clock rate + * of 12.5 MHz, corresponding to the following selection. + * 1000 clk_csr_i/4 + * 1001 clk_csr_i/6 + * 1010 clk_csr_i/8 + * 1011 clk_csr_i/10 + * 1100 clk_csr_i/12 + * 1101 clk_csr_i/14 + * 1110 clk_csr_i/16 + * 1111 clk_csr_i/18 */ + /* Platfrom data for platform device structure's platform_data field */ struct stmmac_mdio_bus_data { -- cgit v1.2.3 From 8327eb65e795ba4f922bf7e531cd312875f0dc29 Mon Sep 17 00:00:00 2001 From: Deepak SIKRI Date: Wed, 4 Apr 2012 04:33:23 +0000 Subject: stmmac: re-work the internal GMAC DMA platf parameters This patch re-works the internal GMAC DMA parameters passed from the platform. In the past, we only passed the pbl but, with new core, other parameters can be passed and are mandatory on some platforms. New parameters are documented in stmmac.txt because this patch has an impact for many platforms. Signed-off-by: Shiraz Hashim Signed-off-by: Vikas Manocha Signed-off-by: Deepak Sikri Hacked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- Documentation/networking/stmmac.txt | 24 +++++++++++-- drivers/net/ethernet/stmicro/stmmac/common.h | 3 +- drivers/net/ethernet/stmicro/stmmac/dwmac1000.h | 2 +- .../net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 42 +++++++++++++++++++--- drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c | 6 ++-- drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h | 1 + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++- drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 3 +- include/linux/stmmac.h | 20 ++++++++++- 9 files changed, 89 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt index 61f40a3fa7ea..eacb640286b1 100644 --- a/Documentation/networking/stmmac.txt +++ b/Documentation/networking/stmmac.txt @@ -111,7 +111,7 @@ and detailed below as well: int phy_addr; int interface; struct stmmac_mdio_bus_data *mdio_bus_data; - int pbl; + struct stmmac_dma_cfg *dma_cfg; int clk_csr; int has_gmac; int enh_desc; @@ -163,7 +163,7 @@ Where: o custom_cfg: this is a custom configuration that can be passed while initialising the resources. -The we have: +For MDIO bus The we have: struct stmmac_mdio_bus_data { int bus_id; @@ -180,10 +180,28 @@ Where: o irqs: list of IRQs, one per PHY. o probed_phy_irq: if irqs is NULL, use this for probed PHY. + +For DMA engine we have the following internal fields that should be +tuned according to the HW capabilities. + +struct stmmac_dma_cfg { + int pbl; + int fixed_burst; + int burst_len_supported; +}; + +Where: + o pbl: Programmable Burst Length + o fixed_burst: program the DMA to use the fixed burst mode + o burst_len: this is the value we put in the register + supported values are provided as macros in + linux/stmmac.h header file. + +--- + Below an example how the structures above are using on ST platforms. static struct plat_stmmacenet_data stxYYY_ethernet_platform_data = { - .pbl = 32, .has_gmac = 0, .enh_desc = 0, .fix_mac_speed = stxYYY_ethernet_fix_mac_speed, diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index eec8d34b6c88..b14829f8085d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -236,7 +236,8 @@ struct stmmac_desc_ops { struct stmmac_dma_ops { /* DMA core initialization */ - int (*init) (void __iomem *ioaddr, int pbl, u32 dma_tx, u32 dma_rx); + int (*init) (void __iomem *ioaddr, int pbl, int fb, int burst_len, + u32 dma_tx, u32 dma_rx); /* Dump DMA registers */ void (*dump_regs) (void __iomem *ioaddr); /* Set tx/rx threshold in the csr6 register diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index cfcef0ea0fa5..54339a78e358 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -142,7 +142,7 @@ enum rx_tx_priority_ratio { #define DMA_BUS_MODE_RPBL_MASK 0x003e0000 /* Rx-Programmable Burst Len */ #define DMA_BUS_MODE_RPBL_SHIFT 17 #define DMA_BUS_MODE_USP 0x00800000 -#define DMA_BUS_MODE_4PBL 0x01000000 +#define DMA_BUS_MODE_PBL 0x01000000 #define DMA_BUS_MODE_AAL 0x02000000 /* DMA CRS Control and Status Register Mapping */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index 4d5402a1d262..3675c5731565 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -30,8 +30,8 @@ #include "dwmac1000.h" #include "dwmac_dma.h" -static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, u32 dma_tx, - u32 dma_rx) +static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, int fb, + int burst_len, u32 dma_tx, u32 dma_rx) { u32 value = readl(ioaddr + DMA_BUS_MODE); int limit; @@ -48,15 +48,47 @@ static int dwmac1000_dma_init(void __iomem *ioaddr, int pbl, u32 dma_tx, if (limit < 0) return -EBUSY; - value = /* DMA_BUS_MODE_FB | */ DMA_BUS_MODE_4PBL | - ((pbl << DMA_BUS_MODE_PBL_SHIFT) | - (pbl << DMA_BUS_MODE_RPBL_SHIFT)); + /* + * Set the DMA PBL (Programmable Burst Length) mode + * Before stmmac core 3.50 this mode bit was 4xPBL, and + * post 3.5 mode bit acts as 8*PBL. + * For core rev < 3.5, when the core is set for 4xPBL mode, the + * DMA transfers the data in 4, 8, 16, 32, 64 & 128 beats + * depending on pbl value. + * For core rev > 3.5, when the core is set for 8xPBL mode, the + * DMA transfers the data in 8, 16, 32, 64, 128 & 256 beats + * depending on pbl value. + */ + value = DMA_BUS_MODE_PBL | ((pbl << DMA_BUS_MODE_PBL_SHIFT) | + (pbl << DMA_BUS_MODE_RPBL_SHIFT)); + + /* Set the Fixed burst mode */ + if (fb) + value |= DMA_BUS_MODE_FB; #ifdef CONFIG_STMMAC_DA value |= DMA_BUS_MODE_DA; /* Rx has priority over tx */ #endif writel(value, ioaddr + DMA_BUS_MODE); + /* In case of GMAC AXI configuration, program the DMA_AXI_BUS_MODE + * for supported bursts. + * + * Note: This is applicable only for revision GMACv3.61a. For + * older version this register is reserved and shall have no + * effect. + * + * Note: + * For Fixed Burst Mode: if we directly write 0xFF to this + * register using the configurations pass from platform code, + * this would ensure that all bursts supported by core are set + * and those which are not supported would remain ineffective. + * + * For Non Fixed Burst Mode: provide the maximum value of the + * burst length. Any burst equal or below the provided burst + * length would be allowed to perform. */ + writel(burst_len, ioaddr + DMA_AXI_BUS_MODE); + /* Mask interrupts by writing to CSR7 */ writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index bc17fd08b55d..92ed2e07609e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -32,8 +32,8 @@ #include "dwmac100.h" #include "dwmac_dma.h" -static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, u32 dma_tx, - u32 dma_rx) +static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, int fb, + int burst_len, u32 dma_tx, u32 dma_rx) { u32 value = readl(ioaddr + DMA_BUS_MODE); int limit; @@ -52,7 +52,7 @@ static int dwmac100_dma_init(void __iomem *ioaddr, int pbl, u32 dma_tx, /* Enable Application Access by writing to DMA CSR0 */ writel(DMA_BUS_MODE_DEFAULT | (pbl << DMA_BUS_MODE_PBL_SHIFT), - ioaddr + DMA_BUS_MODE); + ioaddr + DMA_BUS_MODE); /* Mask interrupts by writing to CSR7 */ writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index 437edacd602e..6e0360f9cfde 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -32,6 +32,7 @@ #define DMA_CONTROL 0x00001018 /* Ctrl (Operational Mode) */ #define DMA_INTR_ENA 0x0000101c /* Interrupt Enable */ #define DMA_MISSED_FRAME_CTR 0x00001020 /* Missed Frame Counter */ +#define DMA_AXI_BUS_MODE 0x00001028 /* AXI Bus Mode */ #define DMA_CUR_TX_BUF_ADDR 0x00001050 /* Current Host Tx Buffer */ #define DMA_CUR_RX_BUF_ADDR 0x00001054 /* Current Host Rx Buffer */ #define DMA_HW_FEATURE 0x00001058 /* HW Feature Register */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 84f6b348ec70..933f63c4b2f3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -944,7 +944,9 @@ static int stmmac_open(struct net_device *dev) init_dma_desc_rings(dev); /* DMA initialization and SW reset */ - ret = priv->hw->dma->init(priv->ioaddr, priv->plat->pbl, + ret = priv->hw->dma->init(priv->ioaddr, priv->plat->dma_cfg->pbl, + priv->plat->dma_cfg->fixed_burst, + priv->plat->dma_cfg->burst_len, priv->dma_tx_phy, priv->dma_rx_phy); if (ret < 0) { pr_err("%s: DMA initialization failed\n", __func__); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index da66ed7c3c5d..65e0f98520d6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -35,7 +35,8 @@ static void stmmac_default_data(void) plat_dat.bus_id = 1; plat_dat.phy_addr = 0; plat_dat.interface = PHY_INTERFACE_MODE_GMII; - plat_dat.pbl = 32; + plat_dat.dma_cfg->pbl = 32; + plat_dat.dma_cfg->burst_len = DMA_AXI_BLEN_256; plat_dat.clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ plat_dat.has_gmac = 1; plat_dat.force_sf_dma_mode = 1; diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index e5292828b684..4aef9baff12b 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -60,6 +60,18 @@ * 1110 clk_csr_i/16 * 1111 clk_csr_i/18 */ +/* AXI DMA Burst length suported */ +#define DMA_AXI_BLEN_4 (1 << 1) +#define DMA_AXI_BLEN_8 (1 << 2) +#define DMA_AXI_BLEN_16 (1 << 3) +#define DMA_AXI_BLEN_32 (1 << 4) +#define DMA_AXI_BLEN_64 (1 << 5) +#define DMA_AXI_BLEN_128 (1 << 6) +#define DMA_AXI_BLEN_256 (1 << 7) +#define DMA_AXI_BLEN_ALL (DMA_AXI_BLEN_4 | DMA_AXI_BLEN_8 | DMA_AXI_BLEN_16 \ + | DMA_AXI_BLEN_32 | DMA_AXI_BLEN_64 \ + | DMA_AXI_BLEN_128 | DMA_AXI_BLEN_256) + /* Platfrom data for platform device structure's platform_data field */ struct stmmac_mdio_bus_data { @@ -70,13 +82,19 @@ struct stmmac_mdio_bus_data { int probed_phy_irq; }; +struct stmmac_dma_cfg { + int pbl; + int fixed_burst; + int burst_len; +}; + struct plat_stmmacenet_data { char *phy_bus_name; int bus_id; int phy_addr; int interface; struct stmmac_mdio_bus_data *mdio_bus_data; - int pbl; + struct stmmac_dma_cfg *dma_cfg; int clk_csr; int has_gmac; int enh_desc; -- cgit v1.2.3 From 18f05d64ec36e27892cc0f55be707762aae053a1 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Wed, 4 Apr 2012 04:33:26 +0000 Subject: stmmac: extend CSR Clock Range programming The CSR Clock Range has been reworked and new macros has been added in the platform header to allow the CSR Clock Range selection in the GMII Address Register. The previous work didn't add the other fields that can be used to achieve MDC clock of frequency higher than the IEEE 802.3 specified frequency limit of 2.5 MHz and program a clock divider of lower value. On such platforms, these are used indeed so this patch adds them. Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 4 +-- include/linux/stmmac.h | 33 ++++++++++++----------- 2 files changed, 19 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 83292f4edc6b..1a420142f6ac 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -70,7 +70,7 @@ static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) int data; u16 regValue = (((phyaddr << 11) & (0x0000F800)) | ((phyreg << 6) & (0x000007C0))); - regValue |= MII_BUSY | ((priv->plat->clk_csr & 7) << 2); + regValue |= MII_BUSY | ((priv->plat->clk_csr & 0xF) << 2); if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address)) return -EBUSY; @@ -106,7 +106,7 @@ static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, (((phyaddr << 11) & (0x0000F800)) | ((phyreg << 6) & (0x000007C0))) | MII_WRITE; - value |= MII_BUSY | ((priv->plat->clk_csr & 7) << 2); + value |= MII_BUSY | ((priv->plat->clk_csr & 0xF) << 2); /* Wait until any existing MII operation is complete */ if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address)) diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 4aef9baff12b..cf6403186359 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -37,28 +37,29 @@ * This could also be configured at run time using CPU freq framework. */ /* MDC Clock Selection define*/ -#define STMMAC_CSR_60_100M 0 /* MDC = clk_scr_i/42 */ -#define STMMAC_CSR_100_150M 1 /* MDC = clk_scr_i/62 */ -#define STMMAC_CSR_20_35M 2 /* MDC = clk_scr_i/16 */ -#define STMMAC_CSR_35_60M 3 /* MDC = clk_scr_i/26 */ -#define STMMAC_CSR_150_250M 4 /* MDC = clk_scr_i/102 */ -#define STMMAC_CSR_250_300M 5 /* MDC = clk_scr_i/122 */ - -/* FIXME: The MDC clock could be set higher than the IEEE 802.3 +#define STMMAC_CSR_60_100M 0x0 /* MDC = clk_scr_i/42 */ +#define STMMAC_CSR_100_150M 0x1 /* MDC = clk_scr_i/62 */ +#define STMMAC_CSR_20_35M 0x2 /* MDC = clk_scr_i/16 */ +#define STMMAC_CSR_35_60M 0x3 /* MDC = clk_scr_i/26 */ +#define STMMAC_CSR_150_250M 0x4 /* MDC = clk_scr_i/102 */ +#define STMMAC_CSR_250_300M 0x5 /* MDC = clk_scr_i/122 */ + +/* The MDC clock could be set higher than the IEEE 802.3 * specified frequency limit 0f 2.5 MHz, by programming a clock divider * of value different than the above defined values. The resultant MDIO * clock frequency of 12.5 MHz is applicable for the interfacing chips * supporting higher MDC clocks. * The MDC clock selection macros need to be defined for MDC clock rate * of 12.5 MHz, corresponding to the following selection. - * 1000 clk_csr_i/4 - * 1001 clk_csr_i/6 - * 1010 clk_csr_i/8 - * 1011 clk_csr_i/10 - * 1100 clk_csr_i/12 - * 1101 clk_csr_i/14 - * 1110 clk_csr_i/16 - * 1111 clk_csr_i/18 */ + */ +#define STMMAC_CSR_I_4 0x8 /* clk_csr_i/4 */ +#define STMMAC_CSR_I_6 0x9 /* clk_csr_i/6 */ +#define STMMAC_CSR_I_8 0xA /* clk_csr_i/8 */ +#define STMMAC_CSR_I_10 0xB /* clk_csr_i/10 */ +#define STMMAC_CSR_I_12 0xC /* clk_csr_i/12 */ +#define STMMAC_CSR_I_14 0xD /* clk_csr_i/14 */ +#define STMMAC_CSR_I_16 0xE /* clk_csr_i/16 */ +#define STMMAC_CSR_I_18 0xF /* clk_csr_i/18 */ /* AXI DMA Burst length suported */ #define DMA_AXI_BLEN_4 (1 << 1) -- cgit v1.2.3 From 2615598fc100451c71b83d06bdf5faead619a40e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 4 Apr 2012 12:16:26 +0000 Subject: team: add binary option type For transfering generic binary data (e.g. BPF code), introduce new binary option type. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 30 ++++++++++++++++++++++++++---- include/linux/if_team.h | 8 ++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 0db6e66ea98d..ea96f822de52 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1145,10 +1145,7 @@ team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = { }, [TEAM_ATTR_OPTION_CHANGED] = { .type = NLA_FLAG }, [TEAM_ATTR_OPTION_TYPE] = { .type = NLA_U8 }, - [TEAM_ATTR_OPTION_DATA] = { - .type = NLA_BINARY, - .len = TEAM_STRING_MAX_LEN, - }, + [TEAM_ATTR_OPTION_DATA] = { .type = NLA_BINARY }, }; static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) @@ -1257,6 +1254,7 @@ static int team_nl_fill_options_get(struct sk_buff *skb, list_for_each_entry(option, &team->option_list, list) { struct nlattr *option_item; long arg; + struct team_option_binary tbinary; /* Include only changed options if fill all mode is not on */ if (!fillall && !option->changed) @@ -1290,6 +1288,15 @@ static int team_nl_fill_options_get(struct sk_buff *skb, (char *) arg)) goto nla_put_failure; break; + case TEAM_OPTION_TYPE_BINARY: + if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) + goto nla_put_failure; + arg = (long) &tbinary; + team_option_get(team, option, &arg); + if (nla_put(skb, TEAM_ATTR_OPTION_DATA, + tbinary.data_len, tbinary.data)) + goto nla_put_failure; + break; default: BUG(); } @@ -1374,6 +1381,9 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) case NLA_STRING: opt_type = TEAM_OPTION_TYPE_STRING; break; + case NLA_BINARY: + opt_type = TEAM_OPTION_TYPE_BINARY; + break; default: goto team_put; } @@ -1382,19 +1392,31 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) list_for_each_entry(option, &team->option_list, list) { long arg; struct nlattr *opt_data_attr; + struct team_option_binary tbinary; + int data_len; if (option->type != opt_type || strcmp(option->name, opt_name)) continue; opt_found = true; opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA]; + data_len = nla_len(opt_data_attr); switch (opt_type) { case TEAM_OPTION_TYPE_U32: arg = nla_get_u32(opt_data_attr); break; case TEAM_OPTION_TYPE_STRING: + if (data_len > TEAM_STRING_MAX_LEN) { + err = -EINVAL; + goto team_put; + } arg = (long) nla_data(opt_data_attr); break; + case TEAM_OPTION_TYPE_BINARY: + tbinary.data_len = data_len; + tbinary.data = nla_data(opt_data_attr); + arg = (long) &tbinary; + break; default: BUG(); } diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 58404b0c5010..41163ac14ab4 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -68,6 +68,7 @@ struct team_mode_ops { enum team_option_type { TEAM_OPTION_TYPE_U32, TEAM_OPTION_TYPE_STRING, + TEAM_OPTION_TYPE_BINARY, }; struct team_option { @@ -82,6 +83,13 @@ struct team_option { bool removed; }; +struct team_option_binary { + u32 data_len; + void *data; +}; + +#define team_optarg_tbinary(arg) (*((struct team_option_binary **) arg)) + struct team_mode { struct list_head list; const char *kind; -- cgit v1.2.3 From 9899b81e7ca5c285b825ff10ca9357dd18813d83 Mon Sep 17 00:00:00 2001 From: Mike Sinkovsky Date: Wed, 4 Apr 2012 19:33:53 +0000 Subject: Ethernet driver for the WIZnet W5300 chip Based on original driver from chip manufacturer, but nearly full rewite. Tested and used in production with Blackfin BF531 embedded processor. Signed-off-by: Mike Sinkovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/wiznet/Kconfig | 65 ++++ drivers/net/ethernet/wiznet/Makefile | 1 + drivers/net/ethernet/wiznet/w5300.c | 722 +++++++++++++++++++++++++++++++++++ include/linux/platform_data/wiznet.h | 24 ++ 6 files changed, 814 insertions(+) create mode 100644 drivers/net/ethernet/wiznet/Kconfig create mode 100644 drivers/net/ethernet/wiznet/Makefile create mode 100644 drivers/net/ethernet/wiznet/w5300.c create mode 100644 include/linux/platform_data/wiznet.h (limited to 'include/linux') diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index c63a64cb6085..a11af5cc4844 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -174,6 +174,7 @@ source "drivers/net/ethernet/tile/Kconfig" source "drivers/net/ethernet/toshiba/Kconfig" source "drivers/net/ethernet/tundra/Kconfig" source "drivers/net/ethernet/via/Kconfig" +source "drivers/net/ethernet/wiznet/Kconfig" source "drivers/net/ethernet/xilinx/Kconfig" source "drivers/net/ethernet/xircom/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 9676a5109d94..878ad32b93f2 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -73,5 +73,6 @@ obj-$(CONFIG_TILE_NET) += tile/ obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/ obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/ obj-$(CONFIG_NET_VENDOR_VIA) += via/ +obj-$(CONFIG_NET_VENDOR_WIZNET) += wiznet/ obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/ obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/ diff --git a/drivers/net/ethernet/wiznet/Kconfig b/drivers/net/ethernet/wiznet/Kconfig new file mode 100644 index 000000000000..f45cef16bfda --- /dev/null +++ b/drivers/net/ethernet/wiznet/Kconfig @@ -0,0 +1,65 @@ +# +# WIZnet devices configuration +# + +config NET_VENDOR_WIZNET + bool "WIZnet devices" + default y + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available from + . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about WIZnet devices. If you say Y, you will be asked + for your specific card in the following questions. + +if NET_VENDOR_WIZNET + +config WIZNET_W5300 + tristate "WIZnet W5300 Ethernet support" + ---help--- + Support for WIZnet W5300 chips. + + W5300 is a single chip with integrated 10/100 Ethernet MAC, + PHY and hardware TCP/IP stack, but this driver is limited to + the MAC and PHY functions only, onchip TCP/IP is unused. + + To compile this driver as a module, choose M here: the module + will be called w5300. + +choice + prompt "WIZnet interface mode" + default WIZNET_BUS_ANY + +config WIZNET_BUS_DIRECT + bool "Direct address bus mode" + ---help--- + In direct address mode host system can directly access all registers + after mapping to Memory-Mapped I/O space. + +config WIZNET_BUS_INDIRECT + bool "Indirect address bus mode" + ---help--- + In indirect address mode host system indirectly accesses registers + using Indirect Mode Address Register and Indirect Mode Data Register, + which are directly mapped to Memory-Mapped I/O space. + +config WIZNET_BUS_ANY + bool "Select interface mode in runtime" + ---help--- + If interface mode is unknown in compile time, it can be selected + in runtime from board/platform resources configuration. + + Performance may decrease compared to explicitly selected bus mode. +endchoice + +config WIZNET_TX_FLOW + bool "Use transmit flow control" + default y + help + This enables transmit flow control for WIZnet chips. + If unsure, say Y. + +endif # NET_VENDOR_WIZNET diff --git a/drivers/net/ethernet/wiznet/Makefile b/drivers/net/ethernet/wiznet/Makefile new file mode 100644 index 000000000000..88e0a3e62ba7 --- /dev/null +++ b/drivers/net/ethernet/wiznet/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_WIZNET_W5300) += w5300.o diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c new file mode 100644 index 000000000000..88afde99de8d --- /dev/null +++ b/drivers/net/ethernet/wiznet/w5300.c @@ -0,0 +1,722 @@ +/* + * Ethernet driver for the WIZnet W5300 chip. + * + * Copyright (C) 2008-2009 WIZnet Co.,Ltd. + * Copyright (C) 2011 Taehun Kim gmail.com> + * Copyright (C) 2012 Mike Sinkovsky + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "w5300" +#define DRV_VERSION "2012-04-04" + +MODULE_DESCRIPTION("WIZnet W5300 Ethernet driver v"DRV_VERSION); +MODULE_AUTHOR("Mike Sinkovsky "); +MODULE_ALIAS("platform:"DRV_NAME); +MODULE_LICENSE("GPL"); + +/* + * Registers + */ +#define W5300_MR 0x0000 /* Mode Register */ +#define MR_DBW (1 << 15) /* Data bus width */ +#define MR_MPF (1 << 14) /* Mac layer pause frame */ +#define MR_WDF(n) (((n)&7)<<11) /* Write data fetch time */ +#define MR_RDH (1 << 10) /* Read data hold time */ +#define MR_FS (1 << 8) /* FIFO swap */ +#define MR_RST (1 << 7) /* S/W reset */ +#define MR_PB (1 << 4) /* Ping block */ +#define MR_DBS (1 << 2) /* Data bus swap */ +#define MR_IND (1 << 0) /* Indirect mode */ +#define W5300_IR 0x0002 /* Interrupt Register */ +#define W5300_IMR 0x0004 /* Interrupt Mask Register */ +#define IR_S0 0x0001 /* S0 interrupt */ +#define W5300_SHARL 0x0008 /* Source MAC address (0123) */ +#define W5300_SHARH 0x000c /* Source MAC address (45) */ +#define W5300_TMSRL 0x0020 /* Transmit Memory Size (0123) */ +#define W5300_TMSRH 0x0024 /* Transmit Memory Size (4567) */ +#define W5300_RMSRL 0x0028 /* Receive Memory Size (0123) */ +#define W5300_RMSRH 0x002c /* Receive Memory Size (4567) */ +#define W5300_MTYPE 0x0030 /* Memory Type */ +#define W5300_IDR 0x00fe /* Chip ID register */ +#define IDR_W5300 0x5300 /* =0x5300 for WIZnet W5300 */ +#define W5300_S0_MR 0x0200 /* S0 Mode Register */ +#define S0_MR_CLOSED 0x0000 /* Close mode */ +#define S0_MR_MACRAW 0x0004 /* MAC RAW mode (promiscous) */ +#define S0_MR_MACRAW_MF 0x0044 /* MAC RAW mode (filtered) */ +#define W5300_S0_CR 0x0202 /* S0 Command Register */ +#define S0_CR_OPEN 0x0001 /* OPEN command */ +#define S0_CR_CLOSE 0x0010 /* CLOSE command */ +#define S0_CR_SEND 0x0020 /* SEND command */ +#define S0_CR_RECV 0x0040 /* RECV command */ +#define W5300_S0_IMR 0x0204 /* S0 Interrupt Mask Register */ +#define W5300_S0_IR 0x0206 /* S0 Interrupt Register */ +#define S0_IR_RECV 0x0004 /* Receive interrupt */ +#define S0_IR_SENDOK 0x0010 /* Send OK interrupt */ +#define W5300_S0_SSR 0x0208 /* S0 Socket Status Register */ +#define W5300_S0_TX_WRSR 0x0220 /* S0 TX Write Size Register */ +#define W5300_S0_TX_FSR 0x0224 /* S0 TX Free Size Register */ +#define W5300_S0_RX_RSR 0x0228 /* S0 Received data Size */ +#define W5300_S0_TX_FIFO 0x022e /* S0 Transmit FIFO */ +#define W5300_S0_RX_FIFO 0x0230 /* S0 Receive FIFO */ +#define W5300_REGS_LEN 0x0400 + +/* + * Device driver private data structure + */ +struct w5300_priv { + void __iomem *base; + spinlock_t reg_lock; + bool indirect; + u16 (*read) (struct w5300_priv *priv, u16 addr); + void (*write)(struct w5300_priv *priv, u16 addr, u16 data); + int irq; + int link_irq; + int link_gpio; + + struct napi_struct napi; + struct net_device *ndev; + bool promisc; + u32 msg_enable; +}; + +/************************************************************************ + * + * Lowlevel I/O functions + * + ***********************************************************************/ + +/* + * In direct address mode host system can directly access W5300 registers + * after mapping to Memory-Mapped I/O space. + * + * 0x400 bytes are required for memory space. + */ +static inline u16 w5300_read_direct(struct w5300_priv *priv, u16 addr) +{ + return ioread16(priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT)); +} + +static inline void w5300_write_direct(struct w5300_priv *priv, + u16 addr, u16 data) +{ + iowrite16(data, priv->base + (addr << CONFIG_WIZNET_BUS_SHIFT)); +} + +/* + * In indirect address mode host system indirectly accesses registers by + * using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data + * Register (IDM_DR), which are directly mapped to Memory-Mapped I/O space. + * Mode Register (MR) is directly accessible. + * + * Only 0x06 bytes are required for memory space. + */ +#define W5300_IDM_AR 0x0002 /* Indirect Mode Address */ +#define W5300_IDM_DR 0x0004 /* Indirect Mode Data */ + +static u16 w5300_read_indirect(struct w5300_priv *priv, u16 addr) +{ + unsigned long flags; + u16 data; + + spin_lock_irqsave(&priv->reg_lock, flags); + w5300_write_direct(priv, W5300_IDM_AR, addr); + mmiowb(); + data = w5300_read_direct(priv, W5300_IDM_DR); + spin_unlock_irqrestore(&priv->reg_lock, flags); + + return data; +} + +static void w5300_write_indirect(struct w5300_priv *priv, u16 addr, u16 data) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->reg_lock, flags); + w5300_write_direct(priv, W5300_IDM_AR, addr); + mmiowb(); + w5300_write_direct(priv, W5300_IDM_DR, data); + mmiowb(); + spin_unlock_irqrestore(&priv->reg_lock, flags); +} + +#if defined(CONFIG_WIZNET_BUS_DIRECT) +#define w5300_read w5300_read_direct +#define w5300_write w5300_write_direct + +#elif defined(CONFIG_WIZNET_BUS_INDIRECT) +#define w5300_read w5300_read_indirect +#define w5300_write w5300_write_indirect + +#else /* CONFIG_WIZNET_BUS_ANY */ +#define w5300_read priv->read +#define w5300_write priv->write +#endif + +static u32 w5300_read32(struct w5300_priv *priv, u16 addr) +{ + u32 data; + data = w5300_read(priv, addr) << 16; + data |= w5300_read(priv, addr + 2); + return data; +} + +static void w5300_write32(struct w5300_priv *priv, u16 addr, u32 data) +{ + w5300_write(priv, addr, data >> 16); + w5300_write(priv, addr + 2, data); +} + +static int w5300_command(struct w5300_priv *priv, u16 cmd) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(100); + + w5300_write(priv, W5300_S0_CR, cmd); + mmiowb(); + + while (w5300_read(priv, W5300_S0_CR) != 0) { + if (time_after(jiffies, timeout)) + return -EIO; + cpu_relax(); + } + + return 0; +} + +static void w5300_read_frame(struct w5300_priv *priv, u8 *buf, int len) +{ + u16 fifo; + int i; + + for (i = 0; i < len; i += 2) { + fifo = w5300_read(priv, W5300_S0_RX_FIFO); + *buf++ = fifo >> 8; + *buf++ = fifo; + } + fifo = w5300_read(priv, W5300_S0_RX_FIFO); + fifo = w5300_read(priv, W5300_S0_RX_FIFO); +} + +static void w5300_write_frame(struct w5300_priv *priv, u8 *buf, int len) +{ + u16 fifo; + int i; + + for (i = 0; i < len; i += 2) { + fifo = *buf++ << 8; + fifo |= *buf++; + w5300_write(priv, W5300_S0_TX_FIFO, fifo); + } + w5300_write32(priv, W5300_S0_TX_WRSR, len); +} + +static void w5300_write_macaddr(struct w5300_priv *priv) +{ + struct net_device *ndev = priv->ndev; + w5300_write32(priv, W5300_SHARL, + ndev->dev_addr[0] << 24 | + ndev->dev_addr[1] << 16 | + ndev->dev_addr[2] << 8 | + ndev->dev_addr[3]); + w5300_write(priv, W5300_SHARH, + ndev->dev_addr[4] << 8 | + ndev->dev_addr[5]); + mmiowb(); +} + +static void w5300_hw_reset(struct w5300_priv *priv) +{ + w5300_write_direct(priv, W5300_MR, MR_RST); + mmiowb(); + mdelay(5); + w5300_write_direct(priv, W5300_MR, priv->indirect ? + MR_WDF(7) | MR_PB | MR_IND : + MR_WDF(7) | MR_PB); + mmiowb(); + w5300_write(priv, W5300_IMR, 0); + w5300_write_macaddr(priv); + + /* Configure 128K of internal memory + * as 64K RX fifo and 64K TX fifo + */ + w5300_write32(priv, W5300_RMSRL, 64 << 24); + w5300_write32(priv, W5300_RMSRH, 0); + w5300_write32(priv, W5300_TMSRL, 64 << 24); + w5300_write32(priv, W5300_TMSRH, 0); + w5300_write(priv, W5300_MTYPE, 0x00ff); + mmiowb(); +} + +static void w5300_hw_start(struct w5300_priv *priv) +{ + w5300_write(priv, W5300_S0_MR, priv->promisc ? + S0_MR_MACRAW : S0_MR_MACRAW_MF); + mmiowb(); + w5300_command(priv, S0_CR_OPEN); + w5300_write(priv, W5300_S0_IMR, IS_ENABLED(CONFIG_WIZNET_TX_FLOW) ? + S0_IR_RECV | S0_IR_SENDOK : + S0_IR_RECV); + w5300_write(priv, W5300_IMR, IR_S0); + mmiowb(); +} + +static void w5300_hw_close(struct w5300_priv *priv) +{ + w5300_write(priv, W5300_IMR, 0); + mmiowb(); + w5300_command(priv, S0_CR_CLOSE); +} + +/*********************************************************************** + * + * Device driver functions / callbacks + * + ***********************************************************************/ + +static void w5300_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(ndev->dev.parent), + sizeof(info->bus_info)); +} + +static u32 w5300_get_link(struct net_device *ndev) +{ + struct w5300_priv *priv = netdev_priv(ndev); + + if (gpio_is_valid(priv->link_gpio)) + return !!gpio_get_value(priv->link_gpio); + + return 1; +} + +static u32 w5300_get_msglevel(struct net_device *ndev) +{ + struct w5300_priv *priv = netdev_priv(ndev); + + return priv->msg_enable; +} + +static void w5300_set_msglevel(struct net_device *ndev, u32 value) +{ + struct w5300_priv *priv = netdev_priv(ndev); + + priv->msg_enable = value; +} + +static int w5300_get_regs_len(struct net_device *ndev) +{ + return W5300_REGS_LEN; +} + +static void w5300_get_regs(struct net_device *ndev, + struct ethtool_regs *regs, void *_buf) +{ + struct w5300_priv *priv = netdev_priv(ndev); + u8 *buf = _buf; + u16 addr; + u16 data; + + regs->version = 1; + for (addr = 0; addr < W5300_REGS_LEN; addr += 2) { + switch (addr & 0x23f) { + case W5300_S0_TX_FIFO: /* cannot read TX_FIFO */ + case W5300_S0_RX_FIFO: /* cannot read RX_FIFO */ + data = 0xffff; + break; + default: + data = w5300_read(priv, addr); + break; + } + *buf++ = data >> 8; + *buf++ = data; + } +} + +static void w5300_tx_timeout(struct net_device *ndev) +{ + struct w5300_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + w5300_hw_reset(priv); + w5300_hw_start(priv); + ndev->stats.tx_errors++; + ndev->trans_start = jiffies; + netif_wake_queue(ndev); +} + +static int w5300_start_tx(struct sk_buff *skb, struct net_device *ndev) +{ + struct w5300_priv *priv = netdev_priv(ndev); + + if (IS_ENABLED(CONFIG_WIZNET_TX_FLOW)) + netif_stop_queue(ndev); + + w5300_write_frame(priv, skb->data, skb->len); + mmiowb(); + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + dev_kfree_skb(skb); + netif_dbg(priv, tx_queued, ndev, "tx queued\n"); + + w5300_command(priv, S0_CR_SEND); + + return NETDEV_TX_OK; +} + +static int w5300_napi_poll(struct napi_struct *napi, int budget) +{ + struct w5300_priv *priv = container_of(napi, struct w5300_priv, napi); + struct net_device *ndev = priv->ndev; + struct sk_buff *skb; + int rx_count; + u16 rx_len; + + for (rx_count = 0; rx_count < budget; rx_count++) { + u32 rx_fifo_len = w5300_read32(priv, W5300_S0_RX_RSR); + if (rx_fifo_len == 0) + break; + + rx_len = w5300_read(priv, W5300_S0_RX_FIFO); + + skb = netdev_alloc_skb_ip_align(ndev, roundup(rx_len, 2)); + if (unlikely(!skb)) { + u32 i; + for (i = 0; i < rx_fifo_len; i += 2) + w5300_read(priv, W5300_S0_RX_FIFO); + ndev->stats.rx_dropped++; + return -ENOMEM; + } + + skb_put(skb, rx_len); + w5300_read_frame(priv, skb->data, rx_len); + skb->protocol = eth_type_trans(skb, ndev); + + netif_receive_skb(skb); + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += rx_len; + } + + if (rx_count < budget) { + w5300_write(priv, W5300_IMR, IR_S0); + mmiowb(); + napi_complete(napi); + } + + return rx_count; +} + +static irqreturn_t w5300_interrupt(int irq, void *ndev_instance) +{ + struct net_device *ndev = ndev_instance; + struct w5300_priv *priv = netdev_priv(ndev); + + int ir = w5300_read(priv, W5300_S0_IR); + if (!ir) + return IRQ_NONE; + w5300_write(priv, W5300_S0_IR, ir); + mmiowb(); + + if (IS_ENABLED(CONFIG_WIZNET_TX_FLOW) && (ir & S0_IR_SENDOK)) { + netif_dbg(priv, tx_done, ndev, "tx done\n"); + netif_wake_queue(ndev); + } + + if (ir & S0_IR_RECV) { + if (napi_schedule_prep(&priv->napi)) { + w5300_write(priv, W5300_IMR, 0); + mmiowb(); + __napi_schedule(&priv->napi); + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t w5300_detect_link(int irq, void *ndev_instance) +{ + struct net_device *ndev = ndev_instance; + struct w5300_priv *priv = netdev_priv(ndev); + + if (netif_running(ndev)) { + if (gpio_get_value(priv->link_gpio) != 0) { + netif_info(priv, link, ndev, "link is up\n"); + netif_carrier_on(ndev); + } else { + netif_info(priv, link, ndev, "link is down\n"); + netif_carrier_off(ndev); + } + } + + return IRQ_HANDLED; +} + +static void w5300_set_rx_mode(struct net_device *ndev) +{ + struct w5300_priv *priv = netdev_priv(ndev); + bool set_promisc = (ndev->flags & IFF_PROMISC) != 0; + + if (priv->promisc != set_promisc) { + priv->promisc = set_promisc; + w5300_hw_start(priv); + } +} + +static int w5300_set_macaddr(struct net_device *ndev, void *addr) +{ + struct w5300_priv *priv = netdev_priv(ndev); + struct sockaddr *sock_addr = addr; + + if (!is_valid_ether_addr(sock_addr->sa_data)) + return -EADDRNOTAVAIL; + memcpy(ndev->dev_addr, sock_addr->sa_data, ETH_ALEN); + ndev->addr_assign_type &= ~NET_ADDR_RANDOM; + w5300_write_macaddr(priv); + return 0; +} + +static int w5300_open(struct net_device *ndev) +{ + struct w5300_priv *priv = netdev_priv(ndev); + + netif_info(priv, ifup, ndev, "enabling\n"); + if (!is_valid_ether_addr(ndev->dev_addr)) + return -EINVAL; + w5300_hw_start(priv); + napi_enable(&priv->napi); + netif_start_queue(ndev); + if (!gpio_is_valid(priv->link_gpio) || + gpio_get_value(priv->link_gpio) != 0) + netif_carrier_on(ndev); + return 0; +} + +static int w5300_stop(struct net_device *ndev) +{ + struct w5300_priv *priv = netdev_priv(ndev); + + netif_info(priv, ifdown, ndev, "shutting down\n"); + w5300_hw_close(priv); + netif_carrier_off(ndev); + netif_stop_queue(ndev); + napi_disable(&priv->napi); + return 0; +} + +static const struct ethtool_ops w5300_ethtool_ops = { + .get_drvinfo = w5300_get_drvinfo, + .get_msglevel = w5300_get_msglevel, + .set_msglevel = w5300_set_msglevel, + .get_link = w5300_get_link, + .get_regs_len = w5300_get_regs_len, + .get_regs = w5300_get_regs, +}; + +static const struct net_device_ops w5300_netdev_ops = { + .ndo_open = w5300_open, + .ndo_stop = w5300_stop, + .ndo_start_xmit = w5300_start_tx, + .ndo_tx_timeout = w5300_tx_timeout, + .ndo_set_rx_mode = w5300_set_rx_mode, + .ndo_set_mac_address = w5300_set_macaddr, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, +}; + +static int __devinit w5300_hw_probe(struct platform_device *pdev) +{ + struct wiznet_platform_data *data = pdev->dev.platform_data; + struct net_device *ndev = platform_get_drvdata(pdev); + struct w5300_priv *priv = netdev_priv(ndev); + const char *name = netdev_name(ndev); + struct resource *mem; + int mem_size; + int irq; + int ret; + + if (data && is_valid_ether_addr(data->mac_addr)) { + memcpy(ndev->dev_addr, data->mac_addr, ETH_ALEN); + } else { + random_ether_addr(ndev->dev_addr); + ndev->addr_assign_type |= NET_ADDR_RANDOM; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + return -ENXIO; + mem_size = resource_size(mem); + if (!devm_request_mem_region(&pdev->dev, mem->start, mem_size, name)) + return -EBUSY; + priv->base = devm_ioremap(&pdev->dev, mem->start, mem_size); + if (!priv->base) + return -EBUSY; + + spin_lock_init(&priv->reg_lock); + priv->indirect = mem_size < W5300_BUS_DIRECT_SIZE; + if (priv->indirect) { + priv->read = w5300_read_indirect; + priv->write = w5300_write_indirect; + } else { + priv->read = w5300_read_direct; + priv->write = w5300_write_direct; + } + + w5300_hw_reset(priv); + if (w5300_read(priv, W5300_IDR) != IDR_W5300) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = request_irq(irq, w5300_interrupt, + IRQ_TYPE_LEVEL_LOW, name, ndev); + if (ret < 0) + return ret; + priv->irq = irq; + + priv->link_gpio = data->link_gpio; + if (gpio_is_valid(priv->link_gpio)) { + char *link_name = devm_kzalloc(&pdev->dev, 16, GFP_KERNEL); + if (!link_name) + return -ENOMEM; + snprintf(link_name, 16, "%s-link", name); + priv->link_irq = gpio_to_irq(priv->link_gpio); + if (request_any_context_irq(priv->link_irq, w5300_detect_link, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + link_name, priv->ndev) < 0) + priv->link_gpio = -EINVAL; + } + + netdev_info(ndev, "at 0x%llx irq %d\n", (u64)mem->start, irq); + return 0; +} + +static int __devinit w5300_probe(struct platform_device *pdev) +{ + struct w5300_priv *priv; + struct net_device *ndev; + int err; + + ndev = alloc_etherdev(sizeof(*priv)); + if (!ndev) + return -ENOMEM; + SET_NETDEV_DEV(ndev, &pdev->dev); + platform_set_drvdata(pdev, ndev); + priv = netdev_priv(ndev); + priv->ndev = ndev; + + ether_setup(ndev); + ndev->netdev_ops = &w5300_netdev_ops; + ndev->ethtool_ops = &w5300_ethtool_ops; + ndev->watchdog_timeo = HZ; + netif_napi_add(ndev, &priv->napi, w5300_napi_poll, 16); + + /* This chip doesn't support VLAN packets with normal MTU, + * so disable VLAN for this device. + */ + ndev->features |= NETIF_F_VLAN_CHALLENGED; + + err = register_netdev(ndev); + if (err < 0) + goto err_register; + + err = w5300_hw_probe(pdev); + if (err < 0) + goto err_hw_probe; + + return 0; + +err_hw_probe: + unregister_netdev(ndev); +err_register: + free_netdev(ndev); + platform_set_drvdata(pdev, NULL); + return err; +} + +static int __devexit w5300_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct w5300_priv *priv = netdev_priv(ndev); + + w5300_hw_reset(priv); + free_irq(priv->irq, ndev); + if (gpio_is_valid(priv->link_gpio)) + free_irq(priv->link_irq, ndev); + + unregister_netdev(ndev); + free_netdev(ndev); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int w5300_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct w5300_priv *priv = netdev_priv(ndev); + + if (netif_running(ndev)) { + netif_carrier_off(ndev); + netif_device_detach(ndev); + + w5300_hw_close(priv); + } + return 0; +} + +static int w5300_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct w5300_priv *priv = netdev_priv(ndev); + + if (!netif_running(ndev)) { + w5300_hw_reset(priv); + w5300_hw_start(priv); + + netif_device_attach(ndev); + if (!gpio_is_valid(priv->link_gpio) || + gpio_get_value(priv->link_gpio) != 0) + netif_carrier_on(ndev); + } + return 0; +} +#endif /* CONFIG_PM */ + +static SIMPLE_DEV_PM_OPS(w5300_pm_ops, w5300_suspend, w5300_resume); + +static struct platform_driver w5300_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &w5300_pm_ops, + }, + .probe = w5300_probe, + .remove = __devexit_p(w5300_remove), +}; + +module_platform_driver(w5300_driver); diff --git a/include/linux/platform_data/wiznet.h b/include/linux/platform_data/wiznet.h new file mode 100644 index 000000000000..b5d8c192d84d --- /dev/null +++ b/include/linux/platform_data/wiznet.h @@ -0,0 +1,24 @@ +/* + * Ethernet driver for the WIZnet W5x00 chip. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef PLATFORM_DATA_WIZNET_H +#define PLATFORM_DATA_WIZNET_H + +#include + +struct wiznet_platform_data { + int link_gpio; + u8 mac_addr[ETH_ALEN]; +}; + +#ifndef CONFIG_WIZNET_BUS_SHIFT +#define CONFIG_WIZNET_BUS_SHIFT 0 +#endif + +#define W5100_BUS_DIRECT_SIZE (0x8000 << CONFIG_WIZNET_BUS_SHIFT) +#define W5300_BUS_DIRECT_SIZE (0x0400 << CONFIG_WIZNET_BUS_SHIFT) + +#endif /* PLATFORM_DATA_WIZNET_H */ -- cgit v1.2.3 From 0e98b523c4a4119cbd17e58dff385cc329064694 Mon Sep 17 00:00:00 2001 From: Amir Vadai Date: Wed, 4 Apr 2012 21:33:24 +0000 Subject: net/mlx4_en: Force user priority by QP attribute Instead of relying on HW to change schedule queue by UP, schedule queue is fixed for a tx_ring, and UP in WQE is ignored in this aspect. This resolves two issues with untagged traffic: 1. untagged traffic has no UP in packet which is needed for QoS. The change above allows setting the schedule queue (and by that the UP) of such a stream. 2. BlueFlame uses the same field used by vlan tag. So forcing UP from QPC allows using BF for untagged but prioritized traffic. In old firmware that force UP is not supported, untagged traffic will not subject to QoS. Because UP is set by QP, need to always have a tx ring per UP, even if pfcrx module paramter is false. Signed-off-by: Amir Vadai Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_main.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 3 ++- drivers/net/ethernet/mellanox/mlx4/en_resources.c | 6 +++++- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 4 ++-- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 12 ++++-------- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 6 +++--- include/linux/mlx4/qp.h | 3 ++- 7 files changed, 19 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index 2097a7d3c5b8..346fdb2e92a6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -114,7 +114,7 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev) params->prof[i].tx_ring_size = MLX4_EN_DEF_TX_RING_SIZE; params->prof[i].rx_ring_size = MLX4_EN_DEF_RX_RING_SIZE; params->prof[i].tx_ring_num = MLX4_EN_NUM_TX_RINGS + - (!!pfcrx) * MLX4_EN_NUM_PPP_RINGS; + MLX4_EN_NUM_PPP_RINGS; params->prof[i].rss_rings = 0; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 31b455a49273..2322622b6098 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -650,7 +650,8 @@ int mlx4_en_start_port(struct net_device *dev) /* Configure ring */ tx_ring = &priv->tx_ring[i]; - err = mlx4_en_activate_tx_ring(priv, tx_ring, cq->mcq.cqn); + err = mlx4_en_activate_tx_ring(priv, tx_ring, cq->mcq.cqn, + max(0, i - MLX4_EN_NUM_TX_RINGS)); if (err) { en_err(priv, "Failed allocating Tx ring\n"); mlx4_en_deactivate_cq(priv, cq); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_resources.c b/drivers/net/ethernet/mellanox/mlx4/en_resources.c index bcbc54c16947..10c24c784b70 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_resources.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_resources.c @@ -39,7 +39,7 @@ void mlx4_en_fill_qp_context(struct mlx4_en_priv *priv, int size, int stride, int is_tx, int rss, int qpn, int cqn, - struct mlx4_qp_context *context) + int user_prio, struct mlx4_qp_context *context) { struct mlx4_en_dev *mdev = priv->mdev; @@ -57,6 +57,10 @@ void mlx4_en_fill_qp_context(struct mlx4_en_priv *priv, int size, int stride, context->local_qpn = cpu_to_be32(qpn); context->pri_path.ackto = 1 & 0x07; context->pri_path.sched_queue = 0x83 | (priv->port - 1) << 6; + if (user_prio >= 0) { + context->pri_path.sched_queue |= user_prio << 3; + context->pri_path.feup = 1 << 6; + } context->pri_path.counter_index = 0xff; context->cqn_send = cpu_to_be32(cqn); context->cqn_recv = cpu_to_be32(cqn); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 9adbd53da525..d49a7ac3187d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -823,7 +823,7 @@ static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv, int qpn, memset(context, 0, sizeof *context); mlx4_en_fill_qp_context(priv, ring->actual_size, ring->stride, 0, 0, - qpn, ring->cqn, context); + qpn, ring->cqn, -1, context); context->db_rec_addr = cpu_to_be64(ring->wqres.db.dma); /* Cancel FCS removal if FW allows */ @@ -890,7 +890,7 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) } rss_map->indir_qp.event = mlx4_en_sqp_event; mlx4_en_fill_qp_context(priv, 0, 0, 0, 1, priv->base_qpn, - priv->rx_ring[0].cqn, &context); + priv->rx_ring[0].cqn, -1, &context); if (!priv->prof->rss_rings || priv->prof->rss_rings > priv->rx_ring_num) rss_rings = priv->rx_ring_num; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 17968244c399..94a605a7cd24 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -156,7 +156,7 @@ void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, - int cq) + int cq, int user_prio) { struct mlx4_en_dev *mdev = priv->mdev; int err; @@ -174,7 +174,7 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, ring->doorbell_qpn = ring->qp.qpn << 8; mlx4_en_fill_qp_context(priv, ring->size, ring->stride, 1, 0, ring->qpn, - ring->cqn, &ring->context); + ring->cqn, user_prio, &ring->context); if (ring->bf_enabled) ring->context.usr_page = cpu_to_be32(ring->bf.uar->index); @@ -570,18 +570,14 @@ static void build_inline_wqe(struct mlx4_en_tx_desc *tx_desc, struct sk_buff *sk u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb) { - struct mlx4_en_priv *priv = netdev_priv(dev); u16 vlan_tag = 0; - /* If we support per priority flow control and the packet contains - * a vlan tag, send the packet to the TX ring assigned to that priority - */ - if (priv->prof->rx_ppp && vlan_tx_tag_present(skb)) { + if (vlan_tx_tag_present(skb)) { vlan_tag = vlan_tx_tag_get(skb); return MLX4_EN_NUM_TX_RINGS + (vlan_tag >> 13); } - return skb_tx_hash(dev, skb); + return __skb_tx_hash(dev, skb, MLX4_EN_NUM_TX_RINGS); } static void mlx4_bf_copy(void __iomem *dst, unsigned long *src, unsigned bytecnt) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 9e2b911a1230..5bd7c2a3823e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -521,7 +521,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ri void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring); int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, - int cq); + int cq, int user_prio); void mlx4_en_deactivate_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring); @@ -539,8 +539,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, int budget); int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget); void mlx4_en_fill_qp_context(struct mlx4_en_priv *priv, int size, int stride, - int is_tx, int rss, int qpn, int cqn, - struct mlx4_qp_context *context); + int is_tx, int rss, int qpn, int cqn, int user_prio, + struct mlx4_qp_context *context); void mlx4_en_sqp_event(struct mlx4_qp *qp, enum mlx4_event event); int mlx4_en_map_buffer(struct mlx4_buf *buf); void mlx4_en_unmap_buffer(struct mlx4_buf *buf); diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index 091f9e7dc8b9..96005d75893c 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -139,7 +139,8 @@ struct mlx4_qp_path { u8 rgid[16]; u8 sched_queue; u8 vlan_index; - u8 reserved3[2]; + u8 feup; + u8 reserved3; u8 reserved4[2]; u8 dmac[6]; }; -- cgit v1.2.3 From e5395e92a470769d67369c002a41e59619f5214b Mon Sep 17 00:00:00 2001 From: Amir Vadai Date: Wed, 4 Apr 2012 21:33:25 +0000 Subject: net/mlx4_core: set port QoS attributes Adding QoS firmware commands: - mlx4_en_SET_PORT_PRIO2TC - set UP <=> TC - mlx4_en_SET_PORT_SCHEDULER - set promised BW, max BW and PG number Signed-off-by: Amir Vadai Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_port.h | 2 + drivers/net/ethernet/mellanox/mlx4/mlx4.h | 20 +++++++++ drivers/net/ethernet/mellanox/mlx4/port.c | 62 ++++++++++++++++++++++++++++ include/linux/mlx4/cmd.h | 4 ++ include/linux/mlx4/device.h | 3 ++ 5 files changed, 91 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.h b/drivers/net/ethernet/mellanox/mlx4/en_port.h index 6934fd7e66ed..745090b49d9e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_port.h +++ b/drivers/net/ethernet/mellanox/mlx4/en_port.h @@ -39,6 +39,8 @@ #define SET_PORT_PROMISC_SHIFT 31 #define SET_PORT_MC_PROMISC_SHIFT 30 +#define MLX4_EN_NUM_TC 8 + #define VLAN_FLTR_SIZE 128 struct mlx4_set_vlan_fltr_mbox { __be32 entry[VLAN_FLTR_SIZE]; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 2a0ff2cc7182..cd56f1aea4b5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -53,6 +53,26 @@ #define DRV_VERSION "1.1" #define DRV_RELDATE "Dec, 2011" +#define MLX4_NUM_UP 8 +#define MLX4_NUM_TC 8 +#define MLX4_RATELIMIT_UNITS 3 /* 100 Mbps */ +#define MLX4_RATELIMIT_DEFAULT 0xffff + +struct mlx4_set_port_prio2tc_context { + u8 prio2tc[4]; +}; + +struct mlx4_port_scheduler_tc_cfg_be { + __be16 pg; + __be16 bw_precentage; + __be16 max_bw_units; /* 3-100Mbps, 4-1Gbps, other values - reserved */ + __be16 max_bw_value; +}; + +struct mlx4_set_port_scheduler_context { + struct mlx4_port_scheduler_tc_cfg_be tc[MLX4_NUM_TC]; +}; + enum { MLX4_HCR_BASE = 0x80680, MLX4_HCR_SIZE = 0x0001c, diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 77535ff18f1b..55b12e6bed87 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -834,6 +834,68 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, } EXPORT_SYMBOL(mlx4_SET_PORT_qpn_calc); +int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_port_prio2tc_context *context; + int err; + u32 in_mod; + int i; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + context = mailbox->buf; + memset(context, 0, sizeof *context); + + for (i = 0; i < MLX4_NUM_UP; i += 2) + context->prio2tc[i >> 1] = prio2tc[i] << 4 | prio2tc[i + 1]; + + in_mod = MLX4_SET_PORT_PRIO2TC << 8 | port; + err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_PRIO2TC); + +int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, + u8 *pg, u16 *ratelimit) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_port_scheduler_context *context; + int err; + u32 in_mod; + int i; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + context = mailbox->buf; + memset(context, 0, sizeof *context); + + for (i = 0; i < MLX4_NUM_TC; i++) { + struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i]; + u16 r = ratelimit && ratelimit[i] ? ratelimit[i] : + MLX4_RATELIMIT_DEFAULT; + + tc->pg = htons(pg[i]); + tc->bw_precentage = htons(tc_tx_bw[i]); + + tc->max_bw_units = htons(MLX4_RATELIMIT_UNITS); + tc->max_bw_value = htons(r); + } + + in_mod = MLX4_SET_PORT_SCHEDULER << 8 | port; + err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER); + int mlx4_SET_MCAST_FLTR_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index 9958ff2cad3c..1f3860a8a109 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -150,6 +150,10 @@ enum { /* statistics commands */ MLX4_CMD_QUERY_IF_STAT = 0X54, MLX4_CMD_SET_IF_STAT = 0X55, + + /* set port opcode modifiers */ + MLX4_SET_PORT_PRIO2TC = 0x8, + MLX4_SET_PORT_SCHEDULER = 0x9, }; enum { diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 834c96c5d879..6d028247f79d 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -628,6 +628,9 @@ int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, u8 pptx, u8 pfctx, u8 pprx, u8 pfcrx); int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, u8 promisc); +int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc); +int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, + u8 *pg, u16 *ratelimit); int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx); int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index); void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index); -- cgit v1.2.3 From 08f10affe45051e18e0d8291c0a53aecef1b8a14 Mon Sep 17 00:00:00 2001 From: Amir Vadai Date: Wed, 4 Apr 2012 21:33:30 +0000 Subject: net/dcb: Add an optional max rate attribute Although not specified in 8021Qaz spec, it could be useful to enable drivers whose HW supports setting a rate limit for an ETS TC. This patch adds this optional attribute to DCB netlink. To use it, drivers should implement and register the callbacks ieee_setmaxrate and ieee_getmaxrate. The units are 64 bits long and specified in Kbps to enable usage over both slow and very fast networks. Signed-off-by: Amir Vadai Signed-off-by: David S. Miller --- include/linux/dcbnl.h | 12 ++++++++++++ include/net/dcbnl.h | 2 ++ net/dcb/dcbnl.c | 20 ++++++++++++++++++++ 3 files changed, 34 insertions(+) (limited to 'include/linux') diff --git a/include/linux/dcbnl.h b/include/linux/dcbnl.h index 65a2562f66b4..6bb43382f3f3 100644 --- a/include/linux/dcbnl.h +++ b/include/linux/dcbnl.h @@ -67,6 +67,17 @@ struct ieee_ets { __u8 reco_prio_tc[IEEE_8021QAZ_MAX_TCS]; }; +/* This structure contains rate limit extension to the IEEE 802.1Qaz ETS + * managed object. + * Values are 64 bits long and specified in Kbps to enable usage over both + * slow and very fast networks. + * + * @tc_maxrate: maximal tc tx bandwidth indexed by traffic class + */ +struct ieee_maxrate { + __u64 tc_maxrate[IEEE_8021QAZ_MAX_TCS]; +}; + /* This structure contains the IEEE 802.1Qaz PFC managed object * * @pfc_cap: Indicates the number of traffic classes on the local device @@ -321,6 +332,7 @@ enum ieee_attrs { DCB_ATTR_IEEE_PEER_ETS, DCB_ATTR_IEEE_PEER_PFC, DCB_ATTR_IEEE_PEER_APP, + DCB_ATTR_IEEE_MAXRATE, __DCB_ATTR_IEEE_MAX }; #define DCB_ATTR_IEEE_MAX (__DCB_ATTR_IEEE_MAX - 1) diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h index f55c980d8e23..fc5d5dcebb00 100644 --- a/include/net/dcbnl.h +++ b/include/net/dcbnl.h @@ -48,6 +48,8 @@ struct dcbnl_rtnl_ops { /* IEEE 802.1Qaz std */ int (*ieee_getets) (struct net_device *, struct ieee_ets *); int (*ieee_setets) (struct net_device *, struct ieee_ets *); + int (*ieee_getmaxrate) (struct net_device *, struct ieee_maxrate *); + int (*ieee_setmaxrate) (struct net_device *, struct ieee_maxrate *); int (*ieee_getpfc) (struct net_device *, struct ieee_pfc *); int (*ieee_setpfc) (struct net_device *, struct ieee_pfc *); int (*ieee_getapp) (struct net_device *, struct dcb_app *); diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 36f37af63bf2..8dfa1da7c40d 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -178,6 +178,7 @@ static const struct nla_policy dcbnl_ieee_policy[DCB_ATTR_IEEE_MAX + 1] = { [DCB_ATTR_IEEE_ETS] = {.len = sizeof(struct ieee_ets)}, [DCB_ATTR_IEEE_PFC] = {.len = sizeof(struct ieee_pfc)}, [DCB_ATTR_IEEE_APP_TABLE] = {.type = NLA_NESTED}, + [DCB_ATTR_IEEE_MAXRATE] = {.len = sizeof(struct ieee_maxrate)}, }; static const struct nla_policy dcbnl_ieee_app[DCB_ATTR_IEEE_APP_MAX + 1] = { @@ -1246,6 +1247,17 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) goto nla_put_failure; } + if (ops->ieee_getmaxrate) { + struct ieee_maxrate maxrate; + err = ops->ieee_getmaxrate(netdev, &maxrate); + if (!err) { + err = nla_put(skb, DCB_ATTR_IEEE_MAXRATE, + sizeof(maxrate), &maxrate); + if (err) + goto nla_put_failure; + } + } + if (ops->ieee_getpfc) { struct ieee_pfc pfc; err = ops->ieee_getpfc(netdev, &pfc); @@ -1601,6 +1613,14 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlattr **tb, goto err; } + if (ieee[DCB_ATTR_IEEE_MAXRATE] && ops->ieee_setmaxrate) { + struct ieee_maxrate *maxrate = + nla_data(ieee[DCB_ATTR_IEEE_MAXRATE]); + err = ops->ieee_setmaxrate(netdev, maxrate); + if (err) + goto err; + } + if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) { struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]); err = ops->ieee_setpfc(netdev, pfc); -- cgit v1.2.3 From 91fe4d508f29d71133e9a82ebdc1f2274c514b70 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Fri, 17 Feb 2012 17:46:21 +0100 Subject: Fix typo milivolt => millivolt Signed-off-by: Thomas Weber Signed-off-by: Jiri Kosina --- Documentation/hwmon/wm831x | 2 +- drivers/mfd/tps65911-comparator.c | 2 +- drivers/power/bq27x00_battery.c | 2 +- drivers/regulator/tps65910-regulator.c | 20 ++++++++++---------- include/linux/mfd/wm831x/pdata.h | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/Documentation/hwmon/wm831x b/Documentation/hwmon/wm831x index 24f47d8f6a42..11446757c8c8 100644 --- a/Documentation/hwmon/wm831x +++ b/Documentation/hwmon/wm831x @@ -22,7 +22,7 @@ reporting of all the input values but does not provide any alarms. Voltage Monitoring ------------------ -Voltages are sampled by a 12 bit ADC. Voltages in milivolts are 1.465 +Voltages are sampled by a 12 bit ADC. Voltages in millivolts are 1.465 times the ADC value. Temperature Monitoring diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c index e7ff783aa31e..5a62e6bf89ae 100644 --- a/drivers/mfd/tps65911-comparator.c +++ b/drivers/mfd/tps65911-comparator.c @@ -26,7 +26,7 @@ #define COMP1 1 #define COMP2 2 -/* Comparator 1 voltage selection table in milivolts */ +/* Comparator 1 voltage selection table in millivolts */ static const u16 COMP_VSEL_TABLE[] = { 0, 2500, 2500, 2500, 2500, 2550, 2600, 2650, 2700, 2750, 2800, 2850, 2900, 2950, 3000, 3050, diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index cd1545f57f49..241848745e29 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -456,7 +456,7 @@ static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di, } /* - * Return the battery Voltage in milivolts + * Return the battery Voltage in millivolts * Or < 0 if something fails. */ static int bq27x00_battery_voltage(struct bq27x00_device_info *di, diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 5c15ba01e9c7..1711dbf28ea5 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -27,54 +27,54 @@ #define TPS65910_SUPPLY_STATE_ENABLED 0x1 -/* supported VIO voltages in milivolts */ +/* supported VIO voltages in millivolts */ static const u16 VIO_VSEL_table[] = { 1500, 1800, 2500, 3300, }; /* VSEL tables for TPS65910 specific LDOs and dcdc's */ -/* supported VDD3 voltages in milivolts */ +/* supported VDD3 voltages in millivolts */ static const u16 VDD3_VSEL_table[] = { 5000, }; -/* supported VDIG1 voltages in milivolts */ +/* supported VDIG1 voltages in millivolts */ static const u16 VDIG1_VSEL_table[] = { 1200, 1500, 1800, 2700, }; -/* supported VDIG2 voltages in milivolts */ +/* supported VDIG2 voltages in millivolts */ static const u16 VDIG2_VSEL_table[] = { 1000, 1100, 1200, 1800, }; -/* supported VPLL voltages in milivolts */ +/* supported VPLL voltages in millivolts */ static const u16 VPLL_VSEL_table[] = { 1000, 1100, 1800, 2500, }; -/* supported VDAC voltages in milivolts */ +/* supported VDAC voltages in millivolts */ static const u16 VDAC_VSEL_table[] = { 1800, 2600, 2800, 2850, }; -/* supported VAUX1 voltages in milivolts */ +/* supported VAUX1 voltages in millivolts */ static const u16 VAUX1_VSEL_table[] = { 1800, 2500, 2800, 2850, }; -/* supported VAUX2 voltages in milivolts */ +/* supported VAUX2 voltages in millivolts */ static const u16 VAUX2_VSEL_table[] = { 1800, 2800, 2900, 3300, }; -/* supported VAUX33 voltages in milivolts */ +/* supported VAUX33 voltages in millivolts */ static const u16 VAUX33_VSEL_table[] = { 1800, 2000, 2800, 3300, }; -/* supported VMMC voltages in milivolts */ +/* supported VMMC voltages in millivolts */ static const u16 VMMC_VSEL_table[] = { 1800, 2800, 3000, 3300, }; diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h index 1d7a3f7b3b5d..dcc9631b3052 100644 --- a/include/linux/mfd/wm831x/pdata.h +++ b/include/linux/mfd/wm831x/pdata.h @@ -26,7 +26,7 @@ struct wm831x_backlight_pdata { struct wm831x_backup_pdata { int charger_enable; int no_constant_voltage; /** Disable constant voltage charging */ - int vlim; /** Voltage limit in milivolts */ + int vlim; /** Voltage limit in millivolts */ int ilim; /** Current limit in microamps */ }; -- cgit v1.2.3 From fc3a1f04f5040255cbc086c419e4237f29f89f88 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 13 Dec 2011 18:34:01 +0100 Subject: gpio: add flags to export GPIOs when requesting Introduce new flags to automatically export GPIOs when using the convenience functions gpio_request_one() or gpio_request_array(). This eases support for custom boards where lots of GPIOs need to be exported for customer applications. Signed-off-by: Wolfram Sang Signed-off-by: Grant Likely --- Documentation/gpio.txt | 3 +++ drivers/gpio/gpiolib.c | 12 +++++++++++- include/linux/gpio.h | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index 620a07844e8c..e08a883de36e 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -322,6 +322,9 @@ where 'flags' is currently defined to specify the following properties: * GPIOF_OPEN_DRAIN - gpio pin is open drain type. * GPIOF_OPEN_SOURCE - gpio pin is open source type. + * GPIOF_EXPORT_DIR_FIXED - export gpio to sysfs, keep direction + * GPIOF_EXPORT_DIR_CHANGEABLE - also export, allow changing direction + since GPIOF_INIT_* are only valid when configured as output, so group valid combinations as: diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5a75510d66bb..566d0122d832 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1302,8 +1302,18 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) (flags & GPIOF_INIT_HIGH) ? 1 : 0); if (err) - gpio_free(gpio); + goto free_gpio; + + if (flags & GPIOF_EXPORT) { + err = gpio_export(gpio, flags & GPIOF_EXPORT_CHANGEABLE); + if (err) + goto free_gpio; + } + + return 0; + free_gpio: + gpio_free(gpio); return err; } EXPORT_SYMBOL_GPL(gpio_request_one); diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 6155ecf192b0..af511a682925 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -20,6 +20,11 @@ /* Gpio pin is open source */ #define GPIOF_OPEN_SOURCE (1 << 3) +#define GPIOF_EXPORT (1 << 2) +#define GPIOF_EXPORT_CHANGEABLE (1 << 3) +#define GPIOF_EXPORT_DIR_FIXED (GPIOF_EXPORT) +#define GPIOF_EXPORT_DIR_CHANGEABLE (GPIOF_EXPORT | GPIOF_EXPORT_CHANGEABLE) + /** * struct gpio - a structure describing a GPIO with configuration * @gpio: the GPIO number -- cgit v1.2.3 From 2c96922ae3f0bfb7324a7a433d96d319fe6de729 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 4 Apr 2012 16:14:48 +0100 Subject: gpiolib: Add !CONFIG_GPIOLIB definitions of devm_ functions Currently the managed gpio_request() and gpio_free() are not stubbed out for configurations not using gpiolib - do that to aid use in drivers. Signed-off-by: Mark Brown Signed-off-by: Grant Likely --- include/linux/gpio.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include/linux') diff --git a/include/linux/gpio.h b/include/linux/gpio.h index af511a682925..d1890d46b6ce 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -60,6 +60,12 @@ static inline int gpio_request(unsigned gpio, const char *label) return -ENOSYS; } +static inline int devm_gpio_request(struct device *dev, unsigned gpio, + const char *label) +{ + return -ENOSYS; +} + static inline int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { @@ -79,6 +85,14 @@ static inline void gpio_free(unsigned gpio) WARN_ON(1); } +static inline void devm_gpio_free(struct device *dev, unsigned gpio) +{ + might_sleep(); + + /* GPIO can never have been requested */ + WARN_ON(1); +} + static inline void gpio_free_array(const struct gpio *array, size_t num) { might_sleep(); -- cgit v1.2.3 From 0135bbcc7a0cc056f0203ff839466236b8e3dc19 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:30 -0600 Subject: regmap: introduce explicit bus_context for bus callbacks The only context needed by I2C and SPI bus definitions is the device itself; this can be converted to an i2c_client or spi_device in order to perform IO on the device. However, other bus types may need more context in order to perform IO. Enable this by having regmap_init accept a bus_context parameter, and pass this to all bus callbacks. The existing callbacks simply pass the struct device here. Future bus types may pass something else. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap-i2c.c | 13 ++++++++----- drivers/base/regmap/regmap-spi.c | 13 ++++++++----- drivers/base/regmap/regmap.c | 19 +++++++++++++------ include/linux/regmap.h | 10 +++++++--- 5 files changed, 37 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index fcafc5b2e651..b95fd1f25295 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -38,6 +38,7 @@ struct regmap { void *work_buf; /* Scratch buffer used to format I/O */ struct regmap_format format; /* Buffer format */ const struct regmap_bus *bus; + void *bus_context; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 9a3a8c564389..5f6b2478bf17 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -15,8 +15,9 @@ #include #include -static int regmap_i2c_write(struct device *dev, const void *data, size_t count) +static int regmap_i2c_write(void *context, const void *data, size_t count) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); int ret; @@ -29,10 +30,11 @@ static int regmap_i2c_write(struct device *dev, const void *data, size_t count) return -EIO; } -static int regmap_i2c_gather_write(struct device *dev, +static int regmap_i2c_gather_write(void *context, const void *reg, size_t reg_size, const void *val, size_t val_size) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); struct i2c_msg xfer[2]; int ret; @@ -62,10 +64,11 @@ static int regmap_i2c_gather_write(struct device *dev, return -EIO; } -static int regmap_i2c_read(struct device *dev, +static int regmap_i2c_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); struct i2c_msg xfer[2]; int ret; @@ -107,7 +110,7 @@ static struct regmap_bus regmap_i2c = { struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config) { - return regmap_init(&i2c->dev, ®map_i2c, config); + return regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config); } EXPORT_SYMBOL_GPL(regmap_init_i2c); @@ -124,7 +127,7 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c); struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config) { - return devm_regmap_init(&i2c->dev, ®map_i2c, config); + return devm_regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config); } EXPORT_SYMBOL_GPL(devm_regmap_init_i2c); diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 7c0c35a39c33..ffa46a92ad33 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -15,17 +15,19 @@ #include #include -static int regmap_spi_write(struct device *dev, const void *data, size_t count) +static int regmap_spi_write(void *context, const void *data, size_t count) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); return spi_write(spi, data, count); } -static int regmap_spi_gather_write(struct device *dev, +static int regmap_spi_gather_write(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); struct spi_message m; struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, }, @@ -38,10 +40,11 @@ static int regmap_spi_gather_write(struct device *dev, return spi_sync(spi, &m); } -static int regmap_spi_read(struct device *dev, +static int regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); return spi_write_then_read(spi, reg, reg_size, val, val_size); @@ -66,7 +69,7 @@ static struct regmap_bus regmap_spi = { struct regmap *regmap_init_spi(struct spi_device *spi, const struct regmap_config *config) { - return regmap_init(&spi->dev, ®map_spi, config); + return regmap_init(&spi->dev, ®map_spi, &spi->dev, config); } EXPORT_SYMBOL_GPL(regmap_init_spi); @@ -83,7 +86,7 @@ EXPORT_SYMBOL_GPL(regmap_init_spi); struct regmap *devm_regmap_init_spi(struct spi_device *spi, const struct regmap_config *config) { - return devm_regmap_init(&spi->dev, ®map_spi, config); + return devm_regmap_init(&spi->dev, ®map_spi, &spi->dev, config); } EXPORT_SYMBOL_GPL(devm_regmap_init_spi); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7a3f535e481c..bde30c5d5ee9 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -163,6 +163,7 @@ static unsigned int regmap_parse_32(void *buf) * * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device + * @bus_context: Data passed to bus-specific callbacks * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to @@ -171,6 +172,7 @@ static unsigned int regmap_parse_32(void *buf) */ struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config) { struct regmap *map; @@ -193,6 +195,7 @@ struct regmap *regmap_init(struct device *dev, map->format.buf_size += map->format.pad_bytes; map->dev = dev; map->bus = bus; + map->bus_context = bus_context; map->max_register = config->max_register; map->writeable_reg = config->writeable_reg; map->readable_reg = config->readable_reg; @@ -316,6 +319,7 @@ static void devm_regmap_release(struct device *dev, void *res) * * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device + * @bus_context: Data passed to bus-specific callbacks * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer @@ -325,6 +329,7 @@ static void devm_regmap_release(struct device *dev, void *res) */ struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config) { struct regmap **ptr, *regmap; @@ -333,7 +338,7 @@ struct regmap *devm_regmap_init(struct device *dev, if (!ptr) return ERR_PTR(-ENOMEM); - regmap = regmap_init(dev, bus, config); + regmap = regmap_init(dev, bus, bus_context, config); if (!IS_ERR(regmap)) { *ptr = regmap; devres_add(dev, ptr); @@ -391,6 +396,8 @@ void regmap_exit(struct regmap *map) { regcache_exit(map); regmap_debugfs_exit(map); + if (map->bus->free_context) + map->bus->free_context(map->bus_context); kfree(map->work_buf); kfree(map); } @@ -444,12 +451,12 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, */ if (val == (map->work_buf + map->format.pad_bytes + map->format.reg_bytes)) - ret = map->bus->write(map->dev, map->work_buf, + ret = map->bus->write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes + val_len); else if (map->bus->gather_write) - ret = map->bus->gather_write(map->dev, map->work_buf, + ret = map->bus->gather_write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); @@ -464,7 +471,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, memcpy(buf, map->work_buf, map->format.reg_bytes); memcpy(buf + map->format.reg_bytes + map->format.pad_bytes, val, val_len); - ret = map->bus->write(map->dev, buf, len); + ret = map->bus->write(map->bus_context, buf, len); kfree(buf); } @@ -498,7 +505,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_hw_write_start(map->dev, reg, 1); - ret = map->bus->write(map->dev, map->work_buf, + ret = map->bus->write(map->bus_context, map->work_buf, map->format.buf_size); trace_regmap_hw_write_done(map->dev, reg, 1); @@ -639,7 +646,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, trace_regmap_hw_read_start(map->dev, reg, val_len / map->format.val_bytes); - ret = map->bus->read(map->dev, map->work_buf, + ret = map->bus->read(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a90abb6bfa64..8fd341e613d6 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -97,14 +97,15 @@ struct regmap_config { u8 write_flag_mask; }; -typedef int (*regmap_hw_write)(struct device *dev, const void *data, +typedef int (*regmap_hw_write)(void *context, const void *data, size_t count); -typedef int (*regmap_hw_gather_write)(struct device *dev, +typedef int (*regmap_hw_gather_write)(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len); -typedef int (*regmap_hw_read)(struct device *dev, +typedef int (*regmap_hw_read)(void *context, const void *reg_buf, size_t reg_size, void *val_buf, size_t val_size); +typedef void (*regmap_hw_free_context)(void *context); /** * Description of a hardware bus for the register map infrastructure. @@ -121,11 +122,13 @@ struct regmap_bus { regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_read read; + regmap_hw_free_context free_context; u8 read_flag_mask; }; struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config); struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); @@ -134,6 +137,7 @@ struct regmap *regmap_init_spi(struct spi_device *dev, struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config); struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); -- cgit v1.2.3 From bacdbe077342ecc9e7b3e374cc5a41995116706a Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:28 -0600 Subject: regmap: introduce fast_io busses, and use a spinlock for them Some bus types have very fast IO. For these, acquiring a mutex for every IO operation is a significant overhead. Allow busses to indicate their IO is fast, and enhance regmap to use a spinlock for those busses. [Currently limited to native endian registers -- broonie] Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 8 ++++- drivers/base/regmap/regcache-rbtree.c | 4 +-- drivers/base/regmap/regcache.c | 20 +++++------ drivers/base/regmap/regmap.c | 62 +++++++++++++++++++++++++---------- include/linux/regmap.h | 3 ++ 5 files changed, 67 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b95fd1f25295..e46c279f8280 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -31,8 +31,14 @@ struct regmap_format { unsigned int (*parse_val)(void *buf); }; +typedef void (*regmap_lock)(struct regmap *map); +typedef void (*regmap_unlock)(struct regmap *map); + struct regmap { - struct mutex lock; + struct mutex mutex; + spinlock_t spinlock; + regmap_lock lock; + regmap_unlock unlock; struct device *dev; /* Device we do I/O on */ void *work_buf; /* Scratch buffer used to format I/O */ diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 5157fa04c2f0..cca46007d969 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -139,7 +139,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) int nodes = 0; int registers = 0; - mutex_lock(&map->lock); + map->lock(map); for (node = rb_first(&rbtree_ctx->root); node != NULL; node = rb_next(node)) { @@ -155,7 +155,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) seq_printf(s, "%d nodes, %d registers, average %d registers\n", nodes, registers, registers / nodes); - mutex_unlock(&map->lock); + map->unlock(map); return 0; } diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 87f54dbf601b..4ad18505e9ae 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -264,7 +264,7 @@ int regcache_sync(struct regmap *map) BUG_ON(!map->cache_ops || !map->cache_ops->sync); - mutex_lock(&map->lock); + map->lock(map); /* Remember the initial bypass state */ bypass = map->cache_bypass; dev_dbg(map->dev, "Syncing %s cache\n", @@ -296,7 +296,7 @@ out: trace_regcache_sync(map->dev, name, "stop"); /* Restore the bypass state */ map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -323,7 +323,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, BUG_ON(!map->cache_ops || !map->cache_ops->sync); - mutex_lock(&map->lock); + map->lock(map); /* Remember the initial bypass state */ bypass = map->cache_bypass; @@ -342,7 +342,7 @@ out: trace_regcache_sync(map->dev, name, "stop region"); /* Restore the bypass state */ map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -361,11 +361,11 @@ out: */ void regcache_cache_only(struct regmap *map, bool enable) { - mutex_lock(&map->lock); + map->lock(map); WARN_ON(map->cache_bypass && enable); map->cache_only = enable; trace_regmap_cache_only(map->dev, enable); - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_cache_only); @@ -380,9 +380,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only); */ void regcache_mark_dirty(struct regmap *map) { - mutex_lock(&map->lock); + map->lock(map); map->cache_dirty = true; - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_mark_dirty); @@ -399,11 +399,11 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty); */ void regcache_cache_bypass(struct regmap *map, bool enable) { - mutex_lock(&map->lock); + map->lock(map); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; trace_regmap_cache_bypass(map->dev, enable); - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_cache_bypass); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index bde30c5d5ee9..6b4a775b439b 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -158,6 +158,26 @@ static unsigned int regmap_parse_32(void *buf) return b[0]; } +static void regmap_lock_mutex(struct regmap *map) +{ + mutex_lock(&map->mutex); +} + +static void regmap_unlock_mutex(struct regmap *map) +{ + mutex_unlock(&map->mutex); +} + +static void regmap_lock_spinlock(struct regmap *map) +{ + spin_lock(&map->spinlock); +} + +static void regmap_unlock_spinlock(struct regmap *map) +{ + spin_unlock(&map->spinlock); +} + /** * regmap_init(): Initialise register map * @@ -187,7 +207,15 @@ struct regmap *regmap_init(struct device *dev, goto err; } - mutex_init(&map->lock); + if (bus->fast_io) { + spin_lock_init(&map->spinlock); + map->lock = regmap_lock_spinlock; + map->unlock = regmap_unlock_spinlock; + } else { + mutex_init(&map->mutex); + map->lock = regmap_lock_mutex; + map->unlock = regmap_unlock_mutex; + } map->format.buf_size = (config->reg_bits + config->val_bits) / 8; map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; @@ -365,7 +393,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) { int ret; - mutex_lock(&map->lock); + map->lock(map); regcache_exit(map); regmap_debugfs_exit(map); @@ -384,7 +412,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) ret = regcache_init(map, config); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -536,11 +564,11 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_write(map, reg, val); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -567,11 +595,11 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, { int ret; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_raw_write(map, reg, val, val_len); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -601,7 +629,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (!map->format.parse_val) return -EINVAL; - mutex_lock(&map->lock); + map->lock(map); /* No formatting is require if val_byte is 1 */ if (val_bytes == 1) { @@ -622,7 +650,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, kfree(wval); out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_write); @@ -696,11 +724,11 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_read(map, reg, val); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -725,7 +753,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int v; int ret, i; - mutex_lock(&map->lock); + map->lock(map); if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || map->cache_type == REGCACHE_NONE) { @@ -746,7 +774,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, } out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -799,7 +827,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int ret; unsigned int tmp, orig; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_read(map, reg, &orig); if (ret != 0) @@ -816,7 +844,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, } out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -883,7 +911,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, if (map->patch) return -EBUSY; - mutex_lock(&map->lock); + map->lock(map); bypass = map->cache_bypass; @@ -911,7 +939,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, out: map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 8fd341e613d6..f14588a96eaf 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -110,6 +110,8 @@ typedef void (*regmap_hw_free_context)(void *context); /** * Description of a hardware bus for the register map infrastructure. * + * @fast_io: Register IO is fast. Use a spinlock instead of a mutex + * to perform locking. * @write: Write operation. * @gather_write: Write operation with split register/value, return -ENOTSUPP * if not implemented on a given device. @@ -119,6 +121,7 @@ typedef void (*regmap_hw_free_context)(void *context); * a read. */ struct regmap_bus { + bool fast_io; regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_read read; -- cgit v1.2.3 From 45f5ff8107a845854b1d1812ab1d9c5541f08b4d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:31 -0600 Subject: regmap: add MMIO bus support This is a basic memory-mapped-IO bus for regmap. It has the following features and limitations: * Registers themselves may be 8, 16, 32, or 64-bit. 64-bit is only supported on 64-bit platforms. * Register offsets are limited to precisely 32-bit. * IO is performed using readl/writel, with no provision for using the __raw_readl or readl_relaxed variants. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/Kconfig | 3 + drivers/base/regmap/Makefile | 1 + drivers/base/regmap/regmap-mmio.c | 217 ++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 6 ++ 4 files changed, 227 insertions(+) create mode 100644 drivers/base/regmap/regmap-mmio.c (limited to 'include/linux') diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 0f6c7fb418e8..9ef0a5326f17 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -14,5 +14,8 @@ config REGMAP_I2C config REGMAP_SPI tristate +config REGMAP_MMIO + tristate + config REGMAP_IRQ bool diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index defd57963c84..5e75d1b683e2 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -3,4 +3,5 @@ obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o +obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c new file mode 100644 index 000000000000..1a7b5ee11abc --- /dev/null +++ b/drivers/base/regmap/regmap-mmio.c @@ -0,0 +1,217 @@ +/* + * Register map access API - MMIO support + * + * Copyright (c) 2012, 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 + +struct regmap_mmio_context { + void __iomem *regs; + unsigned val_bytes; +}; + +static int regmap_mmio_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct regmap_mmio_context *ctx = context; + u32 offset; + + if (reg_size != 4) + return -EIO; + if (val_size % ctx->val_bytes) + return -EIO; + + offset = be32_to_cpup(reg); + + while (val_size) { + switch (ctx->val_bytes) { + case 1: + writeb(*(u8 *)val, ctx->regs + offset); + break; + case 2: + writew(be16_to_cpup(val), ctx->regs + offset); + break; + case 4: + writel(be32_to_cpup(val), ctx->regs + offset); + break; +#ifdef CONFIG_64BIT + case 8: + writeq(be64_to_cpup(val), ctx->regs + offset); + break; +#endif + default: + /* Should be caught by regmap_mmio_check_config */ + return -EIO; + } + val_size -= ctx->val_bytes; + val += ctx->val_bytes; + offset += ctx->val_bytes; + } + + return 0; +} + +static int regmap_mmio_write(void *context, const void *data, size_t count) +{ + if (count < 4) + return -EIO; + return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); +} + +static int regmap_mmio_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct regmap_mmio_context *ctx = context; + u32 offset; + + if (reg_size != 4) + return -EIO; + if (val_size % ctx->val_bytes) + return -EIO; + + offset = be32_to_cpup(reg); + + while (val_size) { + switch (ctx->val_bytes) { + case 1: + *(u8 *)val = readb(ctx->regs + offset); + break; + case 2: + *(u16 *)val = cpu_to_be16(readw(ctx->regs + offset)); + break; + case 4: + *(u32 *)val = cpu_to_be32(readl(ctx->regs + offset)); + break; +#ifdef CONFIG_64BIT + case 8: + *(u64 *)val = cpu_to_be32(readq(ctx->regs + offset)); + break; +#endif + default: + /* Should be caught by regmap_mmio_check_config */ + return -EIO; + } + val_size -= ctx->val_bytes; + val += ctx->val_bytes; + offset += ctx->val_bytes; + } + + return 0; +} + +static void regmap_mmio_free_context(void *context) +{ + kfree(context); +} + +static struct regmap_bus regmap_mmio = { + .fast_io = true, + .write = regmap_mmio_write, + .gather_write = regmap_mmio_gather_write, + .read = regmap_mmio_read, + .free_context = regmap_mmio_free_context, +}; + +struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + if (config->reg_bits != 32) + return ERR_PTR(-EINVAL); + + if (config->pad_bits) + return ERR_PTR(-EINVAL); + + switch (config->val_bits) { + case 8: + case 16: + case 32: +#ifdef CONFIG_64BIT + case 64: +#endif + break; + default: + return ERR_PTR(-EINVAL); + } + + ctx = kzalloc(GFP_KERNEL, sizeof(*ctx)); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->regs = regs; + ctx->val_bytes = config->val_bits / 8; + + return ctx; +} + +/** + * regmap_init_mmio(): Initialise register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + ctx = regmap_mmio_gen_context(regs, config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return regmap_init(dev, ®map_mmio, ctx, config); +} +EXPORT_SYMBOL_GPL(regmap_init_mmio); + +/** + * devm_regmap_init_mmio(): Initialise managed register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + ctx = regmap_mmio_gen_context(regs, config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return devm_regmap_init(dev, ®map_mmio, ctx, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_mmio); + +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f14588a96eaf..f6abc8d33d64 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -137,6 +137,9 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config); struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, @@ -146,6 +149,9 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *devm_regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *devm_regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config); void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, -- cgit v1.2.3 From 7e6bd8fadd1216f50468f965d0308f45e5109ced Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 14 Nov 2011 19:29:17 -0800 Subject: cred: Add forward declaration of init_user_ns in all cases. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/cred.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/cred.h b/include/linux/cred.h index adadf71a7327..d12c4e475c15 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -357,11 +357,11 @@ static inline void put_cred(const struct cred *_cred) #define current_user() (current_cred_xxx(user)) #define current_security() (current_cred_xxx(security)) +extern struct user_namespace init_user_ns; #ifdef CONFIG_USER_NS #define current_user_ns() (current_cred_xxx(user_ns)) #define task_user_ns(task) (task_cred_xxx((task), user_ns)) #else -extern struct user_namespace init_user_ns; #define current_user_ns() (&init_user_ns) #define task_user_ns(task) (&init_user_ns) #endif -- cgit v1.2.3 From 0093ccb68f3753c0ba4d74c89d7e0f444b8d6123 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 21:52:53 -0800 Subject: cred: Refcount the user_ns pointed to by the cred. struct user_struct will shortly loose it's user_ns reference so make the cred user_ns reference a proper reference complete with reference counting. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/cred.h | 2 +- kernel/cred.c | 8 +++----- kernel/user_namespace.c | 8 +++++--- security/keys/process_keys.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cred.h b/include/linux/cred.h index d12c4e475c15..2c60ec802678 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -146,7 +146,7 @@ struct cred { void *security; /* subjective LSM security */ #endif struct user_struct *user; /* real user ID subscription */ - struct user_namespace *user_ns; /* cached user->user_ns */ + struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ struct group_info *group_info; /* supplementary groups for euid/fsgid */ struct rcu_head rcu; /* RCU deletion hook */ }; diff --git a/kernel/cred.c b/kernel/cred.c index 97b36eeca4c9..7a0d80669886 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -148,6 +148,7 @@ static void put_cred_rcu(struct rcu_head *rcu) if (cred->group_info) put_group_info(cred->group_info); free_uid(cred->user); + put_user_ns(cred->user_ns); kmem_cache_free(cred_jar, cred); } @@ -303,6 +304,7 @@ struct cred *prepare_creds(void) set_cred_subscribers(new, 0); get_group_info(new->group_info); get_uid(new->user); + get_user_ns(new->user_ns); #ifdef CONFIG_KEYS key_get(new->thread_keyring); @@ -412,11 +414,6 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) goto error_put; } - /* cache user_ns in cred. Doesn't need a refcount because it will - * stay pinned by cred->user - */ - new->user_ns = new->user->user_ns; - #ifdef CONFIG_KEYS /* new threads get their own thread keyrings if their parent already * had one */ @@ -676,6 +673,7 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon) atomic_set(&new->usage, 1); set_cred_subscribers(new, 0); get_uid(new->user); + get_user_ns(new->user_ns); get_group_info(new->group_info); #ifdef CONFIG_KEYS diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index f084083a0fd3..58bb8781a778 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -24,7 +24,7 @@ static struct kmem_cache *user_ns_cachep __read_mostly; */ int create_user_ns(struct cred *new) { - struct user_namespace *ns; + struct user_namespace *ns, *parent_ns = new->user_ns; struct user_struct *root_user; int n; @@ -57,8 +57,10 @@ int create_user_ns(struct cred *new) #endif /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */ - /* root_user holds a reference to ns, our reference can be dropped */ - put_user_ns(ns); + /* Leave the reference to our user_ns with the new cred */ + new->user_ns = ns; + + put_user_ns(parent_ns); return 0; } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 70febff06da9..447fb7618ff3 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -858,7 +858,7 @@ void key_replace_session_keyring(void) new-> sgid = old-> sgid; new->fsgid = old->fsgid; new->user = get_uid(old->user); - new->user_ns = new->user_ns; + new->user_ns = get_user_ns(new->user_ns); new->group_info = get_group_info(old->group_info); new->securebits = old->securebits; -- cgit v1.2.3 From aeb3ae9da9b50a386b22af786d19b623e8d9f0fa Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 21:59:43 -0800 Subject: userns: Add an explicit reference to the parent user namespace I am about to remove the struct user_namespace reference from struct user_struct. So keep an explicit track of the parent user namespace. Take advantage of this new reference and replace instances of user_ns->creator->user_ns with user_ns->parent. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/user_namespace.h | 1 + kernel/user_namespace.c | 13 ++++++------- security/commoncap.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index faf467944baf..dc2d85a76376 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -12,6 +12,7 @@ struct user_namespace { struct kref kref; struct hlist_head uidhash_table[UIDHASH_SZ]; + struct user_namespace *parent; struct user_struct *creator; struct work_struct destroyer; }; diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 58bb8781a778..c15e533d6bc5 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -45,6 +45,7 @@ int create_user_ns(struct cred *new) } /* set the new root user in the credentials under preparation */ + ns->parent = parent_ns; ns->creator = new->user; new->user = root_user; new->uid = new->euid = new->suid = new->fsuid = 0; @@ -60,8 +61,6 @@ int create_user_ns(struct cred *new) /* Leave the reference to our user_ns with the new cred */ new->user_ns = ns; - put_user_ns(parent_ns); - return 0; } @@ -72,10 +71,12 @@ int create_user_ns(struct cred *new) */ static void free_user_ns_work(struct work_struct *work) { - struct user_namespace *ns = + struct user_namespace *parent, *ns = container_of(work, struct user_namespace, destroyer); + parent = ns->parent; free_uid(ns->creator); kmem_cache_free(user_ns_cachep, ns); + put_user_ns(parent); } void free_user_ns(struct kref *kref) @@ -99,8 +100,7 @@ uid_t user_ns_map_uid(struct user_namespace *to, const struct cred *cred, uid_t /* Is cred->user the creator of the target user_ns * or the creator of one of it's parents? */ - for ( tmp = to; tmp != &init_user_ns; - tmp = tmp->creator->user_ns ) { + for ( tmp = to; tmp != &init_user_ns; tmp = tmp->parent ) { if (cred->user == tmp->creator) { return (uid_t)0; } @@ -120,8 +120,7 @@ gid_t user_ns_map_gid(struct user_namespace *to, const struct cred *cred, gid_t /* Is cred->user the creator of the target user_ns * or the creator of one of it's parents? */ - for ( tmp = to; tmp != &init_user_ns; - tmp = tmp->creator->user_ns ) { + for ( tmp = to; tmp != &init_user_ns; tmp = tmp->parent ) { if (cred->user == tmp->creator) { return (gid_t)0; } diff --git a/security/commoncap.c b/security/commoncap.c index 8b3e10e2eac7..435d074853f3 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -92,7 +92,7 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, *If you have a capability in a parent user ns, then you have * it over all children user namespaces as well. */ - targ_ns = targ_ns->creator->user_ns; + targ_ns = targ_ns->parent; } /* We never get here */ -- cgit v1.2.3 From d0bd6594e286bd6145e04e19e8d3fa2e902cb800 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 23:20:58 -0800 Subject: userns: Deprecate and rename the user_namespace reference in the user_struct With a user_ns reference in struct cred the only user of the user namespace reference in struct user_struct is to keep the uid hash table alive. The user_namespace reference in struct user_struct will be going away soon, and I have removed all of the references. Rename the field from user_ns to _user_ns so that the compiler can verify nothing follows the user struct to the user namespace anymore. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/sched.h | 2 +- kernel/user.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index 720ce8d98a7d..6867ae9bc8a0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -729,7 +729,7 @@ struct user_struct { /* Hash table maintenance information */ struct hlist_node uidhash_node; uid_t uid; - struct user_namespace *user_ns; + struct user_namespace *_user_ns; /* Don't use will be removed soon */ #ifdef CONFIG_PERF_EVENTS atomic_long_t locked_vm; diff --git a/kernel/user.c b/kernel/user.c index 71dd2363ab0f..d65fec0615a0 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -58,7 +58,7 @@ struct user_struct root_user = { .files = ATOMIC_INIT(0), .sigpending = ATOMIC_INIT(0), .locked_shm = 0, - .user_ns = &init_user_ns, + ._user_ns = &init_user_ns, }; /* @@ -72,7 +72,7 @@ static void uid_hash_insert(struct user_struct *up, struct hlist_head *hashent) static void uid_hash_remove(struct user_struct *up) { hlist_del_init(&up->uidhash_node); - put_user_ns(up->user_ns); + put_user_ns(up->_user_ns); /* It is safe to free the uid hash table now */ } static struct user_struct *uid_hash_find(uid_t uid, struct hlist_head *hashent) @@ -153,7 +153,7 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid) new->uid = uid; atomic_set(&new->__count, 1); - new->user_ns = get_user_ns(ns); + new->_user_ns = get_user_ns(ns); /* * Before adding this, check whether we raced -- cgit v1.2.3 From 1a48e2ac034d47ed843081c4523b63c46b46888b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 14 Nov 2011 16:24:06 -0800 Subject: userns: Replace the hard to write inode_userns with inode_capable. This represents a change in strategy of how to handle user namespaces. Instead of tagging everything explicitly with a user namespace and bulking up all of the comparisons of uids and gids in the kernel, all uids and gids in use will have a mapping to a flat kuid and kgid spaces respectively. This allows much more of the existing logic to be preserved and in general allows for faster code. In this new and improved world we allow someone to utiliize capabilities over an inode if the inodes owner mapps into the capabilities holders user namespace and the user has capabilities in their user namespace. Which is simple and efficient. Moving the fs uid comparisons to be comparisons in a flat kuid space follows in later patches, something that is only significant if you are using user namespaces. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- fs/inode.c | 6 ++---- fs/namei.c | 18 +++++------------- include/linux/capability.h | 2 ++ include/linux/fs.h | 6 ------ kernel/capability.c | 19 +++++++++++++++++++ 5 files changed, 28 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/fs/inode.c b/fs/inode.c index 9f4f5fecc096..f0c4ace408e4 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1732,11 +1732,9 @@ EXPORT_SYMBOL(inode_init_owner); */ bool inode_owner_or_capable(const struct inode *inode) { - struct user_namespace *ns = inode_userns(inode); - - if (current_user_ns() == ns && current_fsuid() == inode->i_uid) + if (current_fsuid() == inode->i_uid) return true; - if (ns_capable(ns, CAP_FOWNER)) + if (inode_capable(inode, CAP_FOWNER)) return true; return false; } diff --git a/fs/namei.c b/fs/namei.c index 701954d68ac7..941c4362e298 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -228,9 +228,6 @@ static int acl_permission_check(struct inode *inode, int mask) { unsigned int mode = inode->i_mode; - if (current_user_ns() != inode_userns(inode)) - goto other_perms; - if (likely(current_fsuid() == inode->i_uid)) mode >>= 6; else { @@ -244,7 +241,6 @@ static int acl_permission_check(struct inode *inode, int mask) mode >>= 3; } -other_perms: /* * If the DACs are ok we don't need any capability check. */ @@ -280,10 +276,10 @@ int generic_permission(struct inode *inode, int mask) if (S_ISDIR(inode->i_mode)) { /* DACs are overridable for directories */ - if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE)) + if (inode_capable(inode, CAP_DAC_OVERRIDE)) return 0; if (!(mask & MAY_WRITE)) - if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH)) + if (inode_capable(inode, CAP_DAC_READ_SEARCH)) return 0; return -EACCES; } @@ -293,7 +289,7 @@ int generic_permission(struct inode *inode, int mask) * at least one exec bit set. */ if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO)) - if (ns_capable(inode_userns(inode), CAP_DAC_OVERRIDE)) + if (inode_capable(inode, CAP_DAC_OVERRIDE)) return 0; /* @@ -301,7 +297,7 @@ int generic_permission(struct inode *inode, int mask) */ mask &= MAY_READ | MAY_WRITE | MAY_EXEC; if (mask == MAY_READ) - if (ns_capable(inode_userns(inode), CAP_DAC_READ_SEARCH)) + if (inode_capable(inode, CAP_DAC_READ_SEARCH)) return 0; return -EACCES; @@ -1964,15 +1960,11 @@ static inline int check_sticky(struct inode *dir, struct inode *inode) if (!(dir->i_mode & S_ISVTX)) return 0; - if (current_user_ns() != inode_userns(inode)) - goto other_userns; if (inode->i_uid == fsuid) return 0; if (dir->i_uid == fsuid) return 0; - -other_userns: - return !ns_capable(inode_userns(inode), CAP_FOWNER); + return !inode_capable(inode, CAP_FOWNER); } /* diff --git a/include/linux/capability.h b/include/linux/capability.h index 12d52dedb229..a76eca907470 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -374,6 +374,7 @@ struct cpu_vfs_cap_data { #ifdef __KERNEL__ +struct inode; struct dentry; struct user_namespace; @@ -548,6 +549,7 @@ extern bool has_ns_capability_noaudit(struct task_struct *t, extern bool capable(int cap); extern bool ns_capable(struct user_namespace *ns, int cap); extern bool nsown_capable(int cap); +extern bool inode_capable(const struct inode *inode, int cap); /* audit system wants to get cap info from files as well */ extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps); diff --git a/include/linux/fs.h b/include/linux/fs.h index 135693e79f2b..a6c5efbee0d7 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1522,12 +1522,6 @@ enum { #define vfs_check_frozen(sb, level) \ wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level))) -/* - * until VFS tracks user namespaces for inodes, just make all files - * belong to init_user_ns - */ -extern struct user_namespace init_user_ns; -#define inode_userns(inode) (&init_user_ns) extern bool inode_owner_or_capable(const struct inode *inode); /* not quite ready to be deprecated, but... */ diff --git a/kernel/capability.c b/kernel/capability.c index 3f1adb6c6470..cc5f0718215d 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -419,3 +419,22 @@ bool nsown_capable(int cap) { return ns_capable(current_user_ns(), cap); } + +/** + * inode_capable - Check superior capability over inode + * @inode: The inode in question + * @cap: The capability in question + * + * Return true if the current task has the given superior capability + * targeted at it's own user namespace and that the given inode is owned + * by the current user namespace or a child namespace. + * + * Currently inodes can only be owned by the initial user namespace. + * + */ +bool inode_capable(const struct inode *inode, int cap) +{ + struct user_namespace *ns = current_user_ns(); + + return ns_capable(ns, cap) && (ns == &init_user_ns); +} -- cgit v1.2.3 From 7a4e7408c5cadb240e068a662251754a562355e3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 14 Nov 2011 14:29:51 -0800 Subject: userns: Add kuid_t and kgid_t and associated infrastructure in uidgid.h Start distinguishing between internal kernel uids and gids and values that userspace can use. This is done by introducing two new types: kuid_t and kgid_t. These types and their associated functions are infrastructure are declared in the new header uidgid.h. Ultimately there will be a different implementation of the mapping functions for use with user namespaces. But to keep it simple we introduce the mapping functions first to separate the meat from the mechanical code conversions. Export overflowuid and overflowgid so we can use from_kuid_munged and from_kgid_munged in modular code. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/uidgid.h | 176 +++++++++++++++++++++++++++++++++++++++++++++++++ kernel/sys.c | 2 - 2 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 include/linux/uidgid.h (limited to 'include/linux') diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h new file mode 100644 index 000000000000..a0addb8e5889 --- /dev/null +++ b/include/linux/uidgid.h @@ -0,0 +1,176 @@ +#ifndef _LINUX_UIDGID_H +#define _LINUX_UIDGID_H + +/* + * A set of types for the internal kernel types representing uids and gids. + * + * The types defined in this header allow distinguishing which uids and gids in + * the kernel are values used by userspace and which uid and gid values are + * the internal kernel values. With the addition of user namespaces the values + * can be different. Using the type system makes it possible for the compiler + * to detect when we overlook these differences. + * + */ +#include +#include + +struct user_namespace; +extern struct user_namespace init_user_ns; + +#if defined(NOTYET) + +typedef struct { + uid_t val; +} kuid_t; + + +typedef struct { + gid_t val; +} kgid_t; + +#define KUIDT_INIT(value) (kuid_t){ value } +#define KGIDT_INIT(value) (kgid_t){ value } + +static inline uid_t __kuid_val(kuid_t uid) +{ + return uid.val; +} + +static inline gid_t __kgid_val(kgid_t gid) +{ + return gid.val; +} + +#else + +typedef uid_t kuid_t; +typedef gid_t kgid_t; + +static inline uid_t __kuid_val(kuid_t uid) +{ + return uid; +} + +static inline gid_t __kgid_val(kgid_t gid) +{ + return gid; +} + +#define KUIDT_INIT(value) ((kuid_t) value ) +#define KGIDT_INIT(value) ((kgid_t) value ) + +#endif + +#define GLOBAL_ROOT_UID KUIDT_INIT(0) +#define GLOBAL_ROOT_GID KGIDT_INIT(0) + +#define INVALID_UID KUIDT_INIT(-1) +#define INVALID_GID KGIDT_INIT(-1) + +static inline bool uid_eq(kuid_t left, kuid_t right) +{ + return __kuid_val(left) == __kuid_val(right); +} + +static inline bool gid_eq(kgid_t left, kgid_t right) +{ + return __kgid_val(left) == __kgid_val(right); +} + +static inline bool uid_gt(kuid_t left, kuid_t right) +{ + return __kuid_val(left) > __kuid_val(right); +} + +static inline bool gid_gt(kgid_t left, kgid_t right) +{ + return __kgid_val(left) > __kgid_val(right); +} + +static inline bool uid_gte(kuid_t left, kuid_t right) +{ + return __kuid_val(left) >= __kuid_val(right); +} + +static inline bool gid_gte(kgid_t left, kgid_t right) +{ + return __kgid_val(left) >= __kgid_val(right); +} + +static inline bool uid_lt(kuid_t left, kuid_t right) +{ + return __kuid_val(left) < __kuid_val(right); +} + +static inline bool gid_lt(kgid_t left, kgid_t right) +{ + return __kgid_val(left) < __kgid_val(right); +} + +static inline bool uid_lte(kuid_t left, kuid_t right) +{ + return __kuid_val(left) <= __kuid_val(right); +} + +static inline bool gid_lte(kgid_t left, kgid_t right) +{ + return __kgid_val(left) <= __kgid_val(right); +} + +static inline bool uid_valid(kuid_t uid) +{ + return !uid_eq(uid, INVALID_UID); +} + +static inline bool gid_valid(kgid_t gid) +{ + return !gid_eq(gid, INVALID_GID); +} + +static inline kuid_t make_kuid(struct user_namespace *from, uid_t uid) +{ + return KUIDT_INIT(uid); +} + +static inline kgid_t make_kgid(struct user_namespace *from, gid_t gid) +{ + return KGIDT_INIT(gid); +} + +static inline uid_t from_kuid(struct user_namespace *to, kuid_t kuid) +{ + return __kuid_val(kuid); +} + +static inline gid_t from_kgid(struct user_namespace *to, kgid_t kgid) +{ + return __kgid_val(kgid); +} + +static inline uid_t from_kuid_munged(struct user_namespace *to, kuid_t kuid) +{ + uid_t uid = from_kuid(to, kuid); + if (uid == (uid_t)-1) + uid = overflowuid; + return uid; +} + +static inline gid_t from_kgid_munged(struct user_namespace *to, kgid_t kgid) +{ + gid_t gid = from_kgid(to, kgid); + if (gid == (gid_t)-1) + gid = overflowgid; + return gid; +} + +static inline bool kuid_has_mapping(struct user_namespace *ns, kuid_t uid) +{ + return true; +} + +static inline bool kgid_has_mapping(struct user_namespace *ns, kgid_t gid) +{ + return true; +} + +#endif /* _LINUX_UIDGID_H */ diff --git a/kernel/sys.c b/kernel/sys.c index 82d8714bbede..71852417cfc8 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -93,10 +93,8 @@ int overflowuid = DEFAULT_OVERFLOWUID; int overflowgid = DEFAULT_OVERFLOWGID; -#ifdef CONFIG_UID16 EXPORT_SYMBOL(overflowuid); EXPORT_SYMBOL(overflowgid); -#endif /* * the same as above, but for filesystems which can only store a 16-bit -- cgit v1.2.3 From 5673a94c14574d7c6495c320c6b0e480673d54bd Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 17 Nov 2011 10:23:55 -0800 Subject: userns: Add a Kconfig option to enforce strict kuid and kgid type checks Make it possible to easily switch between strong mandatory type checks and relaxed type checks so that the code can easily be tested with the type checks and then built with the strong type checks disabled so the resulting code can be used. Require strong mandatory type checks when enabling the user namespace. It is very simple to make a typo and use the wrong type allowing conversions to/from userspace values to be bypassed by accident, the strong type checks prevent this. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- include/linux/uidgid.h | 2 +- init/Kconfig | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h index a0addb8e5889..539856829caf 100644 --- a/include/linux/uidgid.h +++ b/include/linux/uidgid.h @@ -17,7 +17,7 @@ struct user_namespace; extern struct user_namespace init_user_ns; -#if defined(NOTYET) +#ifdef CONFIG_UIDGID_STRICT_TYPE_CHECKS typedef struct { uid_t val; diff --git a/init/Kconfig b/init/Kconfig index 72f33faca44f..86cf760893b3 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -828,7 +828,8 @@ config IPC_NS config USER_NS bool "User namespace (EXPERIMENTAL)" depends on EXPERIMENTAL - default y + select UIDGID_STRICT_TYPE_CHECKS + default n help This allows containers, i.e. vservers, to use user namespaces to provide different user info for different servers. @@ -852,6 +853,15 @@ config NET_NS endif # NAMESPACES +config UIDGID_STRICT_TYPE_CHECKS + bool "Require conversions between uid/gids and their internal representation" + default n + help + While the nececessary conversions are being added to all subsystems this option allows + the code to continue to build for unconverted subsystems. + + Say Y here if you want the strict type checking enabled + config SCHED_AUTOGROUP bool "Automatic process group scheduling" select EVENTFD -- cgit v1.2.3 From 7b44ab978b77a91b327058a0f4db7e6fcdb90b92 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 23:20:58 -0800 Subject: userns: Disassociate user_struct from the user_namespace. Modify alloc_uid to take a kuid and make the user hash table global. Stop holding a reference to the user namespace in struct user_struct. This simplifies the code and makes the per user accounting not care about which user namespace a uid happens to appear in. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- fs/ioprio.c | 18 ++++++++++++++---- include/linux/sched.h | 8 ++++---- include/linux/user_namespace.h | 4 ---- kernel/sys.c | 34 +++++++++++++++++++++++----------- kernel/user.c | 28 +++++++++++++--------------- kernel/user_namespace.c | 6 +----- 6 files changed, 55 insertions(+), 43 deletions(-) (limited to 'include/linux') diff --git a/fs/ioprio.c b/fs/ioprio.c index 0f1b9515213b..8e35e964d9ed 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -65,6 +65,7 @@ SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio) struct task_struct *p, *g; struct user_struct *user; struct pid *pgrp; + kuid_t uid; int ret; switch (class) { @@ -110,16 +111,21 @@ SYSCALL_DEFINE3(ioprio_set, int, which, int, who, int, ioprio) } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); break; case IOPRIO_WHO_USER: + uid = make_kuid(current_user_ns(), who); + if (!uid_valid(uid)) + break; if (!who) user = current_user(); else - user = find_user(who); + user = find_user(uid); if (!user) break; do_each_thread(g, p) { - if (__task_cred(p)->uid != who) + const struct cred *tcred = __task_cred(p); + kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); + if (!uid_eq(tcred_uid, uid)) continue; ret = set_task_ioprio(p, ioprio); if (ret) @@ -174,6 +180,7 @@ SYSCALL_DEFINE2(ioprio_get, int, which, int, who) struct task_struct *g, *p; struct user_struct *user; struct pid *pgrp; + kuid_t uid; int ret = -ESRCH; int tmpio; @@ -203,16 +210,19 @@ SYSCALL_DEFINE2(ioprio_get, int, which, int, who) } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); break; case IOPRIO_WHO_USER: + uid = make_kuid(current_user_ns(), who); if (!who) user = current_user(); else - user = find_user(who); + user = find_user(uid); if (!user) break; do_each_thread(g, p) { - if (__task_cred(p)->uid != user->uid) + const struct cred *tcred = __task_cred(p); + kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); + if (!uid_eq(tcred_uid, user->uid)) continue; tmpio = get_task_ioprio(p); if (tmpio < 0) diff --git a/include/linux/sched.h b/include/linux/sched.h index 6867ae9bc8a0..5fdc1ebbcbc4 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -90,6 +90,7 @@ struct sched_param { #include #include #include +#include #include @@ -728,8 +729,7 @@ struct user_struct { /* Hash table maintenance information */ struct hlist_node uidhash_node; - uid_t uid; - struct user_namespace *_user_ns; /* Don't use will be removed soon */ + kuid_t uid; #ifdef CONFIG_PERF_EVENTS atomic_long_t locked_vm; @@ -738,7 +738,7 @@ struct user_struct { extern int uids_sysfs_init(void); -extern struct user_struct *find_user(uid_t); +extern struct user_struct *find_user(kuid_t); extern struct user_struct root_user; #define INIT_USER (&root_user) @@ -2177,7 +2177,7 @@ extern struct task_struct *find_task_by_pid_ns(pid_t nr, extern void __set_special_pids(struct pid *pid); /* per-UID process charging. */ -extern struct user_struct * alloc_uid(struct user_namespace *, uid_t); +extern struct user_struct * alloc_uid(kuid_t); static inline struct user_struct *get_uid(struct user_struct *u) { atomic_inc(&u->__count); diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index dc2d85a76376..d767508db4f9 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -6,12 +6,8 @@ #include #include -#define UIDHASH_BITS (CONFIG_BASE_SMALL ? 3 : 7) -#define UIDHASH_SZ (1 << UIDHASH_BITS) - struct user_namespace { struct kref kref; - struct hlist_head uidhash_table[UIDHASH_SZ]; struct user_namespace *parent; struct user_struct *creator; struct work_struct destroyer; diff --git a/kernel/sys.c b/kernel/sys.c index 71852417cfc8..f0c43b4b6657 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -175,6 +175,8 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) const struct cred *cred = current_cred(); int error = -EINVAL; struct pid *pgrp; + kuid_t cred_uid; + kuid_t uid; if (which > PRIO_USER || which < PRIO_PROCESS) goto out; @@ -207,18 +209,22 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); break; case PRIO_USER: + cred_uid = make_kuid(cred->user_ns, cred->uid); + uid = make_kuid(cred->user_ns, who); user = cred->user; if (!who) - who = cred->uid; - else if ((who != cred->uid) && - !(user = find_user(who))) + uid = cred_uid; + else if (!uid_eq(uid, cred_uid) && + !(user = find_user(uid))) goto out_unlock; /* No processes for this user */ do_each_thread(g, p) { - if (__task_cred(p)->uid == who) + const struct cred *tcred = __task_cred(p); + kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); + if (uid_eq(tcred_uid, uid)) error = set_one_prio(p, niceval, error); } while_each_thread(g, p); - if (who != cred->uid) + if (!uid_eq(uid, cred_uid)) free_uid(user); /* For find_user() */ break; } @@ -242,6 +248,8 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) const struct cred *cred = current_cred(); long niceval, retval = -ESRCH; struct pid *pgrp; + kuid_t cred_uid; + kuid_t uid; if (which > PRIO_USER || which < PRIO_PROCESS) return -EINVAL; @@ -272,21 +280,25 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who) } while_each_pid_thread(pgrp, PIDTYPE_PGID, p); break; case PRIO_USER: + cred_uid = make_kuid(cred->user_ns, cred->uid); + uid = make_kuid(cred->user_ns, who); user = cred->user; if (!who) - who = cred->uid; - else if ((who != cred->uid) && - !(user = find_user(who))) + uid = cred_uid; + else if (!uid_eq(uid, cred_uid) && + !(user = find_user(uid))) goto out_unlock; /* No processes for this user */ do_each_thread(g, p) { - if (__task_cred(p)->uid == who) { + const struct cred *tcred = __task_cred(p); + kuid_t tcred_uid = make_kuid(tcred->user_ns, tcred->uid); + if (uid_eq(tcred_uid, uid)) { niceval = 20 - task_nice(p); if (niceval > retval) retval = niceval; } } while_each_thread(g, p); - if (who != cred->uid) + if (!uid_eq(uid, cred_uid)) free_uid(user); /* for find_user() */ break; } @@ -629,7 +641,7 @@ static int set_user(struct cred *new) { struct user_struct *new_user; - new_user = alloc_uid(current_user_ns(), new->uid); + new_user = alloc_uid(make_kuid(new->user_ns, new->uid)); if (!new_user) return -EAGAIN; diff --git a/kernel/user.c b/kernel/user.c index d65fec0615a0..025077e54a7c 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -34,11 +34,14 @@ EXPORT_SYMBOL_GPL(init_user_ns); * when changing user ID's (ie setuid() and friends). */ +#define UIDHASH_BITS (CONFIG_BASE_SMALL ? 3 : 7) +#define UIDHASH_SZ (1 << UIDHASH_BITS) #define UIDHASH_MASK (UIDHASH_SZ - 1) #define __uidhashfn(uid) (((uid >> UIDHASH_BITS) + uid) & UIDHASH_MASK) -#define uidhashentry(ns, uid) ((ns)->uidhash_table + __uidhashfn((uid))) +#define uidhashentry(uid) (uidhash_table + __uidhashfn((__kuid_val(uid)))) static struct kmem_cache *uid_cachep; +struct hlist_head uidhash_table[UIDHASH_SZ]; /* * The uidhash_lock is mostly taken from process context, but it is @@ -58,7 +61,7 @@ struct user_struct root_user = { .files = ATOMIC_INIT(0), .sigpending = ATOMIC_INIT(0), .locked_shm = 0, - ._user_ns = &init_user_ns, + .uid = GLOBAL_ROOT_UID, }; /* @@ -72,16 +75,15 @@ static void uid_hash_insert(struct user_struct *up, struct hlist_head *hashent) static void uid_hash_remove(struct user_struct *up) { hlist_del_init(&up->uidhash_node); - put_user_ns(up->_user_ns); /* It is safe to free the uid hash table now */ } -static struct user_struct *uid_hash_find(uid_t uid, struct hlist_head *hashent) +static struct user_struct *uid_hash_find(kuid_t uid, struct hlist_head *hashent) { struct user_struct *user; struct hlist_node *h; hlist_for_each_entry(user, h, hashent, uidhash_node) { - if (user->uid == uid) { + if (uid_eq(user->uid, uid)) { atomic_inc(&user->__count); return user; } @@ -110,14 +112,13 @@ static void free_user(struct user_struct *up, unsigned long flags) * * If the user_struct could not be found, return NULL. */ -struct user_struct *find_user(uid_t uid) +struct user_struct *find_user(kuid_t uid) { struct user_struct *ret; unsigned long flags; - struct user_namespace *ns = current_user_ns(); spin_lock_irqsave(&uidhash_lock, flags); - ret = uid_hash_find(uid, uidhashentry(ns, uid)); + ret = uid_hash_find(uid, uidhashentry(uid)); spin_unlock_irqrestore(&uidhash_lock, flags); return ret; } @@ -136,9 +137,9 @@ void free_uid(struct user_struct *up) local_irq_restore(flags); } -struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid) +struct user_struct *alloc_uid(kuid_t uid) { - struct hlist_head *hashent = uidhashentry(ns, uid); + struct hlist_head *hashent = uidhashentry(uid); struct user_struct *up, *new; spin_lock_irq(&uidhash_lock); @@ -153,8 +154,6 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid) new->uid = uid; atomic_set(&new->__count, 1); - new->_user_ns = get_user_ns(ns); - /* * Before adding this, check whether we raced * on adding the same user already.. @@ -162,7 +161,6 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid) spin_lock_irq(&uidhash_lock); up = uid_hash_find(uid, hashent); if (up) { - put_user_ns(ns); key_put(new->uid_keyring); key_put(new->session_keyring); kmem_cache_free(uid_cachep, new); @@ -187,11 +185,11 @@ static int __init uid_cache_init(void) 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); for(n = 0; n < UIDHASH_SZ; ++n) - INIT_HLIST_HEAD(init_user_ns.uidhash_table + n); + INIT_HLIST_HEAD(uidhash_table + n); /* Insert the root user immediately (init already runs as root) */ spin_lock_irq(&uidhash_lock); - uid_hash_insert(&root_user, uidhashentry(&init_user_ns, 0)); + uid_hash_insert(&root_user, uidhashentry(GLOBAL_ROOT_UID)); spin_unlock_irq(&uidhash_lock); return 0; diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index e216e1e8ce84..898e973bd1e8 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -27,7 +27,6 @@ int create_user_ns(struct cred *new) { struct user_namespace *ns, *parent_ns = new->user_ns; struct user_struct *root_user; - int n; ns = kmem_cache_alloc(user_ns_cachep, GFP_KERNEL); if (!ns) @@ -35,11 +34,8 @@ int create_user_ns(struct cred *new) kref_init(&ns->kref); - for (n = 0; n < UIDHASH_SZ; ++n) - INIT_HLIST_HEAD(ns->uidhash_table + n); - /* Alloc new root user. */ - root_user = alloc_uid(ns, 0); + root_user = alloc_uid(make_kuid(ns, 0)); if (!root_user) { kmem_cache_free(user_ns_cachep, ns); return -ENOMEM; -- cgit v1.2.3 From a13007160f1b9ec7c67e28ec9254f197c5c08d7d Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Fri, 9 Mar 2012 12:17:32 +0800 Subject: KVM: resize kvm_io_range array dynamically This patch makes the kvm_io_range array can be resized dynamically. Signed-off-by: Amos Kong Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- include/linux/kvm_host.h | 5 +++-- virt/kvm/kvm_main.c | 38 ++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 665a260c7e09..ba9fb4a9762d 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -68,10 +68,11 @@ struct kvm_io_range { struct kvm_io_device *dev; }; +#define NR_IOBUS_DEVS 300 + struct kvm_io_bus { int dev_count; -#define NR_IOBUS_DEVS 300 - struct kvm_io_range range[NR_IOBUS_DEVS]; + struct kvm_io_range range[]; }; enum kvm_bus { diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 42b73930a6de..a9565e240636 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2393,9 +2393,6 @@ int kvm_io_bus_sort_cmp(const void *p1, const void *p2) int kvm_io_bus_insert_dev(struct kvm_io_bus *bus, struct kvm_io_device *dev, gpa_t addr, int len) { - if (bus->dev_count == NR_IOBUS_DEVS) - return -ENOSPC; - bus->range[bus->dev_count++] = (struct kvm_io_range) { .addr = addr, .len = len, @@ -2495,12 +2492,15 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, struct kvm_io_bus *new_bus, *bus; bus = kvm->buses[bus_idx]; - if (bus->dev_count > NR_IOBUS_DEVS-1) + if (bus->dev_count > NR_IOBUS_DEVS - 1) return -ENOSPC; - new_bus = kmemdup(bus, sizeof(struct kvm_io_bus), GFP_KERNEL); + new_bus = kzalloc(sizeof(*bus) + ((bus->dev_count + 1) * + sizeof(struct kvm_io_range)), GFP_KERNEL); if (!new_bus) return -ENOMEM; + memcpy(new_bus, bus, sizeof(*bus) + (bus->dev_count * + sizeof(struct kvm_io_range))); kvm_io_bus_insert_dev(new_bus, dev, addr, len); rcu_assign_pointer(kvm->buses[bus_idx], new_bus); synchronize_srcu_expedited(&kvm->srcu); @@ -2517,27 +2517,25 @@ int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, struct kvm_io_bus *new_bus, *bus; bus = kvm->buses[bus_idx]; - - new_bus = kmemdup(bus, sizeof(*bus), GFP_KERNEL); - if (!new_bus) - return -ENOMEM; - r = -ENOENT; - for (i = 0; i < new_bus->dev_count; i++) - if (new_bus->range[i].dev == dev) { + for (i = 0; i < bus->dev_count; i++) + if (bus->range[i].dev == dev) { r = 0; - new_bus->dev_count--; - new_bus->range[i] = new_bus->range[new_bus->dev_count]; - sort(new_bus->range, new_bus->dev_count, - sizeof(struct kvm_io_range), - kvm_io_bus_sort_cmp, NULL); break; } - if (r) { - kfree(new_bus); + if (r) return r; - } + + new_bus = kzalloc(sizeof(*bus) + ((bus->dev_count - 1) * + sizeof(struct kvm_io_range)), GFP_KERNEL); + if (!new_bus) + return -ENOMEM; + + memcpy(new_bus, bus, sizeof(*bus) + i * sizeof(struct kvm_io_range)); + new_bus->dev_count--; + memcpy(new_bus->range + i, bus->range + i + 1, + (new_bus->dev_count - i) * sizeof(struct kvm_io_range)); rcu_assign_pointer(kvm->buses[bus_idx], new_bus); synchronize_srcu_expedited(&kvm->srcu); -- cgit v1.2.3 From 786a9f888bfbe70a36d0592b26037ca1e8c8da7f Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Fri, 9 Mar 2012 12:17:40 +0800 Subject: KVM: set upper bounds for iobus dev to limit userspace kvm_io_bus devices are used for ioevent, pit, pic, ioapic, coalesced_mmio. Currently Qemu only emulates one PCI bus, it contains 32 slots, one slot contains 8 functions, maximum of supported PCI devices: 1 * 32 * 8 = 256. One virtio-blk takes one iobus device, one virtio-net(vhost=on) takes two iobus devices. The maximum of coalesced mmio zone is 100, each zone has an iobus devices. So 300 io_bus devices are not enough. Set an upper bounds for kvm_io_range to limit userspace. 1000 is a very large limit and not bloat the typical user. Signed-off-by: Amos Kong Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- include/linux/kvm_host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index ba9fb4a9762d..3a2cea616283 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -68,7 +68,7 @@ struct kvm_io_range { struct kvm_io_device *dev; }; -#define NR_IOBUS_DEVS 300 +#define NR_IOBUS_DEVS 1000 struct kvm_io_bus { int dev_count; -- cgit v1.2.3 From b6d33834bd4e8bdf4a199812e31b3e36da53c794 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Thu, 8 Mar 2012 16:44:24 -0500 Subject: KVM: Factor out kvm_vcpu_kick to arch-generic code The kvm_vcpu_kick function performs roughly the same funcitonality on most all architectures, so we shouldn't have separate copies. PowerPC keeps a pointer to interchanging waitqueues on the vcpu_arch structure and to accomodate this special need a __KVM_HAVE_ARCH_VCPU_GET_WQ define and accompanying function kvm_arch_vcpu_wq have been defined. For all other architectures this is a generic inline that just returns &vcpu->wq; Acked-by: Scott Wood Signed-off-by: Christoffer Dall Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/ia64/include/asm/kvm_host.h | 1 + arch/ia64/kvm/kvm-ia64.c | 20 +++++--------------- arch/powerpc/include/asm/kvm_host.h | 6 ++++++ arch/powerpc/kvm/powerpc.c | 21 ++++++--------------- arch/s390/kvm/kvm-s390.c | 8 ++++++++ arch/x86/kvm/x86.c | 16 ++-------------- include/linux/kvm_host.h | 9 +++++++++ virt/kvm/kvm_main.c | 22 ++++++++++++++++++++++ 8 files changed, 59 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/include/asm/kvm_host.h b/arch/ia64/include/asm/kvm_host.h index e35b3a84a40b..c4b4bac3d09e 100644 --- a/arch/ia64/include/asm/kvm_host.h +++ b/arch/ia64/include/asm/kvm_host.h @@ -365,6 +365,7 @@ struct thash_cb { }; struct kvm_vcpu_stat { + u32 halt_wakeup; }; struct kvm_vcpu_arch { diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index f5104b7c52cd..9d80ff8d9eff 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1872,21 +1872,6 @@ void kvm_arch_hardware_unsetup(void) { } -void kvm_vcpu_kick(struct kvm_vcpu *vcpu) -{ - int me; - int cpu = vcpu->cpu; - - if (waitqueue_active(&vcpu->wq)) - wake_up_interruptible(&vcpu->wq); - - me = get_cpu(); - if (cpu != me && (unsigned) cpu < nr_cpu_ids && cpu_online(cpu)) - if (!test_and_set_bit(KVM_REQ_KICK, &vcpu->requests)) - smp_send_reschedule(cpu); - put_cpu(); -} - int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq) { return __apic_accept_irq(vcpu, irq->vector); @@ -1956,6 +1941,11 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) (kvm_highest_pending_irq(vcpu) != -1); } +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) +{ + return (!test_and_set_bit(KVM_REQ_KICK, &vcpu->requests)); +} + int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, struct kvm_mp_state *mp_state) { diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 52eb9c1f4fe0..889383735e73 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -498,4 +498,10 @@ struct kvm_vcpu_arch { #define KVM_MMIO_REG_QPR 0x0040 #define KVM_MMIO_REG_FQPR 0x0060 +#define __KVM_HAVE_ARCH_VCPU_GET_WQ 1 +static inline wait_queue_head *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) +{ + return vcpu->arch.wqp; +} + #endif /* __POWERPC_KVM_HOST_H__ */ diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 00d7e345b3fe..b5e9046462fd 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -43,6 +43,11 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) v->requests; } +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) +{ + return 1; +} + int kvmppc_kvm_pv(struct kvm_vcpu *vcpu) { int nr = kvmppc_get_gpr(vcpu, 11); @@ -588,21 +593,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) return r; } -void kvm_vcpu_kick(struct kvm_vcpu *vcpu) -{ - int me; - int cpu = vcpu->cpu; - - me = get_cpu(); - if (waitqueue_active(vcpu->arch.wqp)) { - wake_up_interruptible(vcpu->arch.wqp); - vcpu->stat.halt_wakeup++; - } else if (cpu != me && cpu != -1) { - smp_send_reschedule(vcpu->cpu); - } - put_cpu(); -} - int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) { if (irq->irq == KVM_INTERRUPT_UNSET) { @@ -611,6 +601,7 @@ int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) } kvmppc_core_queue_external(vcpu, irq); + kvm_vcpu_kick(vcpu); return 0; diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 217ce44395a4..d30c8350b949 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -423,6 +423,14 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) return 0; } +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) +{ + /* kvm common code refers to this, but never calls it */ + BUG(); + return 0; +} + + static int kvm_arch_vcpu_ioctl_initial_reset(struct kvm_vcpu *vcpu) { kvm_s390_vcpu_initial_reset(vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4044ce0bf7c1..511031dcb9cc 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6403,21 +6403,9 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) kvm_cpu_has_interrupt(vcpu)); } -void kvm_vcpu_kick(struct kvm_vcpu *vcpu) +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) { - int me; - int cpu = vcpu->cpu; - - if (waitqueue_active(&vcpu->wq)) { - wake_up_interruptible(&vcpu->wq); - ++vcpu->stat.halt_wakeup; - } - - me = get_cpu(); - if (cpu != me && (unsigned)cpu < nr_cpu_ids && cpu_online(cpu)) - if (kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE) - smp_send_reschedule(cpu); - put_cpu(); + return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE; } int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 3a2cea616283..5b624e1ff814 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -439,6 +439,7 @@ void mark_page_dirty_in_slot(struct kvm *kvm, struct kvm_memory_slot *memslot, gfn_t gfn); void kvm_vcpu_block(struct kvm_vcpu *vcpu); +void kvm_vcpu_kick(struct kvm_vcpu *vcpu); void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu); void kvm_resched(struct kvm_vcpu *vcpu); void kvm_load_guest_fpu(struct kvm_vcpu *vcpu); @@ -507,6 +508,7 @@ int kvm_arch_hardware_setup(void); void kvm_arch_hardware_unsetup(void); void kvm_arch_check_processor_compat(void *rtn); int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu); +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu); void kvm_free_physmem(struct kvm *kvm); @@ -522,6 +524,13 @@ static inline void kvm_arch_free_vm(struct kvm *kvm) } #endif +#ifndef __KVM_HAVE_ARCH_VCPU_GET_WQ +static inline wait_queue_head_t *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) +{ + return &vcpu->wq; +} +#endif + int kvm_arch_init_vm(struct kvm *kvm, unsigned long type); void kvm_arch_destroy_vm(struct kvm *kvm); void kvm_free_all_assigned_devices(struct kvm *kvm); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index a9565e240636..7149a2e65524 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1514,6 +1514,28 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu) finish_wait(&vcpu->wq, &wait); } +/* + * Kick a sleeping VCPU, or a guest VCPU in guest mode, into host kernel mode. + */ +void kvm_vcpu_kick(struct kvm_vcpu *vcpu) +{ + int me; + int cpu = vcpu->cpu; + wait_queue_head_t *wqp; + + wqp = kvm_arch_vcpu_wq(vcpu); + if (waitqueue_active(wqp)) { + wake_up_interruptible(wqp); + ++vcpu->stat.halt_wakeup; + } + + me = get_cpu(); + if (cpu != me && (unsigned)cpu < nr_cpu_ids && cpu_online(cpu)) + if (kvm_arch_vcpu_should_kick(vcpu)) + smp_send_reschedule(cpu); + put_cpu(); +} + void kvm_resched(struct kvm_vcpu *vcpu) { if (!need_resched()) -- cgit v1.2.3 From 2246f8b56315befa30f3d3d2800e0734c774f70e Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 13 Mar 2012 22:35:01 +0100 Subject: KVM: PPC: Rework wqp conditional code On PowerPC, we sometimes use a waitqueue per core, not per thread, so we can't always use the vcpu internal waitqueue. This code has been generalized by Christoffer Dall recently, but unfortunately broke compilation for PowerPC. At the time the helper function is defined, struct kvm_vcpu is not declared yet, so we can't dereference it. This patch moves all logic into the generic inline function, at which time we have all information necessary. Signed-off-by: Alexander Graf Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/kvm_host.h | 6 +----- include/linux/kvm_host.h | 6 ++++-- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 889383735e73..20ab5b2dbd0f 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -498,10 +498,6 @@ struct kvm_vcpu_arch { #define KVM_MMIO_REG_QPR 0x0040 #define KVM_MMIO_REG_FQPR 0x0060 -#define __KVM_HAVE_ARCH_VCPU_GET_WQ 1 -static inline wait_queue_head *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) -{ - return vcpu->arch.wqp; -} +#define __KVM_HAVE_ARCH_WQP #endif /* __POWERPC_KVM_HOST_H__ */ diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 5b624e1ff814..5184817e714a 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -524,12 +524,14 @@ static inline void kvm_arch_free_vm(struct kvm *kvm) } #endif -#ifndef __KVM_HAVE_ARCH_VCPU_GET_WQ static inline wait_queue_head_t *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) { +#ifdef __KVM_HAVE_ARCH_WQP + return vcpu->arch.wqp; +#else return &vcpu->wq; -} #endif +} int kvm_arch_init_vm(struct kvm *kvm, unsigned long type); void kvm_arch_destroy_vm(struct kvm *kvm); -- cgit v1.2.3 From 1c0b28c2a46d98cd258d96b8c222144b22876c46 Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Sat, 10 Mar 2012 14:37:27 -0500 Subject: KVM: x86: Add ioctl for KVM_KVMCLOCK_CTRL Now that we have a flag that will tell the guest it was suspended, create an interface for that communication using a KVM ioctl. Signed-off-by: Eric B Munson Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- Documentation/virtual/kvm/api.txt | 20 ++++++++++++++++++++ Documentation/virtual/kvm/msr.txt | 4 ++++ arch/x86/kvm/x86.c | 22 ++++++++++++++++++++++ include/linux/kvm.h | 3 +++ 4 files changed, 49 insertions(+) (limited to 'include/linux') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 6386f8c0482e..81ff39f6248d 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1669,6 +1669,26 @@ at the memory location pointed to by "addr". The list of registers accessible using this interface is identical to the list in 4.64. +4.70 KVM_KVMCLOCK_CTRL + +Capability: KVM_CAP_KVMCLOCK_CTRL +Architectures: Any that implement pvclocks (currently x86 only) +Type: vcpu ioctl +Parameters: None +Returns: 0 on success, -1 on error + +This signals to the host kernel that the specified guest is being paused by +userspace. The host will set a flag in the pvclock structure that is checked +from the soft lockup watchdog. The flag is part of the pvclock structure that +is shared between guest and host, specifically the second bit of the flags +field of the pvclock_vcpu_time_info structure. It will be set exclusively by +the host and read/cleared exclusively by the guest. The guest operation of +checking and clearing the flag must an atomic operation so +load-link/store-conditional, or equivalent must be used. There are two cases +where the guest will clear the flag: when the soft lockup watchdog timer resets +itself or when a soft lockup is detected. This ioctl can be called any time +after pausing the vcpu, but before it is resumed. + 5. The kvm_run structure Application code obtains a pointer to the kvm_run structure by diff --git a/Documentation/virtual/kvm/msr.txt b/Documentation/virtual/kvm/msr.txt index 50317809113d..96b41bd97523 100644 --- a/Documentation/virtual/kvm/msr.txt +++ b/Documentation/virtual/kvm/msr.txt @@ -108,6 +108,10 @@ MSR_KVM_SYSTEM_TIME_NEW: 0x4b564d01 | | time measures taken across 0 | 24 | multiple cpus are guaranteed to | | be monotonic + ------------------------------------------------------------- + | | guest vcpu has been paused by + 1 | N/A | the host + | | See 4.70 in api.txt ------------------------------------------------------------- Availability of this MSR must be checked via bit 3 in 0x4000001 cpuid diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 511031dcb9cc..99b738028fc0 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2147,6 +2147,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_ASYNC_PF: case KVM_CAP_GET_TSC_KHZ: case KVM_CAP_PCI_2_3: + case KVM_CAP_KVMCLOCK_CTRL: r = 1; break; case KVM_CAP_COALESCED_MMIO: @@ -2597,6 +2598,23 @@ static int kvm_vcpu_ioctl_x86_set_xcrs(struct kvm_vcpu *vcpu, return r; } +/* + * kvm_set_guest_paused() indicates to the guest kernel that it has been + * stopped by the hypervisor. This function will be called from the host only. + * EINVAL is returned when the host attempts to set the flag for a guest that + * does not support pv clocks. + */ +static int kvm_set_guest_paused(struct kvm_vcpu *vcpu) +{ + struct pvclock_vcpu_time_info *src = &vcpu->arch.hv_clock; + if (!vcpu->arch.time_page) + return -EINVAL; + src->flags |= PVCLOCK_GUEST_STOPPED; + mark_page_dirty(vcpu->kvm, vcpu->arch.time >> PAGE_SHIFT); + kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu); + return 0; +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -2873,6 +2891,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp, r = vcpu->arch.virtual_tsc_khz; goto out; } + case KVM_KVMCLOCK_CTRL: { + r = kvm_set_guest_paused(vcpu); + goto out; + } default: r = -EINVAL; } diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 6c322a90b92f..7a9dd4b3dede 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -589,6 +589,7 @@ struct kvm_ppc_pvinfo { #define KVM_CAP_S390_UCONTROL 73 #define KVM_CAP_SYNC_REGS 74 #define KVM_CAP_PCI_2_3 75 +#define KVM_CAP_KVMCLOCK_CTRL 76 #ifdef KVM_CAP_IRQ_ROUTING @@ -859,6 +860,8 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_ONE_REG */ #define KVM_GET_ONE_REG _IOW(KVMIO, 0xab, struct kvm_one_reg) #define KVM_SET_ONE_REG _IOW(KVMIO, 0xac, struct kvm_one_reg) +/* VM is being stopped by host */ +#define KVM_KVMCLOCK_CTRL _IO(KVMIO, 0xad) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) -- cgit v1.2.3 From 93474b25af1eabf5b14743793156e8d307bfcd6b Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Thu, 1 Mar 2012 19:34:45 +0900 Subject: KVM: Remove unused dirty_bitmap_head and nr_dirty_pages Now that we do neither double buffering nor heuristic selection of the write protection method these are not needed anymore. Note: some drivers have their own implementation of set_bit_le() and making it generic needs a bit of work; so we use test_and_set_bit_le() and will later replace it with generic set_bit_le(). Signed-off-by: Takuya Yoshikawa Signed-off-by: Avi Kivity --- include/linux/kvm_host.h | 2 -- virt/kvm/kvm_main.c | 14 +++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 5184817e714a..49c2f2fd281f 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -179,8 +179,6 @@ struct kvm_memory_slot { unsigned long flags; unsigned long *rmap; unsigned long *dirty_bitmap; - unsigned long *dirty_bitmap_head; - unsigned long nr_dirty_pages; struct kvm_arch_memory_slot arch; unsigned long userspace_addr; int user_alloc; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index a612bc8c921c..6bd34a6ecca1 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -522,12 +522,11 @@ static void kvm_destroy_dirty_bitmap(struct kvm_memory_slot *memslot) return; if (2 * kvm_dirty_bitmap_bytes(memslot) > PAGE_SIZE) - vfree(memslot->dirty_bitmap_head); + vfree(memslot->dirty_bitmap); else - kfree(memslot->dirty_bitmap_head); + kfree(memslot->dirty_bitmap); memslot->dirty_bitmap = NULL; - memslot->dirty_bitmap_head = NULL; } /* @@ -611,8 +610,7 @@ static int kvm_vm_release(struct inode *inode, struct file *filp) /* * Allocation size is twice as large as the actual dirty bitmap size. - * This makes it possible to do double buffering: see x86's - * kvm_vm_ioctl_get_dirty_log(). + * See x86's kvm_vm_ioctl_get_dirty_log() why this is needed. */ static int kvm_create_dirty_bitmap(struct kvm_memory_slot *memslot) { @@ -627,8 +625,6 @@ static int kvm_create_dirty_bitmap(struct kvm_memory_slot *memslot) if (!memslot->dirty_bitmap) return -ENOMEM; - memslot->dirty_bitmap_head = memslot->dirty_bitmap; - memslot->nr_dirty_pages = 0; #endif /* !CONFIG_S390 */ return 0; } @@ -1476,8 +1472,8 @@ void mark_page_dirty_in_slot(struct kvm *kvm, struct kvm_memory_slot *memslot, if (memslot && memslot->dirty_bitmap) { unsigned long rel_gfn = gfn - memslot->base_gfn; - if (!test_and_set_bit_le(rel_gfn, memslot->dirty_bitmap)) - memslot->nr_dirty_pages++; + /* TODO: introduce set_bit_le() and use it */ + test_and_set_bit_le(rel_gfn, memslot->dirty_bitmap); } } -- cgit v1.2.3 From bbb4ce50f3169b08764f9965fd5b9655646d545a Mon Sep 17 00:00:00 2001 From: "Shimoda, Yoshihiro" Date: Fri, 6 Apr 2012 09:59:14 +0900 Subject: serial: sh-sci: modify sci_break_ctl() SCIF modules which have SCSPTR can output the break signal. Now that we have a way of determining port features/capabilities, add trivial break control via SCSPTR support. Tested on sh7757lcr. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Simon Horman Signed-off-by: Paul Mundt --- drivers/tty/serial/sh-sci.c | 30 ++++++++++++++++++++++++++---- include/linux/serial_sci.h | 2 ++ 2 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 3158e17b665c..3e471fc12991 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1564,10 +1564,32 @@ static void sci_enable_ms(struct uart_port *port) static void sci_break_ctl(struct uart_port *port, int break_state) { - /* - * Not supported by hardware. Most parts couple break and rx - * interrupts together, with break detection always enabled. - */ + struct sci_port *s = to_sci_port(port); + unsigned short scscr, scsptr; + + switch (s->cfg->regtype) { + case SCIx_SH4_SCIF_REGTYPE: + scsptr = serial_port_in(port, SCSPTR); + scscr = serial_port_in(port, SCSCR); + + if (break_state == -1) { + scsptr = (scsptr | SCSPTR_SPB2IO) & ~SCSPTR_SPB2DT; + scscr &= ~SCSCR_TE; + } else { + scsptr = (scsptr | SCSPTR_SPB2DT) & ~SCSPTR_SPB2IO; + scscr |= SCSCR_TE; + } + + serial_port_out(port, SCSPTR, scsptr); + serial_port_out(port, SCSCR, scscr); + break; + default: + /* + * Not supported by hardware. Most parts couple break and rx + * interrupts together, with break detection always enabled. + */ + break; + } } #ifdef CONFIG_SERIAL_SH_SCI_DMA diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 78779074f6e8..eb763adf9815 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -52,6 +52,8 @@ enum { /* SCSPTR, optional */ #define SCSPTR_RTSIO (1 << 7) #define SCSPTR_CTSIO (1 << 5) +#define SCSPTR_SPB2IO (1 << 1) +#define SCSPTR_SPB2DT (1 << 0) /* Offsets into the sci_port->irqs array */ enum { -- cgit v1.2.3 From c172708d38a401b2f3f841dfcd862b469fa0b670 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 4 Apr 2012 00:50:22 +0100 Subject: regulator: core: Use a struct to pass in regulator runtime configuration Rather than adding new arguments to regulator_register() every time we want to add a new bit of dynamic information at runtime change the function to take these via a struct. By doing this we avoid needing to do further changes like the recent addition of device tree support which required each regulator driver to be updated to take an additional parameter. The regulator_desc which should (mostly) be static data is still passed separately as most drivers are able to configure this statically at build time. Signed-off-by: Mark Brown --- drivers/regulator/88pm8607.c | 8 ++++++-- drivers/regulator/aat2870-regulator.c | 8 ++++++-- drivers/regulator/ab3100.c | 11 ++++++----- drivers/regulator/ab8500.c | 8 ++++++-- drivers/regulator/ad5398.c | 8 ++++++-- drivers/regulator/core.c | 19 +++++++++---------- drivers/regulator/da903x.c | 8 ++++++-- drivers/regulator/da9052-regulator.c | 10 +++++++--- drivers/regulator/db8500-prcmu.c | 8 ++++++-- drivers/regulator/dummy.c | 6 ++++-- drivers/regulator/fixed.c | 10 +++++++--- drivers/regulator/gpio-regulator.c | 8 ++++++-- drivers/regulator/isl6271a-regulator.c | 11 +++++++++-- drivers/regulator/lp3971.c | 9 +++++++-- drivers/regulator/lp3972.c | 9 +++++++-- drivers/regulator/max1586.c | 10 +++++++--- drivers/regulator/max8649.c | 8 ++++++-- drivers/regulator/max8660.c | 9 ++++++--- drivers/regulator/max8925-regulator.c | 8 ++++++-- drivers/regulator/max8952.c | 8 ++++++-- drivers/regulator/max8997.c | 8 ++++++-- drivers/regulator/max8998.c | 9 +++++++-- drivers/regulator/pcap-regulator.c | 8 ++++++-- drivers/regulator/pcf50633-regulator.c | 8 ++++++-- drivers/regulator/rc5t583-regulator.c | 8 ++++++-- drivers/regulator/s5m8767.c | 8 ++++++-- drivers/regulator/tps6105x-regulator.c | 9 ++++++--- drivers/regulator/tps62360-regulator.c | 8 ++++++-- drivers/regulator/tps65023-regulator.c | 8 ++++++-- drivers/regulator/tps6507x-regulator.c | 8 ++++++-- drivers/regulator/tps65090-regulator.c | 8 ++++++-- drivers/regulator/tps65217-regulator.c | 8 ++++++-- drivers/regulator/tps6524x-regulator.c | 8 ++++++-- drivers/regulator/tps6586x-regulator.c | 8 ++++++-- drivers/regulator/tps65910-regulator.c | 8 ++++++-- drivers/regulator/tps65912-regulator.c | 9 +++++++-- drivers/regulator/twl-regulator.c | 9 +++++++-- drivers/regulator/wm831x-dcdc.c | 32 ++++++++++++++++++++++++-------- drivers/regulator/wm831x-isink.c | 8 ++++++-- drivers/regulator/wm831x-ldo.c | 24 ++++++++++++++++++------ drivers/regulator/wm8350-regulator.c | 9 ++++++--- drivers/regulator/wm8400-regulator.c | 7 +++++-- drivers/regulator/wm8994-regulator.c | 8 ++++++-- include/linux/regulator/driver.h | 31 ++++++++++++++++++++++++++----- sound/soc/codecs/sgtl5000.c | 8 ++++++-- 45 files changed, 331 insertions(+), 120 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index 11e5ddd7e796..d04fbe953dd8 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -365,6 +365,7 @@ static int __devinit pm8607_regulator_probe(struct platform_device *pdev) struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); struct pm8607_regulator_info *info = NULL; struct regulator_init_data *pdata = pdev->dev.platform_data; + struct regulator_config config = { }; struct resource *res; int i; @@ -390,9 +391,12 @@ static int __devinit pm8607_regulator_probe(struct platform_device *pdev) if ((i == PM8607_ID_BUCK3) && info->chip->buck3_double) info->slope_double = 1; + config.dev = &pdev->dev; + config.init_data = pdata; + config.driver_data = info; + /* replace driver_data with info */ - info->regulator = regulator_register(&info->desc, &pdev->dev, - pdata, info, NULL); + info->regulator = regulator_register(&info->desc, &config); if (IS_ERR(info->regulator)) { dev_err(&pdev->dev, "failed to register regulator %s\n", info->desc.name); diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c index 7cc380e950f6..7f000d6bb984 100644 --- a/drivers/regulator/aat2870-regulator.c +++ b/drivers/regulator/aat2870-regulator.c @@ -178,6 +178,7 @@ static struct aat2870_regulator *aat2870_get_regulator(int id) static int aat2870_regulator_probe(struct platform_device *pdev) { struct aat2870_regulator *ri; + struct regulator_config config = { 0 }; struct regulator_dev *rdev; ri = aat2870_get_regulator(pdev->id); @@ -187,8 +188,11 @@ static int aat2870_regulator_probe(struct platform_device *pdev) } ri->aat2870 = dev_get_drvdata(pdev->dev.parent); - rdev = regulator_register(&ri->desc, &pdev->dev, - pdev->dev.platform_data, ri, NULL); + config.dev = &pdev->dev; + config.driver_data = ri; + config.init_data = pdev->dev.platform_data; + + rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "Failed to register regulator %s\n", ri->desc.name); diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index ed56c9352e6f..ce6192592ca2 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -574,6 +574,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { static int __devinit ab3100_regulators_probe(struct platform_device *pdev) { struct ab3100_platform_data *plfdata = pdev->dev.platform_data; + struct regulator_config config = { }; int err = 0; u8 data; int i; @@ -619,15 +620,15 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev) reg->dev = &pdev->dev; reg->plfdata = plfdata; + config.dev = &pdev->dev; + config.driver_data = reg; + config.init_data = &plfdata->reg_constraints[i]; + /* * Register the regulator, pass around * the ab3100_regulator struct */ - rdev = regulator_register(&ab3100_regulator_desc[i], - &pdev->dev, - &plfdata->reg_constraints[i], - reg, NULL); - + rdev = regulator_register(&ab3100_regulator_desc[i], &config); if (IS_ERR(rdev)) { err = PTR_ERR(rdev); dev_err(&pdev->dev, diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index 0d095b6e567a..93feadaf40c0 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -712,6 +712,7 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev) { struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); struct ab8500_platform_data *pdata; + struct regulator_config config = { }; int i, err; if (!ab8500) { @@ -779,6 +780,10 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev) info = &ab8500_regulator_info[i]; info->dev = &pdev->dev; + config->dev = &pdev->dev; + config->init_data = &pdata->regulator[i]; + config->driver_data = info; + /* fix for hardware before ab8500v2.0 */ if (abx500_get_chip_id(info->dev) < 0x20) { if (info->desc.id == AB8500_LDO_AUX3) { @@ -792,8 +797,7 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev) } /* register regulator with framework */ - info->regulator = regulator_register(&info->desc, &pdev->dev, - &pdata->regulator[i], info, NULL); + info->regulator = regulator_register(&info->desc, &config); if (IS_ERR(info->regulator)) { err = PTR_ERR(info->regulator); dev_err(&pdev->dev, "failed to register regulator %s\n", diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c index 9ba69c431da8..46d05f38baf8 100644 --- a/drivers/regulator/ad5398.c +++ b/drivers/regulator/ad5398.c @@ -212,6 +212,7 @@ static int __devinit ad5398_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct regulator_init_data *init_data = client->dev.platform_data; + struct regulator_config config = { }; struct ad5398_chip_info *chip; const struct ad5398_current_data_format *df = (struct ad5398_current_data_format *)id->driver_data; @@ -224,6 +225,10 @@ static int __devinit ad5398_probe(struct i2c_client *client, if (!chip) return -ENOMEM; + config.dev = &client->dev; + config.init_data = init_data; + config.driver_data = chip; + chip->client = client; chip->min_uA = df->min_uA; @@ -232,8 +237,7 @@ static int __devinit ad5398_probe(struct i2c_client *client, chip->current_offset = df->current_offset; chip->current_mask = (chip->current_level - 1) << chip->current_offset; - chip->rdev = regulator_register(&ad5398_reg, &client->dev, - init_data, chip, NULL); + chip->rdev = regulator_register(&ad5398_reg, &config); if (IS_ERR(chip->rdev)) { ret = PTR_ERR(chip->rdev); dev_err(&client->dev, "failed to register %s %s\n", diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index c4b626789f8e..8b9f8602d47b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2820,27 +2820,24 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) /** * regulator_register - register regulator * @regulator_desc: regulator to register - * @dev: struct device for the regulator - * @init_data: platform provided init data, passed through by driver - * @driver_data: private regulator data - * @of_node: OpenFirmware node to parse for device tree bindings (may be - * NULL). + * @config: runtime configuration for regulator * * Called by regulator drivers to register a regulator. * Returns 0 on success. */ struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc, - struct device *dev, const struct regulator_init_data *init_data, - void *driver_data, struct device_node *of_node) + const struct regulator_config *config) { const struct regulation_constraints *constraints = NULL; + const struct regulator_init_data *init_data; static atomic_t regulator_no = ATOMIC_INIT(0); struct regulator_dev *rdev; + struct device *dev = config->dev; int ret, i; const char *supply = NULL; - if (regulator_desc == NULL) + if (regulator_desc == NULL || config == NULL) return ERR_PTR(-EINVAL); if (regulator_desc->name == NULL || regulator_desc->ops == NULL) @@ -2866,6 +2863,8 @@ regulator_register(const struct regulator_desc *regulator_desc, return ERR_PTR(-EINVAL); } + init_data = config->init_data; + rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); if (rdev == NULL) return ERR_PTR(-ENOMEM); @@ -2873,7 +2872,7 @@ regulator_register(const struct regulator_desc *regulator_desc, mutex_lock(®ulator_list_mutex); mutex_init(&rdev->mutex); - rdev->reg_data = driver_data; + rdev->reg_data = config->driver_data; rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; INIT_LIST_HEAD(&rdev->consumer_list); @@ -2890,7 +2889,7 @@ regulator_register(const struct regulator_desc *regulator_desc, /* register with sysfs */ rdev->dev.class = ®ulator_class; - rdev->dev.of_node = of_node; + rdev->dev.of_node = config->of_node; rdev->dev.parent = dev; dev_set_name(&rdev->dev, "regulator.%d", atomic_inc_return(®ulator_no) - 1); diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index 1851f0929ef0..4630b1ee9966 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -517,6 +517,7 @@ static int __devinit da903x_regulator_probe(struct platform_device *pdev) { struct da903x_regulator_info *ri = NULL; struct regulator_dev *rdev; + struct regulator_config config = { }; ri = find_regulator_info(pdev->id); if (ri == NULL) { @@ -536,8 +537,11 @@ static int __devinit da903x_regulator_probe(struct platform_device *pdev) if (ri->desc.id == DA9030_ID_LDO1 || ri->desc.id == DA9030_ID_LDO15) ri->desc.ops = &da9030_regulator_ldo1_15_ops; - rdev = regulator_register(&ri->desc, &pdev->dev, - pdev->dev.platform_data, ri, NULL); + config.dev = &pdev->dev; + conifg.init_data = pdev->dev.platform_data; + config.driver_data = ri; + + rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", ri->desc.name); diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c index 83e489f76a90..b6c8c4be83c9 100644 --- a/drivers/regulator/da9052-regulator.c +++ b/drivers/regulator/da9052-regulator.c @@ -403,6 +403,7 @@ static inline struct da9052_regulator_info *find_regulator_info(u8 chip_id, static int __devinit da9052_regulator_probe(struct platform_device *pdev) { + struct regulator_config config = { }; struct da9052_regulator *regulator; struct da9052 *da9052; struct da9052_pdata *pdata; @@ -422,10 +423,13 @@ static int __devinit da9052_regulator_probe(struct platform_device *pdev) dev_err(&pdev->dev, "invalid regulator ID specified\n"); return -EINVAL; } + + config.dev = &pdev->dev; + config.init_data = pdata->regulators[pdev->id]; + config.driver_data = regulator; + regulator->rdev = regulator_register(®ulator->info->reg_desc, - &pdev->dev, - pdata->regulators[pdev->id], - regulator, NULL); + &config); if (IS_ERR(regulator->rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", regulator->info->reg_desc.name); diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c index 4bd25e75efa0..87b2e83be11c 100644 --- a/drivers/regulator/db8500-prcmu.c +++ b/drivers/regulator/db8500-prcmu.c @@ -414,6 +414,7 @@ static int __devinit db8500_regulator_probe(struct platform_device *pdev) { struct regulator_init_data *db8500_init_data = dev_get_platdata(&pdev->dev); + struct regulator_config config = { }; int i, err; /* register all regulators */ @@ -425,9 +426,12 @@ static int __devinit db8500_regulator_probe(struct platform_device *pdev) info = &dbx500_regulator_info[i]; info->dev = &pdev->dev; + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = info; + /* register with the regulator framework */ - info->rdev = regulator_register(&info->desc, &pdev->dev, - init_data, info, NULL); + info->rdev = regulator_register(&info->desc, &config); if (IS_ERR(info->rdev)) { err = PTR_ERR(info->rdev); dev_err(&pdev->dev, "failed to register %s: err %i\n", diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c index 0ee00de4be72..1571bee6b1bc 100644 --- a/drivers/regulator/dummy.c +++ b/drivers/regulator/dummy.c @@ -39,10 +39,12 @@ static struct regulator_desc dummy_desc = { static int __devinit dummy_regulator_probe(struct platform_device *pdev) { + struct regulator_config config = { }; int ret; - dummy_regulator_rdev = regulator_register(&dummy_desc, NULL, - &dummy_initdata, NULL, NULL); + config.init_data = &dummy_initdata; + + dummy_regulator_rdev = regulator_register(&dummy_desc, &config); if (IS_ERR(dummy_regulator_rdev)) { ret = PTR_ERR(dummy_regulator_rdev); pr_err("Failed to register regulator: %d\n", ret); diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 9a7d70a9c8d7..b47b005a8d28 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -167,6 +167,7 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) { struct fixed_voltage_config *config; struct fixed_voltage_data *drvdata; + struct regulator_config cfg = { }; int ret; if (pdev->dev.of_node) @@ -247,9 +248,12 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.ops = &fixed_voltage_ops; } - drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev, - config->init_data, drvdata, - pdev->dev.of_node); + cfg.dev = &pdev->dev; + cfg.init_data = config->init_data; + cfg.driver_data = drvdata; + cfg.of_node = pdev->dev.of_node; + + drvdata->dev = regulator_register(&drvdata->desc, &cfg); if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index ad0fc78c3cb4..f93b06b1e7ec 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -172,6 +172,7 @@ static int __devinit gpio_regulator_probe(struct platform_device *pdev) { struct gpio_regulator_config *config = pdev->dev.platform_data; struct gpio_regulator_data *drvdata; + struct regulator_config cfg = { }; int ptr, ret, state; drvdata = devm_kzalloc(&pdev->dev, sizeof(struct gpio_regulator_data), @@ -284,8 +285,11 @@ static int __devinit gpio_regulator_probe(struct platform_device *pdev) } drvdata->state = state; - drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev, - config->init_data, drvdata, NULL); + cfg.dev = &pdev->dev; + cfg.init_data = config->init_data; + cfg.driver_data = &drvdata; + + drvdata->dev = regulator_register(&drvdata->desc, &cfg); if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index eee6f6b85ebc..863f45a18c30 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -140,6 +140,7 @@ static const struct regulator_desc isl_rd[] = { static int __devinit isl6271a_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct regulator_config config = { }; struct regulator_init_data *init_data = i2c->dev.platform_data; struct isl_pmic *pmic; int err, i; @@ -156,8 +157,14 @@ static int __devinit isl6271a_probe(struct i2c_client *i2c, mutex_init(&pmic->mtx); for (i = 0; i < 3; i++) { - pmic->rdev[i] = regulator_register(&isl_rd[i], &i2c->dev, - init_data, pmic, NULL); + config.dev = &i2c->dev; + if (i == 0) + config.init_data = init_data; + else + config.init_data = 0; + config.driver_data = pmic; + + pmic->rdev[i] = regulator_register(&isl_rd[i], &config); if (IS_ERR(pmic->rdev[i])) { dev_err(&i2c->dev, "failed to register %s\n", id->name); err = PTR_ERR(pmic->rdev[i]); diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 499986e00fb2..981bea9cb9d7 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -421,10 +421,15 @@ static int __devinit setup_regulators(struct lp3971 *lp3971, /* Instantiate the regulators */ for (i = 0; i < pdata->num_regulators; i++) { + struct regulator_config config = { }; struct lp3971_regulator_subdev *reg = &pdata->regulators[i]; - lp3971->rdev[i] = regulator_register(®ulators[reg->id], - lp3971->dev, reg->initdata, lp3971, NULL); + config.dev = lp3971->dev; + config.init_data = reg->initdata; + config.driver_data = lp3971; + + lp3971->rdev[i] = regulator_register(®ulators[reg->id], + &config); if (IS_ERR(lp3971->rdev[i])) { err = PTR_ERR(lp3971->rdev[i]); dev_err(lp3971->dev, "regulator init failed: %d\n", diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c index fbe3a58a71f2..de073df7d344 100644 --- a/drivers/regulator/lp3972.c +++ b/drivers/regulator/lp3972.c @@ -527,9 +527,14 @@ static int __devinit setup_regulators(struct lp3972 *lp3972, /* Instantiate the regulators */ for (i = 0; i < pdata->num_regulators; i++) { struct lp3972_regulator_subdev *reg = &pdata->regulators[i]; - lp3972->rdev[i] = regulator_register(®ulators[reg->id], - lp3972->dev, reg->initdata, lp3972, NULL); + struct regulator_config config = { }; + + config.dev = lp3972->dev; + config.init_data = reg->initdata; + config.driver_data = lp3972; + lp3972->rdev[i] = regulator_register(®ulators[reg->id], + &config); if (IS_ERR(lp3972->rdev[i])) { err = PTR_ERR(lp3972->rdev[i]); dev_err(lp3972->dev, "regulator init failed: %d\n", diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index fad0bee10c54..ea832b4ef643 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -185,6 +185,7 @@ static int __devinit max1586_pmic_probe(struct i2c_client *client, { struct regulator_dev **rdev; struct max1586_platform_data *pdata = client->dev.platform_data; + struct regulator_config config = { }; struct max1586_data *max1586; int i, id, ret = -ENOMEM; @@ -212,9 +213,12 @@ static int __devinit max1586_pmic_probe(struct i2c_client *client, dev_err(&client->dev, "invalid regulator id %d\n", id); goto err; } - rdev[i] = regulator_register(&max1586_reg[id], &client->dev, - pdata->subdevs[i].platform_data, - max1586, NULL); + + config.dev = &client->dev; + config.init_data = pdata->subdevs[i].platform_data; + config.driver_data = max1586; + + rdev[i] = regulator_register(&max1586_reg[id], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); dev_err(&client->dev, "failed to register %s\n", diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index dca7835b381c..991f517c8dc8 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -225,6 +225,7 @@ static int __devinit max8649_regulator_probe(struct i2c_client *client, { struct max8649_platform_data *pdata = client->dev.platform_data; struct max8649_regulator_info *info = NULL; + struct regulator_config config = { }; unsigned int val; unsigned char data; int ret; @@ -297,8 +298,11 @@ static int __devinit max8649_regulator_probe(struct i2c_client *client, MAX8649_RAMP_DOWN); } - info->regulator = regulator_register(&dcdc_desc, &client->dev, - pdata->regulator, info, NULL); + config.dev = &client->dev; + config.init_data = pdata->regulator; + config.driver_data = info; + + info->regulator = regulator_register(&dcdc_desc, &config); if (IS_ERR(info->regulator)) { dev_err(info->dev, "failed to register regulator %s\n", dcdc_desc.name); diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index 0e327871fd0e..88f678e4a1a7 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -361,6 +361,7 @@ static int __devinit max8660_probe(struct i2c_client *client, { struct regulator_dev **rdev; struct max8660_platform_data *pdata = client->dev.platform_data; + struct regulator_config config = { }; struct max8660 *max8660; int boot_on, i, id, ret = -EINVAL; @@ -449,9 +450,11 @@ static int __devinit max8660_probe(struct i2c_client *client, id = pdata->subdevs[i].id; - rdev[i] = regulator_register(&max8660_reg[id], &client->dev, - pdata->subdevs[i].platform_data, - max8660, NULL); + config.dev = &client->dev; + config.init_data = pdata->subdevs[i].platform_data; + config.driver_data = max8660; + + rdev[i] = regulator_register(&max8660_reg[id], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); dev_err(&client->dev, "failed to register %s\n", diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index a62f3b5cc312..de30ea2b80f5 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -258,6 +258,7 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); struct max8925_platform_data *pdata = chip->dev->platform_data; + struct regulator_config config = { }; struct max8925_regulator_info *ri; struct regulator_dev *rdev; @@ -269,8 +270,11 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) ri->i2c = chip->i2c; ri->chip = chip; - rdev = regulator_register(&ri->desc, &pdev->dev, - pdata->regulator[pdev->id], ri, NULL); + config.dev = &pdev->dev; + config.init_data = pdata->regulator[pdev->id]; + config.driver_data = ri; + + rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", ri->desc.name); diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index b4084314c222..c0ab4ddc1023 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -173,6 +173,7 @@ static int __devinit max8952_pmic_probe(struct i2c_client *client, { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct max8952_platform_data *pdata = client->dev.platform_data; + struct regulator_config config = { }; struct max8952_data *max8952; int ret = 0, err = 0; @@ -193,8 +194,11 @@ static int __devinit max8952_pmic_probe(struct i2c_client *client, max8952->dev = &client->dev; max8952->pdata = pdata; - max8952->rdev = regulator_register(®ulator, max8952->dev, - &pdata->reg_data, max8952, NULL); + config.dev = max8952->dev; + config.init_data = &pdata->reg_data; + config.driver_data = max8952; + + max8952->rdev = regulator_register(®ulator, &config); if (IS_ERR(max8952->rdev)) { ret = PTR_ERR(max8952->rdev); diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index 6e7beee1c205..48fa966929eb 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -913,6 +913,7 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) { struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_config config = { }; struct regulator_dev **rdev; struct max8997_data *max8997; struct i2c_client *i2c; @@ -1096,8 +1097,11 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) else if (id == MAX8997_CHARGER_CV) regulators[id].n_voltages = 16; - rdev[i] = regulator_register(®ulators[id], max8997->dev, - pdata->regulators[i].initdata, max8997, NULL); + config.dev = max8997->dev; + config.init_data = pdata->regulators[i].initdata; + config.driver_data = max8997; + + rdev[i] = regulator_register(®ulators[id], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); dev_err(max8997->dev, "regulator init failed for %d\n", diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 5890265eeacc..74b0b0c94120 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -685,6 +685,7 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) { struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_config config = { }; struct regulator_dev **rdev; struct max8998_data *max8998; struct i2c_client *i2c; @@ -840,8 +841,12 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) int count = (desc->max - desc->min) / desc->step + 1; regulators[index].n_voltages = count; } - rdev[i] = regulator_register(®ulators[index], max8998->dev, - pdata->regulators[i].initdata, max8998, NULL); + + config.dev = max8998->dev; + config.init_data = pdata->regulators[i].initdata; + config.driver_data = max8998; + + rdev[i] = regulator_register(®ulators[index], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); dev_err(max8998->dev, "regulator init failed\n"); diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c index b55128db07cc..8211101121f0 100644 --- a/drivers/regulator/pcap-regulator.c +++ b/drivers/regulator/pcap-regulator.c @@ -251,9 +251,13 @@ static int __devinit pcap_regulator_probe(struct platform_device *pdev) { struct regulator_dev *rdev; void *pcap = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; - rdev = regulator_register(&pcap_regulators[pdev->id], &pdev->dev, - pdev->dev.platform_data, pcap, NULL); + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = pcap; + + rdev = regulator_register(&pcap_regulators[pdev->id], &config); if (IS_ERR(rdev)) return PTR_ERR(rdev); diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index 43163f14bec7..7ee70f1b3f24 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -296,12 +296,16 @@ static int __devinit pcf50633_regulator_probe(struct platform_device *pdev) { struct regulator_dev *rdev; struct pcf50633 *pcf; + struct regulator_config config = { }; /* Already set by core driver */ pcf = dev_to_pcf50633(pdev->dev.parent); - rdev = regulator_register(®ulators[pdev->id], &pdev->dev, - pdev->dev.platform_data, pcf, NULL); + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = pcf; + + rdev = regulator_register(®ulators[pdev->id], &config); if (IS_ERR(rdev)) return PTR_ERR(rdev); diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c index fe094a6140d9..b567c9ec47c1 100644 --- a/drivers/regulator/rc5t583-regulator.c +++ b/drivers/regulator/rc5t583-regulator.c @@ -251,6 +251,7 @@ static int __devinit rc5t583_regulator_probe(struct platform_device *pdev) struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); struct regulator_init_data *reg_data; + struct regulator_config config = { }; struct rc5t583_regulator *reg = NULL; struct rc5t583_regulator *regs; struct regulator_dev *rdev; @@ -300,8 +301,11 @@ static int __devinit rc5t583_regulator_probe(struct platform_device *pdev) "Failed to configure ext control %d\n", id); skip_ext_pwr_config: - rdev = regulator_register(&ri->desc, &pdev->dev, - reg_data, reg, NULL); + config.dev = &pdev->dev; + config.init_data = reg_data; + config.driver_data = reg; + + rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "Failed to register regulator %s\n", ri->desc.name); diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index a2afc0edc5a4..10c38f9ae787 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -579,6 +579,7 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) { struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct s5m_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_config config = { }; struct regulator_dev **rdev; struct s5m8767_info *s5m8767; int i, ret, size; @@ -774,8 +775,11 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) regulators[id].n_voltages = (desc->max - desc->min) / desc->step + 1; - rdev[i] = regulator_register(®ulators[id], s5m8767->dev, - pdata->regulators[i].initdata, s5m8767, NULL); + config.dev = s5m8767->dev; + config.init_data = pdata->regulators[i].initdata; + config.driver_data = s5m8767; + + rdev[i] = regulator_register(®ulators[id], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); dev_err(s5m8767->dev, "regulator init failed for %d\n", diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c index 3b788977cb72..d840d8440a91 100644 --- a/drivers/regulator/tps6105x-regulator.c +++ b/drivers/regulator/tps6105x-regulator.c @@ -139,6 +139,7 @@ static int __devinit tps6105x_regulator_probe(struct platform_device *pdev) { struct tps6105x *tps6105x = dev_get_platdata(&pdev->dev); struct tps6105x_platform_data *pdata = tps6105x->pdata; + struct regulator_config config = { }; int ret; /* This instance is not set for regulator mode so bail out */ @@ -148,11 +149,13 @@ static int __devinit tps6105x_regulator_probe(struct platform_device *pdev) return 0; } + config.dev = &tps6105x->client->dev; + config.init_data = pdata->regulator_data; + config.driver_data = tps6105x; + /* Register regulator with framework */ tps6105x->regulator = regulator_register(&tps6105x_regulator_desc, - &tps6105x->client->dev, - pdata->regulator_data, tps6105x, - NULL); + &config); if (IS_ERR(tps6105x->regulator)) { ret = PTR_ERR(tps6105x->regulator); dev_err(&tps6105x->client->dev, diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index aa57632c0150..8fffc6e45b3a 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -270,6 +270,7 @@ static const struct regmap_config tps62360_regmap_config = { static int __devinit tps62360_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct regulator_config config = { }; struct tps62360_regulator_platform_data *pdata; struct regulator_dev *rdev; struct tps62360_chip *tps; @@ -384,9 +385,12 @@ static int __devinit tps62360_probe(struct i2c_client *client, goto err_init; } + config.dev = &client->dev; + config.init_data = &pdata->reg_init_data; + config.driver_data = tps; + /* Register the regulators */ - rdev = regulator_register(&tps->desc, &client->dev, - &pdata->reg_init_data, tps, NULL); + rdev = regulator_register(&tps->desc, &config); if (IS_ERR(rdev)) { dev_err(tps->dev, "%s() Err: Failed to register %s\n", __func__, id->name); diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 5c9a9001f816..7755afeecede 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -376,6 +376,7 @@ static int __devinit tps_65023_probe(struct i2c_client *client, { const struct tps_driver_data *drv_data = (void *)id->driver_data; const struct tps_info *info = drv_data->info; + struct regulator_config config = { }; struct regulator_init_data *init_data; struct regulator_dev *rdev; struct tps_pmic *tps; @@ -420,9 +421,12 @@ static int __devinit tps_65023_probe(struct i2c_client *client, tps->desc[i].type = REGULATOR_VOLTAGE; tps->desc[i].owner = THIS_MODULE; + config.dev = &client->dev; + config.init_data = init_data; + config.driver_data = tps; + /* Register the regulators */ - rdev = regulator_register(&tps->desc[i], &client->dev, - init_data, tps, NULL); + rdev = regulator_register(&tps->desc[i], &config); if (IS_ERR(rdev)) { dev_err(&client->dev, "failed to register %s\n", id->name); diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 832833fe8aad..16d27fc2c7f7 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -404,6 +404,7 @@ static __devinit int tps6507x_pmic_probe(struct platform_device *pdev) { struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); struct tps_info *info = &tps6507x_pmic_regs[0]; + struct regulator_config config = { }; struct regulator_init_data *init_data; struct regulator_dev *rdev; struct tps6507x_pmic *tps; @@ -453,8 +454,11 @@ static __devinit int tps6507x_pmic_probe(struct platform_device *pdev) tps->desc[i].type = REGULATOR_VOLTAGE; tps->desc[i].owner = THIS_MODULE; - rdev = regulator_register(&tps->desc[i], - tps6507x_dev->dev, init_data, tps, NULL); + config.dev = tps6507x_dev->dev; + config.init_data = init_data; + config.driver_data = tps; + + rdev = regulator_register(&tps->desc[i], &config); if (IS_ERR(rdev)) { dev_err(tps6507x_dev->dev, "failed to register %s regulator\n", diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 7baff2e8765d..6bbf760be80a 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -137,6 +137,7 @@ static inline struct tps65090_regulator *find_regulator_info(int id) static int __devinit tps65090_regulator_probe(struct platform_device *pdev) { struct tps65090_regulator *ri = NULL; + struct regulator_config config = { }; struct regulator_dev *rdev; struct tps65090_regulator_platform_data *tps_pdata; int id = pdev->id; @@ -151,8 +152,11 @@ static int __devinit tps65090_regulator_probe(struct platform_device *pdev) tps_pdata = pdev->dev.platform_data; ri->dev = &pdev->dev; - rdev = regulator_register(&ri->desc, &pdev->dev, - &tps_pdata->regulator, ri, NULL); + config.dev = &pdev->dev; + config.init_data = &tps_pdata->regulator; + config.driver_data = ri; + + rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", ri->desc.name); diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 80fad2d3479e..00c5c1c96d19 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -327,13 +327,17 @@ static int __devinit tps65217_regulator_probe(struct platform_device *pdev) struct regulator_dev *rdev; struct tps65217 *tps; struct tps_info *info = &tps65217_pmic_regs[pdev->id]; + struct regulator_config config = { }; /* Already set by core driver */ tps = dev_to_tps65217(pdev->dev.parent); tps->info[pdev->id] = info; - rdev = regulator_register(®ulators[pdev->id], &pdev->dev, - pdev->dev.platform_data, tps, NULL); + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = tps; + + rdev = regulator_register(®ulators[pdev->id], &config); if (IS_ERR(rdev)) return PTR_ERR(rdev); diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c index eabf0e601f65..6616af7d2956 100644 --- a/drivers/regulator/tps6524x-regulator.c +++ b/drivers/regulator/tps6524x-regulator.c @@ -605,6 +605,7 @@ static int __devinit pmic_probe(struct spi_device *spi) struct device *dev = &spi->dev; const struct supply_info *info = supply_info; struct regulator_init_data *init_data; + struct regulator_config config = { }; int ret = 0, i; init_data = dev->platform_data; @@ -636,8 +637,11 @@ static int __devinit pmic_probe(struct spi_device *spi) if (info->flags & FIXED_VOLTAGE) hw->desc[i].n_voltages = 1; - hw->rdev[i] = regulator_register(&hw->desc[i], dev, - init_data, hw, NULL); + config.dev = dev; + config.init_data = init_data; + config.driver_data = hw; + + hw->rdev[i] = regulator_register(&hw->desc[i], &config); if (IS_ERR(hw->rdev[i])) { ret = PTR_ERR(hw->rdev[i]); hw->rdev[i] = NULL; diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 2dd66fe9570f..deb855c41e16 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -340,6 +340,7 @@ static inline struct tps6586x_regulator *find_regulator_info(int id) static int __devinit tps6586x_regulator_probe(struct platform_device *pdev) { struct tps6586x_regulator *ri = NULL; + struct regulator_config config = { }; struct regulator_dev *rdev; int id = pdev->id; int err; @@ -356,8 +357,11 @@ static int __devinit tps6586x_regulator_probe(struct platform_device *pdev) if (err) return err; - rdev = regulator_register(&ri->desc, &pdev->dev, - pdev->dev.platform_data, ri, NULL); + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = ri; + + rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", ri->desc.name); diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 4a37c2b6367f..e7a4ece10628 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -1097,6 +1097,7 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic, static __devinit int tps65910_probe(struct platform_device *pdev) { struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; struct tps_info *info; struct regulator_init_data *reg_data; struct regulator_dev *rdev; @@ -1206,8 +1207,11 @@ static __devinit int tps65910_probe(struct platform_device *pdev) pmic->desc[i].type = REGULATOR_VOLTAGE; pmic->desc[i].owner = THIS_MODULE; - rdev = regulator_register(&pmic->desc[i], - tps65910->dev, reg_data, pmic, NULL); + config.dev = tps65910->dev; + config.init_data = reg_data; + config.driver_data = pmic; + + rdev = regulator_register(&pmic->desc[i], &config); if (IS_ERR(rdev)) { dev_err(tps65910->dev, "failed to register %s regulator\n", diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index 05ea096cf8a7..8c9c61383fee 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -463,6 +463,7 @@ static struct regulator_ops tps65912_ops_ldo = { static __devinit int tps65912_probe(struct platform_device *pdev) { struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; struct tps_info *info; struct regulator_init_data *reg_data; struct regulator_dev *rdev; @@ -500,8 +501,12 @@ static __devinit int tps65912_probe(struct platform_device *pdev) pmic->desc[i].type = REGULATOR_VOLTAGE; pmic->desc[i].owner = THIS_MODULE; range = tps65912_get_range(pmic, i); - rdev = regulator_register(&pmic->desc[i], - tps65912->dev, reg_data, pmic, NULL); + + config.dev = tps65912->dev; + config.init_data = reg_data; + config.driver_data = pmic; + + rdev = regulator_register(&pmic->desc[i], &config); if (IS_ERR(rdev)) { dev_err(tps65912->dev, "failed to register %s regulator\n", diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 107a08bc50d9..9cf6f59d27bc 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -1175,6 +1175,7 @@ static int __devinit twlreg_probe(struct platform_device *pdev) struct regulator_dev *rdev; struct twl_regulator_driver_data *drvdata; const struct of_device_id *match; + struct regulator_config config = { }; match = of_match_device(twl_of_match, &pdev->dev); if (match) { @@ -1254,8 +1255,12 @@ static int __devinit twlreg_probe(struct platform_device *pdev) break; } - rdev = regulator_register(&info->desc, &pdev->dev, initdata, info, - pdev->dev.of_node); + config.dev = &pdev->dev; + config.init_data = initdata; + config.driver_data = info; + config.of_node = pdev->dev.of_node; + + rdev = regulator_register(&info->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "can't register %s, %ld\n", info->desc.name, PTR_ERR(rdev)); diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 909c53b70375..c754eae18c4a 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -495,6 +495,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; int id; struct wm831x_dcdc *dcdc; struct resource *res; @@ -553,8 +554,11 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) if (pdata->dcdc[id]) wm831x_buckv_dvs_init(dcdc, pdata->dcdc[id]->driver_data); - dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev, - pdata->dcdc[id], dcdc, NULL); + config.dev = pdev->dev.parent; + config.init_data = pdata->dcdc[id]; + config.driver_data = dcdc; + + dcdc->regulator = regulator_register(&dcdc->desc, &config); if (IS_ERR(dcdc->regulator)) { ret = PTR_ERR(dcdc->regulator); dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", @@ -705,6 +709,7 @@ static __devinit int wm831x_buckp_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; int id; struct wm831x_dcdc *dcdc; struct resource *res; @@ -746,8 +751,11 @@ static __devinit int wm831x_buckp_probe(struct platform_device *pdev) dcdc->desc.ops = &wm831x_buckp_ops; dcdc->desc.owner = THIS_MODULE; - dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev, - pdata->dcdc[id], dcdc, NULL); + config.dev = pdev->dev.parent; + config.init_data = pdata->dcdc[id]; + config.driver_data = dcdc; + + dcdc->regulator = regulator_register(&dcdc->desc, &config); if (IS_ERR(dcdc->regulator)) { ret = PTR_ERR(dcdc->regulator); dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", @@ -838,6 +846,7 @@ static __devinit int wm831x_boostp_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; int id = pdev->id % ARRAY_SIZE(pdata->dcdc); struct wm831x_dcdc *dcdc; struct resource *res; @@ -871,8 +880,11 @@ static __devinit int wm831x_boostp_probe(struct platform_device *pdev) dcdc->desc.ops = &wm831x_boostp_ops; dcdc->desc.owner = THIS_MODULE; - dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev, - pdata->dcdc[id], dcdc, NULL); + config.dev = pdev->dev.parent; + config.init_data = pdata->dcdc[id]; + config.driver_data = dcdc; + + dcdc->regulator = regulator_register(&dcdc->desc, &config); if (IS_ERR(dcdc->regulator)) { ret = PTR_ERR(dcdc->regulator); dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n", @@ -941,6 +953,7 @@ static __devinit int wm831x_epe_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; int id = pdev->id % ARRAY_SIZE(pdata->epe); struct wm831x_dcdc *dcdc; int ret; @@ -968,8 +981,11 @@ static __devinit int wm831x_epe_probe(struct platform_device *pdev) dcdc->desc.type = REGULATOR_VOLTAGE; dcdc->desc.owner = THIS_MODULE; - dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev, - pdata->epe[id], dcdc, NULL); + config.dev = pdev->dev.parent; + config.init_data = pdata->epe[id]; + config.driver_data = dcdc; + + dcdc->regulator = regulator_register(&dcdc->desc, &config); if (IS_ERR(dcdc->regulator)) { ret = PTR_ERR(dcdc->regulator); dev_err(wm831x->dev, "Failed to register EPE%d: %d\n", diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 634aac3f2d5f..046fabf648d2 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -154,6 +154,7 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev) struct wm831x_pdata *pdata = wm831x->dev->platform_data; struct wm831x_isink *isink; int id = pdev->id % ARRAY_SIZE(pdata->isink); + struct regulator_config config = { }; struct resource *res; int ret, irq; @@ -189,8 +190,11 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev) isink->desc.type = REGULATOR_CURRENT; isink->desc.owner = THIS_MODULE; - isink->regulator = regulator_register(&isink->desc, &pdev->dev, - pdata->isink[id], isink, NULL); + config.dev = pdev->dev.parent; + config.init_data = pdata->isink[id]; + config.driver_data = isink; + + isink->regulator = regulator_register(&isink->desc, &config); if (IS_ERR(isink->regulator)) { ret = PTR_ERR(isink->regulator); dev_err(wm831x->dev, "Failed to register ISINK%d: %d\n", diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index a4b16831f4ca..eb6a3061884c 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -310,6 +310,7 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; int id; struct wm831x_ldo *ldo; struct resource *res; @@ -350,8 +351,11 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev) ldo->desc.ops = &wm831x_gp_ldo_ops; ldo->desc.owner = THIS_MODULE; - ldo->regulator = regulator_register(&ldo->desc, &pdev->dev, - pdata->ldo[id], ldo, NULL); + config.dev = pdev->dev.parent; + config.init_data = pdata->ldo[id]; + config.driver_data = ldo; + + ldo->regulator = regulator_register(&ldo->desc, &config); if (IS_ERR(ldo->regulator)) { ret = PTR_ERR(ldo->regulator); dev_err(wm831x->dev, "Failed to register LDO%d: %d\n", @@ -578,6 +582,7 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; int id; struct wm831x_ldo *ldo; struct resource *res; @@ -618,8 +623,11 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev) ldo->desc.ops = &wm831x_aldo_ops; ldo->desc.owner = THIS_MODULE; - ldo->regulator = regulator_register(&ldo->desc, &pdev->dev, - pdata->ldo[id], ldo, NULL); + config.dev = pdev->dev.parent; + config.init_data = pdata->ldo[id]; + config.driver_data = ldo; + + ldo->regulator = regulator_register(&ldo->desc, &config); if (IS_ERR(ldo->regulator)) { ret = PTR_ERR(ldo->regulator); dev_err(wm831x->dev, "Failed to register LDO%d: %d\n", @@ -772,6 +780,7 @@ static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; + struct regulator_config config = { }; int id; struct wm831x_ldo *ldo; struct resource *res; @@ -813,8 +822,11 @@ static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev) ldo->desc.ops = &wm831x_alive_ldo_ops; ldo->desc.owner = THIS_MODULE; - ldo->regulator = regulator_register(&ldo->desc, &pdev->dev, - pdata->ldo[id], ldo, NULL); + config.dev = pdev->dev.parent; + config.init_data = pdata->ldo[id]; + config.driver_data = ldo; + + ldo->regulator = regulator_register(&ldo->desc, &config); if (IS_ERR(ldo->regulator)) { ret = PTR_ERR(ldo->regulator); dev_err(wm831x->dev, "Failed to register LDO%d: %d\n", diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 552b1edf8091..4dcbab1314a5 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1398,6 +1398,7 @@ static irqreturn_t pmic_uv_handler(int irq, void *data) static int wm8350_regulator_probe(struct platform_device *pdev) { struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev); + struct regulator_config config = { }; struct regulator_dev *rdev; int ret; u16 val; @@ -1425,10 +1426,12 @@ static int wm8350_regulator_probe(struct platform_device *pdev) break; } + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = dev_get_drvdata(&pdev->dev); + /* register regulator */ - rdev = regulator_register(&wm8350_reg[pdev->id], &pdev->dev, - pdev->dev.platform_data, - dev_get_drvdata(&pdev->dev), NULL); + rdev = regulator_register(&wm8350_reg[pdev->id], &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register %s\n", wm8350_reg[pdev->id].name); diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 8477153780b6..4408b7802e75 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -323,11 +323,14 @@ static struct regulator_desc regulators[] = { static int __devinit wm8400_regulator_probe(struct platform_device *pdev) { struct wm8400 *wm8400 = container_of(pdev, struct wm8400, regulators[pdev->id]); + struct regulator_config config = { }; struct regulator_dev *rdev; - rdev = regulator_register(®ulators[pdev->id], &pdev->dev, - pdev->dev.platform_data, wm8400, NULL); + config.dev = &pdev->dev; + config.init_data = pdev->dev.platform_data; + config.driver_data = wm8400; + rdev = regulator_register(®ulators[pdev->id], &config); if (IS_ERR(rdev)) return PTR_ERR(rdev); diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 8a4897a35f28..f4a62941b711 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -233,6 +233,7 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev) struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent); struct wm8994_pdata *pdata = wm8994->dev->platform_data; int id = pdev->id % ARRAY_SIZE(pdata->ldo); + struct regulator_config config = { }; struct wm8994_ldo *ldo; int ret; @@ -268,8 +269,11 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev) } else ldo->is_enabled = true; - ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &pdev->dev, - pdata->ldo[id].init_data, ldo, NULL); + config.dev = &pdev->dev; + config.init_data = pdata->ldo[id].init_data; + config.driver_data = ldo; + + ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &config); if (IS_ERR(ldo->regulator)) { ret = PTR_ERR(ldo->regulator); dev_err(wm8994->dev, "Failed to register LDO%d: %d\n", diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 1dcdf00e0db2..4f529ed48d4c 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -148,10 +148,12 @@ enum regulator_type { }; /** - * struct regulator_desc - Regulator descriptor + * struct regulator_desc - Static regulator descriptor * - * Each regulator registered with the core is described with a structure of - * this type. + * Each regulator registered with the core is described with a + * structure of this type and a struct regulator_config. This + * structure contains the non-varying parts of the regulator + * description. * * @name: Identifying name for the regulator. * @supply_name: Identifying the regulator supply @@ -173,6 +175,26 @@ struct regulator_desc { struct module *owner; }; +/** + * struct regulator_config - Dynamic regulator descriptor + * + * Each regulator registered with the core is described with a + * structure of this type and a struct regulator_desc. This structure + * contains the runtime variable parts of the regulator description. + * + * @dev: struct device for the regulator + * @init_data: platform provided init data, passed through by driver + * @driver_data: private regulator data + * @of_node: OpenFirmware node to parse for device tree bindings (may be + * NULL). + */ +struct regulator_config { + struct device *dev; + const struct regulator_init_data *init_data; + void *driver_data; + struct device_node *of_node; +}; + /* * struct regulator_dev * @@ -212,8 +234,7 @@ struct regulator_dev { struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc, - struct device *dev, const struct regulator_init_data *init_data, - void *driver_data, struct device_node *of_node); + const struct regulator_config *config); void regulator_unregister(struct regulator_dev *rdev); int regulator_notifier_call_chain(struct regulator_dev *rdev, diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index d1926266fe00..a554b0c8ad38 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -808,6 +808,7 @@ static int ldo_regulator_register(struct snd_soc_codec *codec, { struct ldo_regulator *ldo; struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct regulator_config config = { }; ldo = kzalloc(sizeof(struct ldo_regulator), GFP_KERNEL); @@ -831,8 +832,11 @@ static int ldo_regulator_register(struct snd_soc_codec *codec, ldo->codec_data = codec; ldo->voltage = voltage; - ldo->dev = regulator_register(&ldo->desc, codec->dev, - init_data, ldo, NULL); + config.dev = codec->dev; + config.driver_data = ldo; + config.init_data = init_data; + + ldo->dev = regulator_register(&ldo->desc, &config); if (IS_ERR(ldo->dev)) { int ret = PTR_ERR(ldo->dev); -- cgit v1.2.3 From 83d498569e9a7a4b92c4c5d3566f2d6a604f28c9 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 13:45:40 -0400 Subject: SELinux: rename dentry_open to file_open dentry_open takes a file, rename it to file_open Signed-off-by: Eric Paris --- fs/open.c | 2 +- include/linux/security.h | 13 +++++-------- security/apparmor/lsm.c | 4 ++-- security/capability.c | 4 ++-- security/security.c | 4 ++-- security/selinux/hooks.c | 6 +++--- security/smack/smack_lsm.c | 6 +++--- security/tomoyo/tomoyo.c | 6 +++--- 8 files changed, 21 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/fs/open.c b/fs/open.c index 5720854156db..5eccdcea2d1b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -681,7 +681,7 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, f->f_op = fops_get(inode->i_fop); - error = security_dentry_open(f, cred); + error = security_file_open(f, cred); if (error) goto cleanup_all; diff --git a/include/linux/security.h b/include/linux/security.h index 673afbb8238a..de412ea29aac 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -639,10 +639,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * to receive an open file descriptor via socket IPC. * @file contains the file structure being received. * Return 0 if permission is granted. - * - * Security hook for dentry - * - * @dentry_open + * @file_open * Save open-time permission checking state for later use upon * file_permission, and recheck access if anything has changed * since inode_permission. @@ -1497,7 +1494,7 @@ struct security_operations { int (*file_send_sigiotask) (struct task_struct *tsk, struct fown_struct *fown, int sig); int (*file_receive) (struct file *file); - int (*dentry_open) (struct file *file, const struct cred *cred); + int (*file_open) (struct file *file, const struct cred *cred); int (*task_create) (unsigned long clone_flags); void (*task_free) (struct task_struct *task); @@ -1756,7 +1753,7 @@ int security_file_set_fowner(struct file *file); int security_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int sig); int security_file_receive(struct file *file); -int security_dentry_open(struct file *file, const struct cred *cred); +int security_file_open(struct file *file, const struct cred *cred); int security_task_create(unsigned long clone_flags); void security_task_free(struct task_struct *task); int security_cred_alloc_blank(struct cred *cred, gfp_t gfp); @@ -2227,8 +2224,8 @@ static inline int security_file_receive(struct file *file) return 0; } -static inline int security_dentry_open(struct file *file, - const struct cred *cred) +static inline int security_file_open(struct file *file, + const struct cred *cred) { return 0; } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index ad05d391974d..02fddcd4c647 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -373,7 +373,7 @@ static int apparmor_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) AA_MAY_META_READ); } -static int apparmor_dentry_open(struct file *file, const struct cred *cred) +static int apparmor_file_open(struct file *file, const struct cred *cred) { struct aa_file_cxt *fcxt = file->f_security; struct aa_profile *profile; @@ -640,9 +640,9 @@ static struct security_operations apparmor_ops = { .path_chmod = apparmor_path_chmod, .path_chown = apparmor_path_chown, .path_truncate = apparmor_path_truncate, - .dentry_open = apparmor_dentry_open, .inode_getattr = apparmor_inode_getattr, + .file_open = apparmor_file_open, .file_permission = apparmor_file_permission, .file_alloc_security = apparmor_file_alloc_security, .file_free_security = apparmor_file_free_security, diff --git a/security/capability.c b/security/capability.c index 5bb21b1c448c..fca889676c5e 100644 --- a/security/capability.c +++ b/security/capability.c @@ -348,7 +348,7 @@ static int cap_file_receive(struct file *file) return 0; } -static int cap_dentry_open(struct file *file, const struct cred *cred) +static int cap_file_open(struct file *file, const struct cred *cred) { return 0; } @@ -956,7 +956,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, file_set_fowner); set_to_cap_if_null(ops, file_send_sigiotask); set_to_cap_if_null(ops, file_receive); - set_to_cap_if_null(ops, dentry_open); + set_to_cap_if_null(ops, file_open); set_to_cap_if_null(ops, task_create); set_to_cap_if_null(ops, task_free); set_to_cap_if_null(ops, cred_alloc_blank); diff --git a/security/security.c b/security/security.c index bf619ffc9a4d..5497a57fba01 100644 --- a/security/security.c +++ b/security/security.c @@ -701,11 +701,11 @@ int security_file_receive(struct file *file) return security_ops->file_receive(file); } -int security_dentry_open(struct file *file, const struct cred *cred) +int security_file_open(struct file *file, const struct cred *cred) { int ret; - ret = security_ops->dentry_open(file, cred); + ret = security_ops->file_open(file, cred); if (ret) return ret; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f7d7e779c7f3..dc15f16a357c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2973,7 +2973,7 @@ static int selinux_file_permission(struct file *file, int mask) if (sid == fsec->sid && fsec->isid == isec->sid && fsec->pseqno == avc_policy_seqno()) - /* No change since dentry_open check. */ + /* No change since file_open check. */ return 0; return selinux_revalidate_file_permission(file, mask); @@ -3232,7 +3232,7 @@ static int selinux_file_receive(struct file *file) return file_has_perm(cred, file, file_to_av(file)); } -static int selinux_dentry_open(struct file *file, const struct cred *cred) +static int selinux_file_open(struct file *file, const struct cred *cred) { struct file_security_struct *fsec; struct inode *inode; @@ -5596,7 +5596,7 @@ static struct security_operations selinux_ops = { .file_send_sigiotask = selinux_file_send_sigiotask, .file_receive = selinux_file_receive, - .dentry_open = selinux_dentry_open, + .file_open = selinux_file_open, .task_create = selinux_task_create, .cred_alloc_blank = selinux_cred_alloc_blank, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 81c03a597112..8ef0199ebca1 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1349,7 +1349,7 @@ static int smack_file_receive(struct file *file) } /** - * smack_dentry_open - Smack dentry open processing + * smack_file_open - Smack dentry open processing * @file: the object * @cred: unused * @@ -1357,7 +1357,7 @@ static int smack_file_receive(struct file *file) * * Returns 0 */ -static int smack_dentry_open(struct file *file, const struct cred *cred) +static int smack_file_open(struct file *file, const struct cred *cred) { struct inode_smack *isp = file->f_path.dentry->d_inode->i_security; @@ -3538,7 +3538,7 @@ struct security_operations smack_ops = { .file_send_sigiotask = smack_file_send_sigiotask, .file_receive = smack_file_receive, - .dentry_open = smack_dentry_open, + .file_open = smack_file_open, .cred_alloc_blank = smack_cred_alloc_blank, .cred_free = smack_cred_free, diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 620d37c159a3..c2d04a50f76a 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -319,14 +319,14 @@ static int tomoyo_file_fcntl(struct file *file, unsigned int cmd, } /** - * tomoyo_dentry_open - Target for security_dentry_open(). + * tomoyo_file_open - Target for security_file_open(). * * @f: Pointer to "struct file". * @cred: Pointer to "struct cred". * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_dentry_open(struct file *f, const struct cred *cred) +static int tomoyo_file_open(struct file *f, const struct cred *cred) { int flags = f->f_flags; /* Don't check read permission here if called from do_execve(). */ @@ -510,7 +510,7 @@ static struct security_operations tomoyo_security_ops = { .bprm_set_creds = tomoyo_bprm_set_creds, .bprm_check_security = tomoyo_bprm_check_security, .file_fcntl = tomoyo_file_fcntl, - .dentry_open = tomoyo_dentry_open, + .file_open = tomoyo_file_open, .path_truncate = tomoyo_path_truncate, .path_unlink = tomoyo_path_unlink, .path_mkdir = tomoyo_path_mkdir, -- cgit v1.2.3 From bd5e50f9c1c71daac273fa586424f07205f6b13b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:42 -0400 Subject: LSM: remove the COMMON_AUDIT_DATA_INIT type expansion Just open code it so grep on the source code works better. Signed-off-by: Eric Paris --- include/linux/lsm_audit.h | 2 +- security/apparmor/capability.c | 2 +- security/apparmor/file.c | 2 +- security/apparmor/ipc.c | 2 +- security/apparmor/lib.c | 2 +- security/apparmor/lsm.c | 2 +- security/apparmor/policy.c | 2 +- security/apparmor/policy_unpack.c | 2 +- security/apparmor/resource.c | 2 +- security/selinux/avc.c | 2 +- security/selinux/hooks.c | 68 +++++++++++++++++++-------------------- 11 files changed, 44 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index fad48aab893b..9e1ebf5851b8 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -96,7 +96,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb, /* Initialize an LSM audit data structure. */ #define COMMON_AUDIT_DATA_INIT(_d, _t) \ { memset((_d), 0, sizeof(struct common_audit_data)); \ - (_d)->type = LSM_AUDIT_DATA_##_t; } + (_d)->type = _t; } void common_lsm_audit(struct common_audit_data *a, void (*pre_audit)(struct audit_buffer *, void *), diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 088dba3bf7dc..3ecb8b7d8502 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -65,7 +65,7 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, CAP); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_CAP); sa.aad = &aad; sa.tsk = task; sa.u.cap = cap; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 2f8fcba9ce4b..6ab264ca85cc 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -108,7 +108,7 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.op = op, aad.fs.request = request; diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index c3da93a5150d..dba449b74db3 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -42,7 +42,7 @@ static int aa_audit_ptrace(struct aa_profile *profile, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.op = OP_PTRACE; aad.target = target; diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index e75829ba0ff9..b11a2652f541 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -66,7 +66,7 @@ void aa_info_message(const char *str) if (audit_enabled) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.info = str; aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 02fddcd4c647..4f7bc07b2dce 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -589,7 +589,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, } else { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.op = OP_SETPROCATTR; aad.info = name; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index f1f7506a464d..03dbaef2f8e3 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -965,7 +965,7 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.op = op; aad.name = name; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index deab7c7e8dc0..504ba4015aa2 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -95,7 +95,7 @@ static int audit_iface(struct aa_profile *new, const char *name, struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; if (e) aad.iface.pos = e->pos - e->start; diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 2fe8613efe33..d06f57b74f77 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -52,7 +52,7 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource, struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.op = OP_SETRLIMIT, aad.rlim.rlim = resource; diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 1a04247e3a17..c04eea2bdb0a 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -469,7 +469,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, if (!a) { a = &stack_data; - COMMON_AUDIT_DATA_INIT(a, NONE); + COMMON_AUDIT_DATA_INIT(a, LSM_AUDIT_DATA_NONE); a->selinux_audit_data = &sad; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b3bd8e1d268a..9f0384493009 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1427,7 +1427,7 @@ static int cred_has_capability(const struct cred *cred, u32 av = CAP_TO_MASK(cap); int rc; - COMMON_AUDIT_DATA_INIT(&ad, CAP); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_CAP); ad.selinux_audit_data = &sad; ad.tsk = current; ad.u.cap = cap; @@ -1499,7 +1499,7 @@ static inline int dentry_has_perm(const struct cred *cred, struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.u.dentry = dentry; ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); @@ -1516,7 +1516,7 @@ static inline int path_has_perm(const struct cred *cred, struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, PATH); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); ad.u.path = *path; ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); @@ -1541,7 +1541,7 @@ static int file_has_perm(const struct cred *cred, u32 sid = cred_sid(cred); int rc; - COMMON_AUDIT_DATA_INIT(&ad, PATH); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); ad.u.path = file->f_path; ad.selinux_audit_data = &sad; @@ -1582,7 +1582,7 @@ static int may_create(struct inode *dir, sid = tsec->sid; newsid = tsec->create_sid; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.u.dentry = dentry; ad.selinux_audit_data = &sad; @@ -1637,7 +1637,7 @@ static int may_link(struct inode *dir, dsec = dir->i_security; isec = dentry->d_inode->i_security; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.u.dentry = dentry; ad.selinux_audit_data = &sad; @@ -1685,7 +1685,7 @@ static inline int may_rename(struct inode *old_dir, old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode); new_dsec = new_dir->i_security; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.selinux_audit_data = &sad; ad.u.dentry = old_dentry; @@ -2011,7 +2011,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) return rc; } - COMMON_AUDIT_DATA_INIT(&ad, PATH); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); ad.selinux_audit_data = &sad; ad.u.path = bprm->file->f_path; @@ -2135,7 +2135,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, /* Revalidate access to inherited open files. */ - COMMON_AUDIT_DATA_INIT(&ad, INODE); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_INODE); ad.selinux_audit_data = &sad; spin_lock(&files->file_lock); @@ -2485,7 +2485,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) if (flags & MS_KERNMOUNT) return 0; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.selinux_audit_data = &sad; ad.u.dentry = sb->s_root; return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad); @@ -2497,7 +2497,7 @@ static int selinux_sb_statfs(struct dentry *dentry) struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.selinux_audit_data = &sad; ad.u.dentry = dentry->d_sb->s_root; return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); @@ -2667,7 +2667,7 @@ static noinline int audit_inode_permission(struct inode *inode, struct inode_security_struct *isec = inode->i_security; int rc; - COMMON_AUDIT_DATA_INIT(&ad, INODE); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_INODE); ad.selinux_audit_data = &sad; ad.u.inode = inode; @@ -2797,7 +2797,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, if (!inode_owner_or_capable(inode)) return -EPERM; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.selinux_audit_data = &sad; ad.u.dentry = dentry; @@ -3412,7 +3412,7 @@ static int selinux_kernel_module_request(char *kmod_name) sid = task_sid(current); - COMMON_AUDIT_DATA_INIT(&ad, KMOD); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_KMOD); ad.selinux_audit_data = &sad; ad.u.kmod_name = kmod_name; @@ -3793,7 +3793,7 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) if (sksec->sid == SECINITSID_KERNEL) return 0; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = sk; @@ -3901,7 +3901,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in snum, &sid); if (err) goto out; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sport = htons(snum); @@ -3936,7 +3936,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (err) goto out; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sport = htons(snum); @@ -3998,7 +3998,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->dport = htons(snum); @@ -4095,7 +4095,7 @@ static int selinux_socket_unix_stream_connect(struct sock *sock, struct lsm_network_audit net = {0,}; int err; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = other; @@ -4128,7 +4128,7 @@ static int selinux_socket_unix_may_send(struct socket *sock, struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = other->sk; @@ -4171,7 +4171,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, struct lsm_network_audit net = {0,}; char *addrp; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = skb->skb_iif; @@ -4227,7 +4227,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (!secmark_active && !peerlbl_active) return 0; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = skb->skb_iif; @@ -4584,7 +4584,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) return NF_DROP; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4684,7 +4684,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_ACCEPT; sksec = sk->sk_security; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4757,7 +4757,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, secmark_perm = PACKET__SEND; } - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4881,7 +4881,7 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, isec = ipc_perms->security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = ipc_perms->key; @@ -4913,7 +4913,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) isec = msq->q_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -4940,7 +4940,7 @@ static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg) isec = msq->q_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5002,7 +5002,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, return rc; } - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5035,7 +5035,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, isec = msq->q_perm.security; msec = msg->security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5062,7 +5062,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) isec = shp->shm_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; @@ -5089,7 +5089,7 @@ static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg) isec = shp->shm_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; @@ -5158,7 +5158,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma) isec = sma->sem_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; @@ -5185,7 +5185,7 @@ static int selinux_sem_associate(struct sem_array *sma, int semflg) isec = sma->sem_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; -- cgit v1.2.3 From b466066f9b648ccb6aa1e174f0389b7433e460fd Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:43 -0400 Subject: LSM: remove the task field from common_audit_data There are no legitimate users. Always use current and get back some stack space for the common_audit_data. Signed-off-by: Eric Paris --- include/linux/lsm_audit.h | 1 - security/lsm_audit.c | 8 ++------ security/selinux/hooks.c | 1 - 3 files changed, 2 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 9e1ebf5851b8..75368c1aac78 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -53,7 +53,6 @@ struct common_audit_data { #define LSM_AUDIT_DATA_KMOD 8 #define LSM_AUDIT_DATA_INODE 9 #define LSM_AUDIT_DATA_DENTRY 10 - struct task_struct *tsk; union { struct path path; struct dentry *dentry; diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 90c129b0102f..e796d2517653 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -213,12 +213,8 @@ static void dump_common_audit_data(struct audit_buffer *ab, { struct task_struct *tsk = current; - if (a->tsk) - tsk = a->tsk; - if (tsk && tsk->pid) { - audit_log_format(ab, " pid=%d comm=", tsk->pid); - audit_log_untrustedstring(ab, tsk->comm); - } + audit_log_format(ab, " pid=%d comm=", tsk->pid); + audit_log_untrustedstring(ab, tsk->comm); switch (a->type) { case LSM_AUDIT_DATA_NONE: diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9f0384493009..d79762946c6e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1429,7 +1429,6 @@ static int cred_has_capability(const struct cred *cred, COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_CAP); ad.selinux_audit_data = &sad; - ad.tsk = current; ad.u.cap = cap; switch (CAP_TO_INDEX(cap)) { -- cgit v1.2.3 From 50c205f5e5c2e2af002fd4ef537ded79b90b1b56 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:43 -0400 Subject: LSM: do not initialize common_audit_data to 0 It isn't needed. If you don't set the type of the data associated with that type it is a pretty obvious programming bug. So why waste the cycles? Signed-off-by: Eric Paris --- include/linux/lsm_audit.h | 5 --- security/apparmor/capability.c | 2 +- security/apparmor/file.c | 2 +- security/apparmor/ipc.c | 2 +- security/apparmor/lib.c | 2 +- security/apparmor/lsm.c | 2 +- security/apparmor/policy.c | 2 +- security/apparmor/policy_unpack.c | 2 +- security/apparmor/resource.c | 2 +- security/selinux/avc.c | 2 +- security/selinux/hooks.c | 68 +++++++++++++++++++-------------------- security/smack/smack.h | 2 +- 12 files changed, 44 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 75368c1aac78..1cc89e9df480 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -92,11 +92,6 @@ int ipv4_skb_to_auditdata(struct sk_buff *skb, int ipv6_skb_to_auditdata(struct sk_buff *skb, struct common_audit_data *ad, u8 *proto); -/* Initialize an LSM audit data structure. */ -#define COMMON_AUDIT_DATA_INIT(_d, _t) \ - { memset((_d), 0, sizeof(struct common_audit_data)); \ - (_d)->type = _t; } - void common_lsm_audit(struct common_audit_data *a, void (*pre_audit)(struct audit_buffer *, void *), void (*post_audit)(struct audit_buffer *, void *)); diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index b66a0e4a5693..887a5e948945 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -65,7 +65,7 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_CAP); + sa.type = LSM_AUDIT_DATA_CAP; sa.aad = &aad; sa.u.cap = cap; sa.aad->tsk = task; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 6ab264ca85cc..cf19d4093ca4 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -108,7 +108,7 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = op, aad.fs.request = request; diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index dba449b74db3..cf1071b14232 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -42,7 +42,7 @@ static int aa_audit_ptrace(struct aa_profile *profile, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = OP_PTRACE; aad.target = target; diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index b11a2652f541..7430298116d6 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -66,7 +66,7 @@ void aa_info_message(const char *str) if (audit_enabled) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.info = str; aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 4f7bc07b2dce..032daab449b0 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -589,7 +589,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, } else { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = OP_SETPROCATTR; aad.info = name; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 03dbaef2f8e3..421681c7c346 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -965,7 +965,7 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = op; aad.name = name; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 504ba4015aa2..329b1fd30749 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -95,7 +95,7 @@ static int audit_iface(struct aa_profile *new, const char *name, struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; if (e) aad.iface.pos = e->pos - e->start; diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index d06f57b74f77..e1f3d7ef2c54 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -52,7 +52,7 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource, struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = OP_SETRLIMIT, aad.rlim.rlim = resource; diff --git a/security/selinux/avc.c b/security/selinux/avc.c index c04eea2bdb0a..cd91e25667d1 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -469,7 +469,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, if (!a) { a = &stack_data; - COMMON_AUDIT_DATA_INIT(a, LSM_AUDIT_DATA_NONE); + a->type = LSM_AUDIT_DATA_NONE; a->selinux_audit_data = &sad; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d79762946c6e..d9fa2489a551 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1427,7 +1427,7 @@ static int cred_has_capability(const struct cred *cred, u32 av = CAP_TO_MASK(cap); int rc; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_CAP); + ad.type = LSM_AUDIT_DATA_CAP; ad.selinux_audit_data = &sad; ad.u.cap = cap; @@ -1498,7 +1498,7 @@ static inline int dentry_has_perm(const struct cred *cred, struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); @@ -1515,7 +1515,7 @@ static inline int path_has_perm(const struct cred *cred, struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); + ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = *path; ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); @@ -1540,7 +1540,7 @@ static int file_has_perm(const struct cred *cred, u32 sid = cred_sid(cred); int rc; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); + ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = file->f_path; ad.selinux_audit_data = &sad; @@ -1581,7 +1581,7 @@ static int may_create(struct inode *dir, sid = tsec->sid; newsid = tsec->create_sid; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; ad.selinux_audit_data = &sad; @@ -1636,7 +1636,7 @@ static int may_link(struct inode *dir, dsec = dir->i_security; isec = dentry->d_inode->i_security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; ad.selinux_audit_data = &sad; @@ -1684,7 +1684,7 @@ static inline int may_rename(struct inode *old_dir, old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode); new_dsec = new_dir->i_security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.selinux_audit_data = &sad; ad.u.dentry = old_dentry; @@ -2010,7 +2010,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) return rc; } - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); + ad.type = LSM_AUDIT_DATA_PATH; ad.selinux_audit_data = &sad; ad.u.path = bprm->file->f_path; @@ -2134,7 +2134,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, /* Revalidate access to inherited open files. */ - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_INODE); + ad.type = LSM_AUDIT_DATA_INODE; ad.selinux_audit_data = &sad; spin_lock(&files->file_lock); @@ -2484,7 +2484,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) if (flags & MS_KERNMOUNT) return 0; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.selinux_audit_data = &sad; ad.u.dentry = sb->s_root; return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad); @@ -2496,7 +2496,7 @@ static int selinux_sb_statfs(struct dentry *dentry) struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.selinux_audit_data = &sad; ad.u.dentry = dentry->d_sb->s_root; return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); @@ -2666,7 +2666,7 @@ static noinline int audit_inode_permission(struct inode *inode, struct inode_security_struct *isec = inode->i_security; int rc; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_INODE); + ad.type = LSM_AUDIT_DATA_INODE; ad.selinux_audit_data = &sad; ad.u.inode = inode; @@ -2796,7 +2796,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, if (!inode_owner_or_capable(inode)) return -EPERM; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.selinux_audit_data = &sad; ad.u.dentry = dentry; @@ -3411,7 +3411,7 @@ static int selinux_kernel_module_request(char *kmod_name) sid = task_sid(current); - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_KMOD); + ad.type = LSM_AUDIT_DATA_KMOD; ad.selinux_audit_data = &sad; ad.u.kmod_name = kmod_name; @@ -3792,7 +3792,7 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) if (sksec->sid == SECINITSID_KERNEL) return 0; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = sk; @@ -3900,7 +3900,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in snum, &sid); if (err) goto out; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sport = htons(snum); @@ -3935,7 +3935,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (err) goto out; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sport = htons(snum); @@ -3997,7 +3997,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->dport = htons(snum); @@ -4094,7 +4094,7 @@ static int selinux_socket_unix_stream_connect(struct sock *sock, struct lsm_network_audit net = {0,}; int err; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = other; @@ -4127,7 +4127,7 @@ static int selinux_socket_unix_may_send(struct socket *sock, struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = other->sk; @@ -4170,7 +4170,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, struct lsm_network_audit net = {0,}; char *addrp; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = skb->skb_iif; @@ -4226,7 +4226,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (!secmark_active && !peerlbl_active) return 0; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = skb->skb_iif; @@ -4583,7 +4583,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) return NF_DROP; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4683,7 +4683,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_ACCEPT; sksec = sk->sk_security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4756,7 +4756,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, secmark_perm = PACKET__SEND; } - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4880,7 +4880,7 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, isec = ipc_perms->security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = ipc_perms->key; @@ -4912,7 +4912,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) isec = msq->q_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -4939,7 +4939,7 @@ static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg) isec = msq->q_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5001,7 +5001,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, return rc; } - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5034,7 +5034,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, isec = msq->q_perm.security; msec = msg->security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5061,7 +5061,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) isec = shp->shm_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; @@ -5088,7 +5088,7 @@ static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg) isec = shp->shm_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; @@ -5157,7 +5157,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma) isec = sma->sem_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; @@ -5184,7 +5184,7 @@ static int selinux_sem_associate(struct sem_array *sma, int semflg) isec = sma->sem_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; diff --git a/security/smack/smack.h b/security/smack/smack.h index 4ede719922ed..b61e75f224d4 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -319,7 +319,7 @@ void smack_log(char *subject_label, char *object_label, static inline void smk_ad_init(struct smk_audit_info *a, const char *func, char type) { - memset(a, 0, sizeof(*a)); + memset(&a->sad, 0, sizeof(a->sad)); a->a.type = type; a->a.smack_audit_data = &a->sad; a->a.smack_audit_data->function = func; -- cgit v1.2.3 From 5de0a567c020479d89953038eee9192d417349be Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:53:47 +0200 Subject: TTY: deprecate linux/generic_serial.h Since nobody in the kernel includes that file, let us remove the structs visible to the kernel. However since the userspace sees the file, it still may include that. hence deprecate the use of the header by an added cpp #warning. We should remove the file completely after a couple of years. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- include/linux/generic_serial.h | 64 ++---------------------------------------- 1 file changed, 2 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/include/linux/generic_serial.h b/include/linux/generic_serial.h index fadff28505bb..79b3eb37243a 100644 --- a/include/linux/generic_serial.h +++ b/include/linux/generic_serial.h @@ -4,7 +4,6 @@ * Copyright (C) 1998 R.E.Wolff@BitWizard.nl * * written for the SX serial driver. - * Contains the code that should be shared over all the serial drivers. * * Version 0.1 -- December, 1998. */ @@ -12,45 +11,8 @@ #ifndef GENERIC_SERIAL_H #define GENERIC_SERIAL_H -#ifdef __KERNEL__ -#include -#include - -struct real_driver { - void (*disable_tx_interrupts) (void *); - void (*enable_tx_interrupts) (void *); - void (*disable_rx_interrupts) (void *); - void (*enable_rx_interrupts) (void *); - void (*shutdown_port) (void*); - int (*set_real_termios) (void*); - int (*chars_in_buffer) (void*); - void (*close) (void*); - void (*hungup) (void*); - void (*getserial) (void*, struct serial_struct *sp); -}; - - - -struct gs_port { - int magic; - struct tty_port port; - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - struct mutex port_write_mutex; - unsigned long event; - unsigned short closing_wait; - int close_delay; - struct real_driver *rd; - int wakeup_chars; - int baud_base; - int baud; - int custom_divisor; - spinlock_t driver_lock; -}; - -#endif /* __KERNEL__ */ +#warning Use of this header is deprecated. +#warning Since nobody sets the constants defined here for you, you should not, in any case, use them. Including the header is thus pointless. /* Flags */ /* Warning: serial.h defines some ASYNC_ flags, they say they are "only" @@ -60,8 +22,6 @@ struct gs_port { #define GS_RX_INTEN 0x00400000 #define GS_ACTIVE 0x00200000 - - #define GS_TYPE_NORMAL 1 #define GS_DEBUG_FLUSH 0x00000001 @@ -72,24 +32,4 @@ struct gs_port { #define GS_DEBUG_FLOW 0x00000020 #define GS_DEBUG_WRITE 0x00000040 -#ifdef __KERNEL__ -int gs_put_char(struct tty_struct *tty, unsigned char ch); -int gs_write(struct tty_struct *tty, - const unsigned char *buf, int count); -int gs_write_room(struct tty_struct *tty); -int gs_chars_in_buffer(struct tty_struct *tty); -void gs_flush_buffer(struct tty_struct *tty); -void gs_flush_chars(struct tty_struct *tty); -void gs_stop(struct tty_struct *tty); -void gs_start(struct tty_struct *tty); -void gs_hangup(struct tty_struct *tty); -int gs_block_til_ready(void *port, struct file *filp); -void gs_close(struct tty_struct *tty, struct file *filp); -void gs_set_termios (struct tty_struct * tty, - struct ktermios * old_termios); -int gs_init_port(struct gs_port *port); -int gs_setserial(struct gs_port *port, struct serial_struct __user *sp); -int gs_getserial(struct gs_port *port, struct serial_struct __user *sp); -void gs_got_break(struct gs_port *port); -#endif /* __KERNEL__ */ #endif -- cgit v1.2.3 From 05eb48be91e6675c225b5f86c88ecbb83152119c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:53:49 +0200 Subject: TTY: isdn, remove callout I wonder how this survived there during the whole 2.6 series until now :D. Callouts are not used for a decade, so let us remove it also from isdn. This means removal of ISDN_ASYNC_CALLOUT_ACTIVE which is never raised in info->flags and callout_termios which are never used. This will help us to get rid of ISDN_ASYNC_* flags and use ASYNC ones from serial.h. And then we will switch to tty_port. Signed-off-by: Jiri Slaby Cc: Karsten Keil Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_tty.c | 32 ++++++-------------------------- include/linux/isdn.h | 3 --- 2 files changed, 6 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 1d701cd6031b..27f70923c224 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1504,18 +1504,11 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) - return -EBUSY; info->flags |= ISDN_ASYNC_NORMAL_ACTIVE; return 0; } - if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) { - if (info->normal_termios.c_cflag & CLOCAL) - do_clocal = 1; - } else { - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - } + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in @@ -1546,8 +1539,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * #endif break; } - if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) && - !(info->flags & ISDN_ASYNC_CLOSING) && + if (!(info->flags & ISDN_ASYNC_CLOSING) && (do_clocal || (info->msr & UART_MSR_DCD))) { break; } @@ -1670,14 +1662,6 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) return; } info->flags |= ISDN_ASYNC_CLOSING; - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; - if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) - info->callout_termios = *tty->termios; tty->closing = 1; /* @@ -1731,7 +1715,7 @@ isdn_tty_hangup(struct tty_struct *tty) return; isdn_tty_shutdown(info); info->count = 0; - info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE); + info->flags &= ~ISDN_ASYNC_NORMAL_ACTIVE; info->tty = NULL; wake_up_interruptible(&info->open_wait); } @@ -2116,8 +2100,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm *setup) return (wret == 2) ? 3 : 0; } -#define TTY_IS_ACTIVE(info) \ - (info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) +#define TTY_IS_ACTIVE(info) (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) int isdn_tty_stat_callback(int i, isdn_ctrl *c) @@ -2628,11 +2611,8 @@ isdn_tty_modem_result(int code, modem_info *info) if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { return; } - if ((info->flags & ISDN_ASYNC_CHECK_CD) && - (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) && - (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) { + if (info->flags & ISDN_ASYNC_CHECK_CD) tty_hangup(info->tty); - } } } diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 292f27a793d4..d13b76df34b3 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -393,7 +393,6 @@ typedef struct isdn_net_dev_s { #define ISDN_ASYNC_MAGIC 0x49344C01 /* for paranoia-checking */ #define ISDN_ASYNC_INITIALIZED 0x80000000 /* port was initialized */ -#define ISDN_ASYNC_CALLOUT_ACTIVE 0x40000000 /* Call out device active */ #define ISDN_ASYNC_NORMAL_ACTIVE 0x20000000 /* Normal device active */ #define ISDN_ASYNC_CLOSING 0x08000000 /* Serial port is closing */ #define ISDN_ASYNC_CTS_FLOW 0x04000000 /* Do CTS flow control */ @@ -498,8 +497,6 @@ typedef struct modem_info { #endif struct tty_struct *tty; /* Pointer to corresponding tty */ atemu emu; /* AT-emulator data */ - struct ktermios normal_termios; /* For saving termios structs */ - struct ktermios callout_termios; wait_queue_head_t open_wait, close_wait; spinlock_t readlock; } modem_info; -- cgit v1.2.3 From 6776a2f07924192fd168bdceb1a00d10630edb4d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:53:50 +0200 Subject: TTY: isdn, remove ISDN_ASYNC_* flags They are the same as TTY ones. So there is no need to redefine them. Remove ISDN_ASYNC_* and use only ASYNC_*. Except the MAGIC number, of course. While we are there, remove also the SERIAL_TYPE flags which are unused. Perhaps we should move the ASYNC flags from serial.h to tty.h given they are used by the tty layer and tty drivers, not only serial? Signed-off-by: Jiri Slaby Cc: Karsten Keil Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_tty.c | 59 +++++++++++++++++++++++---------------------- include/linux/isdn.h | 12 --------- 2 files changed, 30 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 27f70923c224..bcf4bbe693bc 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -11,6 +11,7 @@ #undef ISDN_TTY_STAT_DEBUG #include +#include /* ASYNC_* flags */ #include #include #include @@ -1036,20 +1037,20 @@ isdn_tty_change_speed(modem_info *info) /* CTS flow control flag and modem status interrupts */ if (cflag & CRTSCTS) { - info->flags |= ISDN_ASYNC_CTS_FLOW; + info->flags |= ASYNC_CTS_FLOW; } else - info->flags &= ~ISDN_ASYNC_CTS_FLOW; + info->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) - info->flags &= ~ISDN_ASYNC_CHECK_CD; + info->flags &= ~ASYNC_CHECK_CD; else { - info->flags |= ISDN_ASYNC_CHECK_CD; + info->flags |= ASYNC_CHECK_CD; } } static int isdn_tty_startup(modem_info *info) { - if (info->flags & ISDN_ASYNC_INITIALIZED) + if (info->flags & ASYNC_INITIALIZED) return 0; isdn_lock_drivers(); #ifdef ISDN_DEBUG_MODEM_OPEN @@ -1066,7 +1067,7 @@ isdn_tty_startup(modem_info *info) */ isdn_tty_change_speed(info); - info->flags |= ISDN_ASYNC_INITIALIZED; + info->flags |= ASYNC_INITIALIZED; info->msr |= (UART_MSR_DSR | UART_MSR_CTS); info->send_outstanding = 0; return 0; @@ -1079,7 +1080,7 @@ isdn_tty_startup(modem_info *info) static void isdn_tty_shutdown(modem_info *info) { - if (!(info->flags & ISDN_ASYNC_INITIALIZED)) + if (!(info->flags & ASYNC_INITIALIZED)) return; #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line); @@ -1099,7 +1100,7 @@ isdn_tty_shutdown(modem_info *info) if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - info->flags &= ~ISDN_ASYNC_INITIALIZED; + info->flags &= ~ASYNC_INITIALIZED; } /* isdn_tty_write() is the main send-routine. It is called from the upper @@ -1486,11 +1487,11 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * * until it's done, and then try again. */ if (tty_hung_up_p(filp) || - (info->flags & ISDN_ASYNC_CLOSING)) { - if (info->flags & ISDN_ASYNC_CLOSING) + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); #ifdef MODEM_DO_RESTART - if (info->flags & ISDN_ASYNC_HUP_NOTIFY) + if (info->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -1504,7 +1505,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ISDN_ASYNC_NORMAL_ACTIVE; + info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } if (tty->termios->c_cflag & CLOCAL) @@ -1528,9 +1529,9 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * while (1) { set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ISDN_ASYNC_INITIALIZED)) { + !(info->flags & ASYNC_INITIALIZED)) { #ifdef MODEM_DO_RESTART - if (info->flags & ISDN_ASYNC_HUP_NOTIFY) + if (info->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -1539,7 +1540,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * #endif break; } - if (!(info->flags & ISDN_ASYNC_CLOSING) && + if (!(info->flags & ASYNC_CLOSING) && (do_clocal || (info->msr & UART_MSR_DCD))) { break; } @@ -1564,7 +1565,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * #endif if (retval) return retval; - info->flags |= ISDN_ASYNC_NORMAL_ACTIVE; + info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -1661,7 +1662,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) module_put(info->owner); return; } - info->flags |= ISDN_ASYNC_CLOSING; + info->flags |= ASYNC_CLOSING; tty->closing = 1; /* @@ -1670,7 +1671,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) * interrupt driver to stop checking the data ready bit in the * line status register. */ - if (info->flags & ISDN_ASYNC_INITIALIZED) { + if (info->flags & ASYNC_INITIALIZED) { tty_wait_until_sent_from_close(tty, 3000); /* 30 seconds timeout */ /* * Before we drop DTR, make sure the UART transmitter @@ -1696,7 +1697,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) msleep_interruptible(500); wake_up_interruptible(&info->open_wait); } - info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CLOSING); + info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_close normal exit\n"); @@ -1715,7 +1716,7 @@ isdn_tty_hangup(struct tty_struct *tty) return; isdn_tty_shutdown(info); info->count = 0; - info->flags &= ~ISDN_ASYNC_NORMAL_ACTIVE; + info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; wake_up_interruptible(&info->open_wait); } @@ -2061,7 +2062,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm *setup) #endif if ( #ifndef FIX_FILE_TRANSFER - (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) && + (info->flags & ASYNC_NORMAL_ACTIVE) && #endif (info->isdn_driver == -1) && (info->isdn_channel == -1) && @@ -2100,7 +2101,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm *setup) return (wret == 2) ? 3 : 0; } -#define TTY_IS_ACTIVE(info) (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) +#define TTY_IS_ACTIVE(info) (info->flags & ASYNC_NORMAL_ACTIVE) int isdn_tty_stat_callback(int i, isdn_ctrl *c) @@ -2319,7 +2320,7 @@ isdn_tty_at_cout(char *msg, modem_info *info) spin_lock_irqsave(&info->readlock, flags); tty = info->tty; - if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) { + if ((info->flags & ASYNC_CLOSING) || (!tty)) { spin_unlock_irqrestore(&info->readlock, flags); return; } @@ -2469,15 +2470,15 @@ isdn_tty_modem_result(int code, modem_info *info) case RESULT_NO_CARRIER: #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n", - (info->flags & ISDN_ASYNC_CLOSING), + (info->flags & ASYNC_CLOSING), (!info->tty)); #endif m->mdmreg[REG_RINGCNT] = 0; del_timer(&info->nc_timer); info->ncarrier = 0; - if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { + if ((info->flags & ASYNC_CLOSING) || (!info->tty)) return; - } + #ifdef CONFIG_ISDN_AUDIO if (info->vonline & 1) { #ifdef ISDN_DEBUG_MODEM_VOICE @@ -2608,10 +2609,10 @@ isdn_tty_modem_result(int code, modem_info *info) } } if (code == RESULT_NO_CARRIER) { - if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { + if ((info->flags & ASYNC_CLOSING) || (!info->tty)) return; - } - if (info->flags & ISDN_ASYNC_CHECK_CD) + + if (info->flags & ASYNC_CHECK_CD) tty_hangup(info->tty); } } diff --git a/include/linux/isdn.h b/include/linux/isdn.h index d13b76df34b3..1b4b4c1846c5 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -392,20 +392,8 @@ typedef struct isdn_net_dev_s { /*======================= Start of ISDN-tty stuff ===========================*/ #define ISDN_ASYNC_MAGIC 0x49344C01 /* for paranoia-checking */ -#define ISDN_ASYNC_INITIALIZED 0x80000000 /* port was initialized */ -#define ISDN_ASYNC_NORMAL_ACTIVE 0x20000000 /* Normal device active */ -#define ISDN_ASYNC_CLOSING 0x08000000 /* Serial port is closing */ -#define ISDN_ASYNC_CTS_FLOW 0x04000000 /* Do CTS flow control */ -#define ISDN_ASYNC_CHECK_CD 0x02000000 /* i.e., CLOCAL */ -#define ISDN_ASYNC_HUP_NOTIFY 0x0001 /* Notify tty on hangups/closes */ -#define ISDN_ASYNC_SESSION_LOCKOUT 0x0100 /* Lock cua opens on session */ -#define ISDN_ASYNC_PGRP_LOCKOUT 0x0200 /* Lock cua opens on pgrp */ -#define ISDN_ASYNC_CALLOUT_NOHUP 0x0400 /* No hangup for cui */ -#define ISDN_ASYNC_SPLIT_TERMIOS 0x0008 /* Sep. termios for dialin/out */ #define ISDN_SERIAL_XMIT_SIZE 1024 /* Default bufsize for write */ #define ISDN_SERIAL_XMIT_MAX 4000 /* Maximum bufsize for write */ -#define ISDN_SERIAL_TYPE_NORMAL 1 -#define ISDN_SERIAL_TYPE_CALLOUT 2 #ifdef CONFIG_ISDN_AUDIO /* For using sk_buffs with audio we need some private variables -- cgit v1.2.3 From ce93d33cf45db9893cfc725a2b470da15bfe4435 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:53:51 +0200 Subject: TTY: isdn, do not play with module refcounts The module which called allocate_tty_driver is already refcounted by the TTY layer automatically. And since THIS_MODULE is isdn_tty and it allocated the tty_driver, there is no need to do the counts in isdn's tty->ops->open/close. Signed-off-by: Jiri Slaby Cc: Karsten Keil Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_tty.c | 11 ----------- include/linux/isdn.h | 1 - 2 files changed, 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index bcf4bbe693bc..1ff307276d2d 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1584,10 +1584,6 @@ isdn_tty_open(struct tty_struct *tty, struct file *filp) info = &dev->mdm.info[tty->index]; if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_open")) return -ENODEV; - if (!try_module_get(info->owner)) { - printk(KERN_WARNING "%s: cannot reserve module\n", __func__); - return -ENODEV; - } #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name, info->count); @@ -1603,7 +1599,6 @@ isdn_tty_open(struct tty_struct *tty, struct file *filp) #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_open return after startup\n"); #endif - module_put(info->owner); return retval; } retval = isdn_tty_block_til_ready(tty, filp, info); @@ -1611,7 +1606,6 @@ isdn_tty_open(struct tty_struct *tty, struct file *filp) #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n"); #endif - module_put(info->owner); return retval; } #ifdef ISDN_DEBUG_MODEM_OPEN @@ -1659,7 +1653,6 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n"); #endif - module_put(info->owner); return; } info->flags |= ASYNC_CLOSING; @@ -1692,7 +1685,6 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) info->tty = NULL; info->ncarrier = 0; tty->closing = 0; - module_put(info->owner); if (info->blocked_open) { msleep_interruptible(500); wake_up_interruptible(&info->open_wait); @@ -1879,9 +1871,6 @@ isdn_tty_modem_init(void) retval = -ENOMEM; goto err_unregister; } -#endif -#ifdef MODULE - info->owner = THIS_MODULE; #endif spin_lock_init(&info->readlock); sprintf(info->last_cause, "0000"); diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 1b4b4c1846c5..66d9d71e003c 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -435,7 +435,6 @@ typedef struct atemu { /* Private data (similar to async_struct in ) */ typedef struct modem_info { int magic; - struct module *owner; int flags; /* defined in tty.h */ int x_char; /* xon/xoff character */ int mcr; /* Modem control register */ -- cgit v1.2.3 From ed722ead61e3776cbcda3805bb872ff2b856d3b9 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:53:53 +0200 Subject: TTY: isdn, remove unused members from modem_info session and pgrp are unused. Prune them. Signed-off-by: Jiri Slaby Cc: Karsten Keil Signed-off-by: Greg Kroah-Hartman --- include/linux/isdn.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 66d9d71e003c..63af3208004f 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -443,8 +443,6 @@ typedef struct modem_info { int line; int count; /* # of fd on device */ int blocked_open; /* # of blocked opens */ - long session; /* Session of opening process */ - long pgrp; /* pgrp of opening process */ int online; /* 1 = B-Channel is up, drop data */ /* 2 = B-Channel is up, deliver d.*/ int dialing; /* Dial in progress or ATA */ -- cgit v1.2.3 From 48decc1c743fa4eb5ba5dd3f556e7fcbefe65440 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:53:54 +0200 Subject: TTY: isdn, add tty_port And use tty_port->flags now. Other members will follow. Signed-off-by: Jiri Slaby Cc: Karsten Keil Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_tty.c | 60 +++++++++++++++++++++++---------------------- include/linux/isdn.h | 3 ++- 2 files changed, 33 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 74b05188c18f..8ba526dc98bc 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1046,20 +1046,20 @@ isdn_tty_change_speed(modem_info *info) /* CTS flow control flag and modem status interrupts */ if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; + info->port.flags |= ASYNC_CTS_FLOW; } else - info->flags &= ~ASYNC_CTS_FLOW; + info->port.flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; + info->port.flags &= ~ASYNC_CHECK_CD; else { - info->flags |= ASYNC_CHECK_CD; + info->port.flags |= ASYNC_CHECK_CD; } } static int isdn_tty_startup(modem_info *info) { - if (info->flags & ASYNC_INITIALIZED) + if (info->port.flags & ASYNC_INITIALIZED) return 0; isdn_lock_drivers(); #ifdef ISDN_DEBUG_MODEM_OPEN @@ -1076,7 +1076,7 @@ isdn_tty_startup(modem_info *info) */ isdn_tty_change_speed(info); - info->flags |= ASYNC_INITIALIZED; + info->port.flags |= ASYNC_INITIALIZED; info->msr |= (UART_MSR_DSR | UART_MSR_CTS); info->send_outstanding = 0; return 0; @@ -1089,7 +1089,7 @@ isdn_tty_startup(modem_info *info) static void isdn_tty_shutdown(modem_info *info) { - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) return; #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line); @@ -1109,7 +1109,7 @@ isdn_tty_shutdown(modem_info *info) if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + info->port.flags &= ~ASYNC_INITIALIZED; } /* isdn_tty_write() is the main send-routine. It is called from the upper @@ -1496,11 +1496,11 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * * until it's done, and then try again. */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) + (info->port.flags & ASYNC_CLOSING)) { + if (info->port.flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); #ifdef MODEM_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -1514,7 +1514,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } if (tty->termios->c_cflag & CLOCAL) @@ -1538,9 +1538,9 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * while (1) { set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { + !(info->port.flags & ASYNC_INITIALIZED)) { #ifdef MODEM_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -1549,7 +1549,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * #endif break; } - if (!(info->flags & ASYNC_CLOSING) && + if (!(info->port.flags & ASYNC_CLOSING) && (do_clocal || (info->msr & UART_MSR_DCD))) { break; } @@ -1574,7 +1574,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * #endif if (retval) return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -1600,6 +1600,7 @@ isdn_tty_open(struct tty_struct *tty, struct file *filp) info->count++; tty->driver_data = info; info->tty = tty; + tty->port = &info->port; /* * Start up serial port */ @@ -1664,7 +1665,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) #endif return; } - info->flags |= ASYNC_CLOSING; + info->port.flags |= ASYNC_CLOSING; tty->closing = 1; /* @@ -1673,7 +1674,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) * interrupt driver to stop checking the data ready bit in the * line status register. */ - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { tty_wait_until_sent_from_close(tty, 3000); /* 30 seconds timeout */ /* * Before we drop DTR, make sure the UART transmitter @@ -1698,7 +1699,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) msleep_interruptible(500); wake_up_interruptible(&info->open_wait); } - info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + info->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_close normal exit\n"); @@ -1717,7 +1718,7 @@ isdn_tty_hangup(struct tty_struct *tty) return; isdn_tty_shutdown(info); info->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; wake_up_interruptible(&info->open_wait); } @@ -1881,6 +1882,7 @@ isdn_tty_modem_init(void) goto err_unregister; } #endif + tty_port_init(&info->port); spin_lock_init(&info->readlock); sprintf(info->last_cause, "0000"); sprintf(info->last_num, "none"); @@ -2055,12 +2057,12 @@ isdn_tty_find_icall(int di, int ch, setup_parm *setup) #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret); printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx, - info->flags, info->isdn_driver, info->isdn_channel, - dev->usage[idx]); + info->port.flags, info->isdn_driver, + info->isdn_channel, dev->usage[idx]); #endif if ( #ifndef FIX_FILE_TRANSFER - (info->flags & ASYNC_NORMAL_ACTIVE) && + (info->port.flags & ASYNC_NORMAL_ACTIVE) && #endif (info->isdn_driver == -1) && (info->isdn_channel == -1) && @@ -2099,7 +2101,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm *setup) return (wret == 2) ? 3 : 0; } -#define TTY_IS_ACTIVE(info) (info->flags & ASYNC_NORMAL_ACTIVE) +#define TTY_IS_ACTIVE(info) (info->port.flags & ASYNC_NORMAL_ACTIVE) int isdn_tty_stat_callback(int i, isdn_ctrl *c) @@ -2318,7 +2320,7 @@ isdn_tty_at_cout(char *msg, modem_info *info) spin_lock_irqsave(&info->readlock, flags); tty = info->tty; - if ((info->flags & ASYNC_CLOSING) || (!tty)) { + if ((info->port.flags & ASYNC_CLOSING) || (!tty)) { spin_unlock_irqrestore(&info->readlock, flags); return; } @@ -2468,13 +2470,13 @@ isdn_tty_modem_result(int code, modem_info *info) case RESULT_NO_CARRIER: #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n", - (info->flags & ASYNC_CLOSING), + (info->port.flags & ASYNC_CLOSING), (!info->tty)); #endif m->mdmreg[REG_RINGCNT] = 0; del_timer(&info->nc_timer); info->ncarrier = 0; - if ((info->flags & ASYNC_CLOSING) || (!info->tty)) + if ((info->port.flags & ASYNC_CLOSING) || (!info->tty)) return; #ifdef CONFIG_ISDN_AUDIO @@ -2607,10 +2609,10 @@ isdn_tty_modem_result(int code, modem_info *info) } } if (code == RESULT_NO_CARRIER) { - if ((info->flags & ASYNC_CLOSING) || (!info->tty)) + if ((info->port.flags & ASYNC_CLOSING) || (!info->tty)) return; - if (info->flags & ASYNC_CHECK_CD) + if (info->port.flags & ASYNC_CHECK_CD) tty_hangup(info->tty); } } diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 63af3208004f..bb710414f275 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -15,6 +15,7 @@ #define __ISDN_H__ #include +#include #define ISDN_MAX_DRIVERS 32 #define ISDN_MAX_CHANNELS 64 @@ -435,7 +436,7 @@ typedef struct atemu { /* Private data (similar to async_struct in ) */ typedef struct modem_info { int magic; - int flags; /* defined in tty.h */ + struct tty_port port; int x_char; /* xon/xoff character */ int mcr; /* Modem control register */ int msr; /* Modem status register */ -- cgit v1.2.3 From c6e92b63d711c12ffb47f4dbb53902b7f26032e7 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:53:55 +0200 Subject: TTY: isdn, use open/close_wait from tty_port Hmm, the isdn ones were initialized twice. Signed-off-by: Jiri Slaby Cc: Karsten Keil Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_common.c | 2 -- drivers/isdn/i4l/isdn_tty.c | 16 +++++++--------- include/linux/isdn.h | 1 - 3 files changed, 7 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index d9f5524593fb..2ffa0b733327 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -2327,8 +2327,6 @@ static int __init isdn_init(void) dev->chanmap[i] = -1; dev->m_idx[i] = -1; strcpy(dev->num[i], "???"); - init_waitqueue_head(&dev->mdm.info[i].open_wait); - init_waitqueue_head(&dev->mdm.info[i].close_wait); } if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) { printk(KERN_WARNING "isdn: Could not register control devices\n"); diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 8ba526dc98bc..33764d052e19 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1498,7 +1498,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); + interruptible_sleep_on(&info->port.close_wait); #ifdef MODEM_DO_RESTART if (info->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; @@ -1527,7 +1527,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->port.open_wait, &wait); #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n", info->line, info->count); @@ -1564,7 +1564,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * schedule(); } current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); if (!tty_hung_up_p(filp)) info->count++; info->blocked_open--; @@ -1697,10 +1697,10 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) tty->closing = 0; if (info->blocked_open) { msleep_interruptible(500); - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } info->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + wake_up_interruptible(&info->port.close_wait); #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_close normal exit\n"); #endif @@ -1720,7 +1720,7 @@ isdn_tty_hangup(struct tty_struct *tty) info->count = 0; info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } /* This routine initializes all emulator-data. @@ -1898,8 +1898,6 @@ isdn_tty_modem_init(void) info->x_char = 0; info->count = 0; info->blocked_open = 0; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); info->isdn_driver = -1; info->isdn_channel = -1; info->drv_index = -1; @@ -2194,7 +2192,7 @@ isdn_tty_stat_callback(int i, isdn_ctrl *c) */ if (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD)) { - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } /* Schedule CONNECT-Message to any tty diff --git a/include/linux/isdn.h b/include/linux/isdn.h index bb710414f275..c3ddf2081907 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -483,7 +483,6 @@ typedef struct modem_info { #endif struct tty_struct *tty; /* Pointer to corresponding tty */ atemu emu; /* AT-emulator data */ - wait_queue_head_t open_wait, close_wait; spinlock_t readlock; } modem_info; -- cgit v1.2.3 From 1b05f030a90569d9617d0fe42910f9e5c8cc59ec Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:53:56 +0200 Subject: TTY: isdn, use counts from tty_port blocked_open and count this time. Signed-off-by: Jiri Slaby Cc: Karsten Keil Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_tty.c | 43 +++++++++++++++++++++---------------------- include/linux/isdn.h | 2 -- 2 files changed, 21 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 33764d052e19..c81a725fde74 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1533,8 +1533,8 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info->line, info->count); #endif if (!(tty_hung_up_p(filp))) - info->count--; - info->blocked_open++; + info->port.count--; + info->port.blocked_open++; while (1) { set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || @@ -1559,18 +1559,18 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * } #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&info->port.open_wait, &wait); if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; + info->port.count++; + info->port.blocked_open--; #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif if (retval) return retval; @@ -1595,9 +1595,9 @@ isdn_tty_open(struct tty_struct *tty, struct file *filp) return -ENODEV; #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name, - info->count); + info->port.count); #endif - info->count++; + info->port.count++; tty->driver_data = info; info->tty = tty; tty->port = &info->port; @@ -1642,7 +1642,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) #endif return; } - if ((tty->count == 1) && (info->count != 1)) { + if ((tty->count == 1) && (info->port.count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -1651,15 +1651,15 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) * serial port won't be shutdown. */ printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; + "info->count is %d\n", info->port.count); + info->port.count = 1; } - if (--info->count < 0) { + if (--info->port.count < 0) { printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n", - info->line, info->count); - info->count = 0; + info->line, info->port.count); + info->port.count = 0; } - if (info->count) { + if (info->port.count) { #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n"); #endif @@ -1695,7 +1695,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) info->tty = NULL; info->ncarrier = 0; tty->closing = 0; - if (info->blocked_open) { + if (info->port.blocked_open) { msleep_interruptible(500); wake_up_interruptible(&info->port.open_wait); } @@ -1717,7 +1717,7 @@ isdn_tty_hangup(struct tty_struct *tty) if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_hangup")) return; isdn_tty_shutdown(info); - info->count = 0; + info->port.count = 0; info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; wake_up_interruptible(&info->port.open_wait); @@ -1896,8 +1896,6 @@ isdn_tty_modem_init(void) info->line = i; info->tty = NULL; info->x_char = 0; - info->count = 0; - info->blocked_open = 0; info->isdn_driver = -1; info->isdn_channel = -1; info->drv_index = -1; @@ -2047,7 +2045,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm *setup) for (i = 0; i < ISDN_MAX_CHANNELS; i++) { modem_info *info = &dev->mdm.info[i]; - if (info->count == 0) + if (info->port.count == 0) continue; if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) && /* SI1 is matching */ (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */ @@ -2190,7 +2188,7 @@ isdn_tty_stat_callback(int i, isdn_ctrl *c) * for incoming call of this device when * DCD follow the state of incoming carrier */ - if (info->blocked_open && + if (info->port.blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD)) { wake_up_interruptible(&info->port.open_wait); } @@ -2200,7 +2198,8 @@ isdn_tty_stat_callback(int i, isdn_ctrl *c) * set DCD-bit of its modem-status. */ if (TTY_IS_ACTIVE(info) || - (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD))) { + (info->port.blocked_open && + (info->emu.mdmreg[REG_DCD] & BIT_DCD))) { info->msr |= UART_MSR_DCD; info->emu.charge = 0; if (info->dialing & 0xf) diff --git a/include/linux/isdn.h b/include/linux/isdn.h index c3ddf2081907..a4b71fe20a69 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -442,8 +442,6 @@ typedef struct modem_info { int msr; /* Modem status register */ int lsr; /* Line status register */ int line; - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ int online; /* 1 = B-Channel is up, drop data */ /* 2 = B-Channel is up, deliver d.*/ int dialing; /* Dial in progress or ATA */ -- cgit v1.2.3 From ba43294d51ac6491e60c2fc33a974a9a1002dfed Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:53:57 +0200 Subject: TTY: isdn, use tty from tty_port No recounting this time, just a plain switch. Signed-off-by: Jiri Slaby Cc: Karsten Keil Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_tty.c | 41 ++++++++++++++++++++--------------------- include/linux/isdn.h | 1 - 2 files changed, 20 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index c81a725fde74..869885620c94 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -68,7 +68,7 @@ isdn_tty_try_read(modem_info *info, struct sk_buff *skb) if (!info->online) return 0; - tty = info->tty; + tty = info->port.tty; if (!tty) return 0; @@ -144,7 +144,7 @@ isdn_tty_readmodem(void) if ((info->vonline & 1) && (info->emu.vpar[1])) isdn_audio_eval_silence(info); #endif - tty = info->tty; + tty = info->port.tty; if (tty) { if (info->mcr & UART_MCR_RTS) { /* CISCO AsyncPPP Hack */ @@ -300,7 +300,7 @@ isdn_tty_tint(modem_info *info) len = skb->len; if ((slen = isdn_writebuf_skb_stub(info->isdn_driver, info->isdn_channel, 1, skb)) == len) { - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; info->send_outstanding++; info->msr &= ~UART_MSR_CTS; info->lsr &= ~UART_LSR_TEMT; @@ -705,7 +705,7 @@ isdn_tty_modem_hup(modem_info *info, int local) printk(KERN_DEBUG "Mhup ttyI%d\n", info->line); #endif info->rcvsched = 0; - isdn_tty_flush_buffer(info->tty); + isdn_tty_flush_buffer(info->port.tty); if (info->online) { info->last_lhup = local; info->online = 0; @@ -1008,15 +1008,15 @@ isdn_tty_change_speed(modem_info *info) quot; int i; - if (!info->tty || !info->tty->termios) + if (!info->port.tty || !info->port.tty->termios) return; - cflag = info->tty->termios->c_cflag; + cflag = info->port.tty->termios->c_cflag; quot = i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; if (i < 1 || i > 2) - info->tty->termios->c_cflag &= ~CBAUDEX; + info->port.tty->termios->c_cflag &= ~CBAUDEX; else i += 15; } @@ -1069,8 +1069,8 @@ isdn_tty_startup(modem_info *info) * Now, initialize the UART */ info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); /* * and set the speed of the serial port */ @@ -1096,7 +1096,7 @@ isdn_tty_shutdown(modem_info *info) #endif isdn_unlock_drivers(); info->msr &= ~UART_MSR_RI; - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0); @@ -1106,8 +1106,8 @@ isdn_tty_shutdown(modem_info *info) isdn_tty_modem_hup(info, 1); } } - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); info->port.flags &= ~ASYNC_INITIALIZED; } @@ -1599,7 +1599,7 @@ isdn_tty_open(struct tty_struct *tty, struct file *filp) #endif info->port.count++; tty->driver_data = info; - info->tty = tty; + info->port.tty = tty; tty->port = &info->port; /* * Start up serial port @@ -1692,7 +1692,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) isdn_tty_shutdown(info); isdn_tty_flush_buffer(tty); tty_ldisc_flush(tty); - info->tty = NULL; + info->port.tty = NULL; info->ncarrier = 0; tty->closing = 0; if (info->port.blocked_open) { @@ -1719,7 +1719,7 @@ isdn_tty_hangup(struct tty_struct *tty) isdn_tty_shutdown(info); info->port.count = 0; info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - info->tty = NULL; + info->port.tty = NULL; wake_up_interruptible(&info->port.open_wait); } @@ -1894,7 +1894,6 @@ isdn_tty_modem_init(void) isdn_tty_modem_reset_regs(info, 1); info->magic = ISDN_ASYNC_MAGIC; info->line = i; - info->tty = NULL; info->x_char = 0; info->isdn_driver = -1; info->isdn_channel = -1; @@ -2316,7 +2315,7 @@ isdn_tty_at_cout(char *msg, modem_info *info) l = strlen(msg); spin_lock_irqsave(&info->readlock, flags); - tty = info->tty; + tty = info->port.tty; if ((info->port.flags & ASYNC_CLOSING) || (!tty)) { spin_unlock_irqrestore(&info->readlock, flags); return; @@ -2468,12 +2467,12 @@ isdn_tty_modem_result(int code, modem_info *info) #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n", (info->port.flags & ASYNC_CLOSING), - (!info->tty)); + (!info->port.tty)); #endif m->mdmreg[REG_RINGCNT] = 0; del_timer(&info->nc_timer); info->ncarrier = 0; - if ((info->port.flags & ASYNC_CLOSING) || (!info->tty)) + if ((info->port.flags & ASYNC_CLOSING) || (!info->port.tty)) return; #ifdef CONFIG_ISDN_AUDIO @@ -2606,11 +2605,11 @@ isdn_tty_modem_result(int code, modem_info *info) } } if (code == RESULT_NO_CARRIER) { - if ((info->port.flags & ASYNC_CLOSING) || (!info->tty)) + if ((info->port.flags & ASYNC_CLOSING) || (!info->port.tty)) return; if (info->port.flags & ASYNC_CHECK_CD) - tty_hangup(info->tty); + tty_hangup(info->port.tty); } } diff --git a/include/linux/isdn.h b/include/linux/isdn.h index a4b71fe20a69..95883ac5a2f4 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -479,7 +479,6 @@ typedef struct modem_info { struct T30_s *fax; /* T30 Fax Group 3 data/interface */ int faxonline; /* Fax-channel status */ #endif - struct tty_struct *tty; /* Pointer to corresponding tty */ atemu emu; /* AT-emulator data */ spinlock_t readlock; } modem_info; -- cgit v1.2.3 From 82e46b31908244678a6e7404c4204dd3f6fea9f0 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:53:58 +0200 Subject: TTY: isdn, use xmit_buf from tty_port Signed-off-by: Jiri Slaby Cc: Karsten Keil Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_tty.c | 16 +++++++++------- include/linux/isdn.h | 1 - 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 869885620c94..dfb83e68bd86 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -333,7 +333,7 @@ isdn_tty_countDLE(unsigned char *buf, int len) static int isdn_tty_handleDLEdown(modem_info *info, atemu *m, int len) { - unsigned char *p = &info->xmit_buf[info->xmit_count]; + unsigned char *p = &info->port.xmit_buf[info->xmit_count]; int count = 0; while (len > 0) { @@ -477,7 +477,7 @@ isdn_tty_senddown(modem_info *info) return; } skb_reserve(skb, skb_res); - memcpy(skb_put(skb, buflen), info->xmit_buf, buflen); + memcpy(skb_put(skb, buflen), info->port.xmit_buf, buflen); info->xmit_count = 0; #ifdef CONFIG_ISDN_AUDIO if (info->vonline & 2) { @@ -1152,7 +1152,7 @@ isdn_tty_write(struct tty_struct *tty, const u_char *buf, int count) isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c, &(m->pluscount), &(m->lastplus)); - memcpy(&(info->xmit_buf[info->xmit_count]), buf, c); + memcpy(&info->port.xmit_buf[info->xmit_count], buf, c); #ifdef CONFIG_ISDN_AUDIO if (info->vonline) { int cc = isdn_tty_handleDLEdown(info, m, c); @@ -1906,13 +1906,15 @@ isdn_tty_modem_init(void) #ifdef CONFIG_ISDN_AUDIO skb_queue_head_init(&info->dtmf_queue); #endif - if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) { + info->port.xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, + GFP_KERNEL); + if (!info->port.xmit_buf) { printk(KERN_ERR "Could not allocate modem xmit-buffer\n"); retval = -ENOMEM; goto err_unregister; } /* Make room for T.70 header */ - info->xmit_buf += 4; + info->port.xmit_buf += 4; } return 0; err_unregister: @@ -1921,7 +1923,7 @@ err_unregister: #ifdef CONFIG_ISDN_TTY_FAX kfree(info->fax); #endif - kfree(info->xmit_buf - 4); + kfree(info->port.xmit_buf - 4); } tty_unregister_driver(m->tty_modem); err: @@ -1942,7 +1944,7 @@ isdn_tty_exit(void) #ifdef CONFIG_ISDN_TTY_FAX kfree(info->fax); #endif - kfree(info->xmit_buf - 4); + kfree(info->port.xmit_buf - 4); } tty_unregister_driver(dev->mdm.tty_modem); put_tty_driver(dev->mdm.tty_modem); diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 95883ac5a2f4..215c41602af8 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -461,7 +461,6 @@ typedef struct modem_info { int send_outstanding;/* # of outstanding send-requests */ int xmit_size; /* max. # of chars in xmit_buf */ int xmit_count; /* # of chars in xmit_buf */ - unsigned char *xmit_buf; /* transmit buffer */ struct sk_buff_head xmit_queue; /* transmit queue */ atomic_t xmit_lock; /* Semaphore for isdn_tty_write */ #ifdef CONFIG_ISDN_AUDIO -- cgit v1.2.3 From 074d46d1d23f27488a3f314e29cae2453541f17d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 15 Mar 2012 19:45:16 +0100 Subject: wireless: rename ht_info to ht_operation Since some of the HT code pre-dates 802.11n-2009 some names are wrong. The one that bothers me most is that "HT operation" is called "HT information" in our code and that causes confusion. Rename "HT information" to "HT operation" and also the control_chan field to primary_chan to match the name used in the spec. Signed-off-by: Johannes Berg Acked-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n.c | 17 +++++++------ drivers/net/wireless/mwifiex/fw.h | 2 +- drivers/net/wireless/mwifiex/join.c | 14 +++++------ drivers/net/wireless/mwifiex/main.h | 2 +- drivers/net/wireless/mwifiex/scan.c | 12 ++++----- drivers/net/wireless/wl12xx/main.c | 2 +- include/linux/ieee80211.h | 14 +++++------ include/net/mac80211.h | 2 +- net/mac80211/ibss.c | 16 ++++++------ net/mac80211/ieee80211_i.h | 13 +++++----- net/mac80211/mesh.c | 12 ++++----- net/mac80211/mesh.h | 2 +- net/mac80211/mesh_plink.c | 4 +-- net/mac80211/mlme.c | 49 +++++++++++++++++++------------------ net/mac80211/tx.c | 4 +-- net/mac80211/util.c | 43 ++++++++++++++++---------------- 16 files changed, 102 insertions(+), 106 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index a5e182b5e944..fe8ebfebcc0e 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -350,25 +350,26 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, ret_len += sizeof(struct mwifiex_ie_types_htcap); } - if (bss_desc->bcn_ht_info) { + if (bss_desc->bcn_ht_oper) { if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { ht_info = (struct mwifiex_ie_types_htinfo *) *buffer; memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo)); ht_info->header.type = - cpu_to_le16(WLAN_EID_HT_INFORMATION); + cpu_to_le16(WLAN_EID_HT_OPERATION); ht_info->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_info)); + cpu_to_le16( + sizeof(struct ieee80211_ht_operation)); memcpy((u8 *) ht_info + sizeof(struct mwifiex_ie_types_header), - (u8 *) bss_desc->bcn_ht_info + + (u8 *) bss_desc->bcn_ht_oper + sizeof(struct ieee_types_header), le16_to_cpu(ht_info->header.len)); if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) - ht_info->ht_info.ht_param &= + ht_info->ht_oper.ht_param &= ~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY | IEEE80211_HT_PARAM_CHA_SEC_OFFSET); @@ -385,16 +386,16 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, sizeof(struct mwifiex_ie_types_chan_list_param_set) - sizeof(struct mwifiex_ie_types_header)); chan_list->chan_scan_param[0].chan_number = - bss_desc->bcn_ht_info->control_chan; + bss_desc->bcn_ht_oper->primary_chan; chan_list->chan_scan_param[0].radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && - bss_desc->bcn_ht_info->ht_param & + bss_desc->bcn_ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. radio_type, - (bss_desc->bcn_ht_info->ht_param & + (bss_desc->bcn_ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); *buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set); diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index e98fc5af73dc..930ad2f4b1e3 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -1045,7 +1045,7 @@ struct mwifiex_ie_types_htcap { struct mwifiex_ie_types_htinfo { struct mwifiex_ie_types_header header; - struct ieee80211_ht_info ht_info; + struct ieee80211_ht_operation ht_oper; } __packed; struct mwifiex_ie_types_2040bssco { diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 8f9382b9c3ca..bca8b6d52273 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -932,20 +932,20 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, /* Fill HT INFORMATION */ ht_info = (struct mwifiex_ie_types_htinfo *) pos; memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo)); - ht_info->header.type = cpu_to_le16(WLAN_EID_HT_INFORMATION); + ht_info->header.type = cpu_to_le16(WLAN_EID_HT_OPERATION); ht_info->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_info)); + cpu_to_le16(sizeof(struct ieee80211_ht_operation)); - ht_info->ht_info.control_chan = + ht_info->ht_oper.primary_chan = (u8) priv->curr_bss_params.bss_descriptor.channel; if (adapter->sec_chan_offset) { - ht_info->ht_info.ht_param = adapter->sec_chan_offset; - ht_info->ht_info.ht_param |= + ht_info->ht_oper.ht_param = adapter->sec_chan_offset; + ht_info->ht_oper.ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; } - ht_info->ht_info.operation_mode = + ht_info->ht_oper.operation_mode = cpu_to_le16(IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - ht_info->ht_info.basic_set[0] = 0xff; + ht_info->ht_oper.basic_set[0] = 0xff; pos += sizeof(struct mwifiex_ie_types_htinfo); cmd_append_size += sizeof(struct mwifiex_ie_types_htinfo); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 35225e9b1080..5ce9e7eabf5a 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -269,7 +269,7 @@ struct mwifiex_bssdescriptor { u8 disable_11n; struct ieee80211_ht_cap *bcn_ht_cap; u16 ht_cap_offset; - struct ieee80211_ht_info *bcn_ht_info; + struct ieee80211_ht_operation *bcn_ht_oper; u16 ht_info_offset; u8 *bcn_bss_co_2040; u16 bss_co_2040_offset; diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index aff9cd763f2b..5948905ff615 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1221,9 +1221,9 @@ mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, sizeof(struct ieee_types_header) - bss_entry->beacon_buf); break; - case WLAN_EID_HT_INFORMATION: - bss_entry->bcn_ht_info = (struct ieee80211_ht_info *) - (current_ptr + + case WLAN_EID_HT_OPERATION: + bss_entry->bcn_ht_oper = + (struct ieee80211_ht_operation *)(current_ptr + sizeof(struct ieee_types_header)); bss_entry->ht_info_offset = (u16) (current_ptr + sizeof(struct ieee_types_header) - @@ -1493,7 +1493,7 @@ mwifiex_update_curr_bss_params(struct mwifiex_private *priv, u8 *bssid, priv->curr_bss_params.bss_descriptor.bcn_ht_cap = NULL; priv->curr_bss_params.bss_descriptor.ht_cap_offset = 0; - priv->curr_bss_params.bss_descriptor.bcn_ht_info = NULL; + priv->curr_bss_params.bss_descriptor.bcn_ht_oper = NULL; priv->curr_bss_params.bss_descriptor.ht_info_offset = 0; priv->curr_bss_params.bss_descriptor.bcn_bss_co_2040 = @@ -2019,8 +2019,8 @@ mwifiex_save_curr_bcn(struct mwifiex_private *priv) (curr_bss->beacon_buf + curr_bss->ht_cap_offset); - if (curr_bss->bcn_ht_info) - curr_bss->bcn_ht_info = (struct ieee80211_ht_info *) + if (curr_bss->bcn_ht_oper) + curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *) (curr_bss->beacon_buf + curr_bss->ht_info_offset); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 39002363611e..b1555fb5815b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -232,7 +232,7 @@ static struct conf_drv_settings default_conf = { .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, }, [1] = { - .ie = WLAN_EID_HT_INFORMATION, + .ie = WLAN_EID_HT_OPERATION, .rule = CONF_BCN_RULE_PASS_ON_CHANGE, }, }, diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 210e2c325534..a8c1c46431ab 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1007,13 +1007,13 @@ enum ieee80211_min_mpdu_spacing { }; /** - * struct ieee80211_ht_info - HT information + * struct ieee80211_ht_operation - HT operation IE * - * This structure is the "HT information element" as - * described in 802.11n D5.0 7.3.2.58 + * This structure is the "HT operation element" as + * described in 802.11n-2009 7.3.2.57 */ -struct ieee80211_ht_info { - u8 control_chan; +struct ieee80211_ht_operation { + u8 primary_chan; u8 ht_param; __le16 operation_mode; __le16 stbc_param; @@ -1027,8 +1027,6 @@ struct ieee80211_ht_info { #define IEEE80211_HT_PARAM_CHA_SEC_BELOW 0x03 #define IEEE80211_HT_PARAM_CHAN_WIDTH_ANY 0x04 #define IEEE80211_HT_PARAM_RIFS_MODE 0x08 -#define IEEE80211_HT_PARAM_SPSMP_SUPPORT 0x10 -#define IEEE80211_HT_PARAM_SERV_INTERVAL_GRAN 0xE0 /* for operation_mode */ #define IEEE80211_HT_OP_MODE_PROTECTION 0x0003 @@ -1301,7 +1299,7 @@ enum ieee80211_eid { WLAN_EID_EXT_SUPP_RATES = 50, WLAN_EID_HT_CAPABILITY = 45, - WLAN_EID_HT_INFORMATION = 61, + WLAN_EID_HT_OPERATION = 61, WLAN_EID_RSN = 48, WLAN_EID_MMIE = 76, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 87d203ff7a8a..81cb66c3989e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -244,7 +244,7 @@ enum ieee80211_rssi_event { * @channel_type: Channel type for this BSS -- the hardware might be * configured for HT40+ while this BSS only uses no-HT, for * example. - * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info). + * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation. * This field is only valid when the channel type is one of the HT types. * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value * implies disabled diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 33fd8d9f714e..547cd7e3018a 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -160,13 +160,11 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, if (channel_type && sband->ht_cap.ht_supported) { pos = skb_put(skb, 4 + sizeof(struct ieee80211_ht_cap) + - sizeof(struct ieee80211_ht_info)); + sizeof(struct ieee80211_ht_operation)); pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); - pos = ieee80211_ie_build_ht_info(pos, - &sband->ht_cap, - chan, - channel_type); + pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, + chan, channel_type); } if (local->hw.queues >= 4) { @@ -441,13 +439,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, if (sta && elems->wmm_info) set_sta_flag(sta, WLAN_STA_WME); - if (sta && elems->ht_info_elem && elems->ht_cap_elem && + if (sta && elems->ht_operation && elems->ht_cap_elem && sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) { /* we both use HT */ struct ieee80211_sta_ht_cap sta_ht_cap_new; enum nl80211_channel_type channel_type = - ieee80211_ht_info_to_channel_type( - elems->ht_info_elem); + ieee80211_ht_oper_to_channel_type( + elems->ht_operation); ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems->ht_cap_elem, @@ -1063,7 +1061,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, 4 /* IBSS params */ + 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2 + sizeof(struct ieee80211_ht_cap) + - 2 + sizeof(struct ieee80211_ht_info) + + 2 + sizeof(struct ieee80211_ht_operation) + params->ie_len); if (!skb) return -ENOMEM; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d9798a307f20..0ae822c47930 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -397,7 +397,7 @@ struct ieee80211_mgd_auth_data { struct ieee80211_mgd_assoc_data { struct cfg80211_bss *bss; const u8 *supp_rates; - const u8 *ht_information_ie; + const u8 *ht_operation_ie; unsigned long timeout; int tries; @@ -1117,7 +1117,7 @@ struct ieee802_11_elems { u8 *wmm_info; u8 *wmm_param; struct ieee80211_ht_cap *ht_cap_elem; - struct ieee80211_ht_info *ht_info_elem; + struct ieee80211_ht_operation *ht_operation; struct ieee80211_meshconf_ie *mesh_config; u8 *mesh_id; u8 *peering; @@ -1470,10 +1470,9 @@ size_t ieee80211_ie_split(const u8 *ies, size_t ielen, size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap); -u8 *ieee80211_ie_build_ht_info(u8 *pos, - struct ieee80211_sta_ht_cap *ht_cap, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type); +u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type); /* internal work items */ void ieee80211_work_init(struct ieee80211_local *local); @@ -1501,7 +1500,7 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum nl80211_channel_type chantype); enum nl80211_channel_type -ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info); +ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper); enum nl80211_channel_type ieee80211_get_tx_channel_type( struct ieee80211_local *local, enum nl80211_channel_type channel_type); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index e5fbb7cf3562..b05fa9ef866c 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -98,9 +98,9 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat goto mismatch; /* disallow peering with mismatched channel types for now */ - if (ie->ht_info_elem && + if (ie->ht_operation && (local->_oper_channel_type != - ieee80211_ht_info_to_channel_type(ie->ht_info_elem))) + ieee80211_ht_oper_to_channel_type(ie->ht_operation))) goto mismatch; return true; @@ -371,7 +371,7 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb, return 0; } -int mesh_add_ht_info_ie(struct sk_buff *skb, +int mesh_add_ht_oper_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -385,11 +385,11 @@ int mesh_add_ht_info_ie(struct sk_buff *skb, if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT) return 0; - if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_info)) + if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_operation)) return -ENOMEM; - pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_info)); - ieee80211_ie_build_ht_info(pos, ht_cap, channel, channel_type); + pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); + ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type); return 0; } diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 8d53b71378e3..2bd5d8b864f6 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -220,7 +220,7 @@ int mesh_add_ds_params_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_ht_cap_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); -int mesh_add_ht_info_ie(struct sk_buff *skb, +int mesh_add_ht_oper_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 4e53c4cbca9e..923269b62b43 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -187,7 +187,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, 2 + sdata->u.mesh.mesh_id_len + 2 + sizeof(struct ieee80211_meshconf_ie) + 2 + sizeof(struct ieee80211_ht_cap) + - 2 + sizeof(struct ieee80211_ht_info) + + 2 + sizeof(struct ieee80211_ht_operation) + 2 + 8 + /* peering IE */ sdata->u.mesh.ie_len); if (!skb) @@ -263,7 +263,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, if (action != WLAN_SP_MESH_PEERING_CLOSE) { if (mesh_add_ht_cap_ie(skb, sdata) || - mesh_add_ht_info_ie(skb, sdata)) + mesh_add_ht_oper_ie(skb, sdata)) return -1; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f733aeb50e85..c59bc509ed6f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -177,7 +177,7 @@ static int ecw2cw(int ecw) * HT abilities for a specific band. */ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, - struct ieee80211_ht_info *hti, + struct ieee80211_ht_operation *ht_oper, const u8 *bssid, u16 ap_ht_cap_flags, bool beacon_htcap_ie) { @@ -185,7 +185,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband; struct sta_info *sta; u32 changed = 0; - int hti_cfreq; + int ht_cfreq; u16 ht_opmode; bool enable_ht = true; enum nl80211_channel_type prev_chantype; @@ -196,10 +196,10 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, prev_chantype = sdata->vif.bss_conf.channel_type; - hti_cfreq = ieee80211_channel_to_frequency(hti->control_chan, - sband->band); + ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, + sband->band); /* check that channel matches the right operating channel */ - if (local->hw.conf.channel->center_freq != hti_cfreq) { + if (local->hw.conf.channel->center_freq != ht_cfreq) { /* Some APs mess this up, evidently. * Netgear WNDR3700 sometimes reports 4 higher than * the actual channel, for instance. @@ -207,11 +207,11 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: Wrong control channel in association" " response: configured center-freq: %d" - " hti-cfreq: %d hti->control_chan: %d" + " ht-cfreq: %d ht->control_chan: %d" " band: %d. Disabling HT.\n", sdata->name, local->hw.conf.channel->center_freq, - hti_cfreq, hti->control_chan, + ht_cfreq, ht_oper->primary_chan, sband->band); enable_ht = false; } @@ -222,8 +222,9 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) && !ieee80111_cfg_override_disables_ht40(sdata) && (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && - (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { - switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + (ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) { + switch (ht_oper->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: rx_channel_type = NL80211_CHAN_HT40PLUS; break; @@ -278,7 +279,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); } - ht_opmode = le16_to_cpu(hti->operation_mode); + ht_opmode = le16_to_cpu(ht_oper->operation_mode); /* if bss configuration changed store the new one */ if (sdata->ht_opmode_valid != enable_ht || @@ -316,12 +317,12 @@ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len, } static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, const u8 *ht_info_ie, + struct sk_buff *skb, const u8 *ht_oper_ie, struct ieee80211_supported_band *sband, struct ieee80211_channel *channel, enum ieee80211_smps_mode smps) { - struct ieee80211_ht_info *ht_info; + struct ieee80211_ht_operation *ht_oper; u8 *pos; u32 flags = channel->flags; u16 cap; @@ -329,21 +330,21 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap)); - if (!ht_info_ie) + if (!ht_oper_ie) return; - if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info)) + if (ht_oper_ie[1] < sizeof(struct ieee80211_ht_operation)) return; memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); ieee80211_apply_htcap_overrides(sdata, &ht_cap); - ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2); + ht_oper = (struct ieee80211_ht_operation *)(ht_oper_ie + 2); /* determine capability flags */ cap = ht_cap.cap; - switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: if (flags & IEEE80211_CHAN_NO_HT40PLUS) { cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; @@ -557,7 +558,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) } if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) - ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_information_ie, + ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_operation_ie, sband, local->oper_channel, ifmgd->ap_smps); /* if present, add any custom non-vendor IEs that go after HT */ @@ -2094,9 +2095,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, ieee80211_set_wmm_default(sdata, false); changed |= BSS_CHANGED_QOS; - if (elems.ht_info_elem && elems.wmm_param && + if (elems.ht_operation && elems.wmm_param && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) - changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, + changed |= ieee80211_enable_ht(sdata, elems.ht_operation, cbss->bssid, ap_ht_cap_flags, false); @@ -2321,7 +2322,7 @@ static const u64 care_about_ies = (1ULL << WLAN_EID_CHANNEL_SWITCH) | (1ULL << WLAN_EID_PWR_CONSTRAINT) | (1ULL << WLAN_EID_HT_CAPABILITY) | - (1ULL << WLAN_EID_HT_INFORMATION); + (1ULL << WLAN_EID_HT_OPERATION); static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, @@ -2506,7 +2507,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, erp_valid, erp_value); - if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param && + if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) { struct sta_info *sta; struct ieee80211_supported_band *sband; @@ -2529,7 +2530,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); - changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, + changed |= ieee80211_enable_ht(sdata, elems.ht_operation, bssid, ap_ht_cap_flags, true); } @@ -3339,8 +3340,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, assoc_data->wmm = bss->wmm_used && (local->hw.queues >= 4); assoc_data->supp_rates = bss->supp_rates; assoc_data->supp_rates_len = bss->supp_rates_len; - assoc_data->ht_information_ie = - ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION); + assoc_data->ht_operation_ie = + ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION); if (bss->wmm_used && bss->uapsd_supported && (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 782a60198df4..a9b27273320e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2390,7 +2390,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, 2 + 3 + /* DS params */ 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2 + sizeof(struct ieee80211_ht_cap) + - 2 + sizeof(struct ieee80211_ht_info) + + 2 + sizeof(struct ieee80211_ht_operation) + 2 + sdata->u.mesh.mesh_id_len + 2 + sizeof(struct ieee80211_meshconf_ie) + sdata->u.mesh.ie_len); @@ -2419,7 +2419,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, ieee80211_add_ext_srates_ie(&sdata->vif, skb) || mesh_add_rsn_ie(skb, sdata) || mesh_add_ht_cap_ie(skb, sdata) || - mesh_add_ht_info_ie(skb, sdata) || + mesh_add_ht_oper_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata) || mesh_add_vendor_ies(skb, sdata)) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 32f7a3b3d43c..5e23cf6389d0 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -684,9 +684,9 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, else elem_parse_failed = true; break; - case WLAN_EID_HT_INFORMATION: - if (elen >= sizeof(struct ieee80211_ht_info)) - elems->ht_info_elem = (void *)pos; + case WLAN_EID_HT_OPERATION: + if (elen >= sizeof(struct ieee80211_ht_operation)) + elems->ht_operation = (void *)pos; else elem_parse_failed = true; break; @@ -1611,57 +1611,56 @@ u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, return pos; } -u8 *ieee80211_ie_build_ht_info(u8 *pos, - struct ieee80211_sta_ht_cap *ht_cap, +u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type) { - struct ieee80211_ht_info *ht_info; + struct ieee80211_ht_operation *ht_oper; /* Build HT Information */ - *pos++ = WLAN_EID_HT_INFORMATION; - *pos++ = sizeof(struct ieee80211_ht_info); - ht_info = (struct ieee80211_ht_info *)pos; - ht_info->control_chan = + *pos++ = WLAN_EID_HT_OPERATION; + *pos++ = sizeof(struct ieee80211_ht_operation); + ht_oper = (struct ieee80211_ht_operation *)pos; + ht_oper->primary_chan = ieee80211_frequency_to_channel(channel->center_freq); switch (channel_type) { case NL80211_CHAN_HT40MINUS: - ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; break; case NL80211_CHAN_HT40PLUS: - ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; break; case NL80211_CHAN_HT20: default: - ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; + ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; break; } if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) - ht_info->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; + ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; /* * Note: According to 802.11n-2009 9.13.3.1, HT Protection field and * RIFS Mode are reserved in IBSS mode, therefore keep them at 0 */ - ht_info->operation_mode = 0x0000; - ht_info->stbc_param = 0x0000; + ht_oper->operation_mode = 0x0000; + ht_oper->stbc_param = 0x0000; /* It seems that Basic MCS set and Supported MCS set are identical for the first 10 bytes */ - memset(&ht_info->basic_set, 0, 16); - memcpy(&ht_info->basic_set, &ht_cap->mcs, 10); + memset(&ht_oper->basic_set, 0, 16); + memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10); - return pos + sizeof(struct ieee80211_ht_info); + return pos + sizeof(struct ieee80211_ht_operation); } enum nl80211_channel_type -ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info) +ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper) { enum nl80211_channel_type channel_type; - if (!ht_info) + if (!ht_oper) return NL80211_CHAN_NO_HT; - switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_NONE: channel_type = NL80211_CHAN_HT20; break; -- cgit v1.2.3 From 292c41acddfdbe0fb42d4c4ad9b896168fd16e91 Mon Sep 17 00:00:00 2001 From: Chun-Yeow Yeoh Date: Mon, 19 Mar 2012 21:38:46 +0800 Subject: mac80211: fix the sparse warnings on endian handling in RANN propagation The HWMP sequence number of received RANN element is compared to decide whether to be propagated. The sequence number is required to covert from 32bit little endian data into CPUs endianness for comparison. The same applies to the RANN metric. Signed-off-by: Chun-Yeow Yeoh Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 6 +++--- net/mac80211/mesh_hwmp.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index a8c1c46431ab..09301b0768d0 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -640,9 +640,9 @@ struct ieee80211_rann_ie { u8 rann_hopcount; u8 rann_ttl; u8 rann_addr[6]; - u32 rann_seq; - u32 rann_interval; - u32 rann_metric; + __le32 rann_seq; + __le32 rann_interval; + __le32 rann_metric; } __attribute__ ((packed)); enum ieee80211_rann_flags { diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 1c6f3d02aebf..f80a9e3da359 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -748,10 +748,10 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata, flags = rann->rann_flags; root_is_gate = !!(flags & RANN_FLAG_IS_GATE); orig_addr = rann->rann_addr; - orig_sn = rann->rann_seq; + orig_sn = le32_to_cpu(rann->rann_seq); hopcount = rann->rann_hopcount; hopcount++; - metric = rann->rann_metric; + metric = le32_to_cpu(rann->rann_metric); /* Ignore our own RANNs */ if (compare_ether_addr(orig_addr, sdata->vif.addr) == 0) -- cgit v1.2.3 From d3c242e1f22f5dfed009296ee45ce896153f0b53 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:29 -0600 Subject: regmap: allow regmap instances to be named Some devices have multiple separate register regions. Logically, one regmap would be created per region. One issue that prevents this is that each instance will attempt to create the same debugfs files. Avoid this by allowing regmaps to be named, and use the name to construct the debugfs directory name. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 3 ++- drivers/base/regmap/regmap-debugfs.c | 14 +++++++++++--- drivers/base/regmap/regmap.c | 4 ++-- include/linux/regmap.h | 5 +++++ 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index fcafc5b2e651..6beef6691c47 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -41,6 +41,7 @@ struct regmap { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; + const char *debugfs_name; #endif unsigned int max_register; @@ -101,7 +102,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, #ifdef CONFIG_DEBUG_FS extern void regmap_debugfs_initcall(void); -extern void regmap_debugfs_init(struct regmap *map); +extern void regmap_debugfs_init(struct regmap *map, const char *name); extern void regmap_debugfs_exit(struct regmap *map); #else static inline void regmap_debugfs_initcall(void) { } diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 58517a5dac13..9715e8e44506 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -248,10 +248,17 @@ static const struct file_operations regmap_access_fops = { .llseek = default_llseek, }; -void regmap_debugfs_init(struct regmap *map) +void regmap_debugfs_init(struct regmap *map, const char *name) { - map->debugfs = debugfs_create_dir(dev_name(map->dev), - regmap_debugfs_root); + if (name) { + map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", + dev_name(map->dev), name); + name = map->debugfs_name; + } else { + name = dev_name(map->dev); + } + + map->debugfs = debugfs_create_dir(name, regmap_debugfs_root); if (!map->debugfs) { dev_warn(map->dev, "Failed to create debugfs directory\n"); return; @@ -280,6 +287,7 @@ void regmap_debugfs_init(struct regmap *map) void regmap_debugfs_exit(struct regmap *map) { debugfs_remove_recursive(map->debugfs); + kfree(map->debugfs_name); } void regmap_debugfs_initcall(void) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 7a3f535e481c..b1dad1f9c47d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -289,7 +289,7 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } - regmap_debugfs_init(map); + regmap_debugfs_init(map, config->name); ret = regcache_init(map, config); if (ret < 0) @@ -372,7 +372,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; - regmap_debugfs_init(map); + regmap_debugfs_init(map, config->name); map->cache_bypass = false; map->cache_only = false; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a90abb6bfa64..0a27ee809ca1 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -46,6 +46,9 @@ struct reg_default { /** * Configuration for the register map of a device. * + * @name: Optional name of the regmap. Useful when a device has multiple + * register regions. + * * @reg_bits: Number of bits in a register address, mandatory. * @pad_bits: Number of bits of padding between register and value. * @val_bits: Number of bits in a register value, mandatory. @@ -77,6 +80,8 @@ struct reg_default { * @num_reg_defaults_raw: Number of elements in reg_defaults_raw. */ struct regmap_config { + const char *name; + int reg_bits; int pad_bits; int val_bits; -- cgit v1.2.3 From f01ee60fffa4dc6c77122121233a793f7f696e67 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 9 Apr 2012 13:40:24 -0600 Subject: regmap: implement register striding regmap_config.reg_stride is introduced. All extant register addresses are a multiple of this value. Users of serial-oriented regmap busses will typically set this to 1. Users of the MMIO regmap bus will typically set this based on the value size of their registers, in bytes, so 4 for a 32-bit register. Throughout the regmap code, actual register addresses are used. Wherever the register address is used to index some array of values, the address is divided by the stride to determine the index, or vice-versa. Error- checking is added to all entry-points for register address data to ensure that register addresses actually satisfy the specified stride. The MMIO bus ensures that the specified stride is large enough for the register size. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache-lzo.c | 11 +++++----- drivers/base/regmap/regcache-rbtree.c | 40 ++++++++++++++++++++--------------- drivers/base/regmap/regcache.c | 14 +++++++++--- drivers/base/regmap/regmap-debugfs.c | 4 ++-- drivers/base/regmap/regmap-irq.c | 34 +++++++++++++++++++---------- drivers/base/regmap/regmap-mmio.c | 13 ++++++++++++ drivers/base/regmap/regmap.c | 30 ++++++++++++++++++++++---- include/linux/regmap.h | 4 ++++ 9 files changed, 109 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 99b28fffbd0e..d92e9b1cb83c 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -62,6 +62,7 @@ struct regmap { /* number of bits to (left) shift the reg value when formatting*/ int reg_shift; + int reg_stride; /* regcache specific members */ const struct regcache_ops *cache_ops; diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 483b06d4a380..afd6aa91a0df 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -108,7 +108,7 @@ static int regcache_lzo_decompress_cache_block(struct regmap *map, static inline int regcache_lzo_get_blkindex(struct regmap *map, unsigned int reg) { - return (reg * map->cache_word_size) / + return ((reg / map->reg_stride) * map->cache_word_size) / DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count(map)); } @@ -116,9 +116,10 @@ static inline int regcache_lzo_get_blkindex(struct regmap *map, static inline int regcache_lzo_get_blkpos(struct regmap *map, unsigned int reg) { - return reg % (DIV_ROUND_UP(map->cache_size_raw, - regcache_lzo_block_count(map)) / - map->cache_word_size); + return (reg / map->reg_stride) % + (DIV_ROUND_UP(map->cache_size_raw, + regcache_lzo_block_count(map)) / + map->cache_word_size); } static inline int regcache_lzo_get_blksize(struct regmap *map) @@ -322,7 +323,7 @@ static int regcache_lzo_write(struct regmap *map, } /* set the bit so we know we have to sync this register */ - set_bit(reg, lzo_block->sync_bmp); + set_bit(reg / map->reg_stride, lzo_block->sync_bmp); kfree(tmp_dst); kfree(lzo_block->src); return 0; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index e49e71fab184..e6732cf7c06e 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -39,11 +39,12 @@ struct regcache_rbtree_ctx { }; static inline void regcache_rbtree_get_base_top_reg( + struct regmap *map, struct regcache_rbtree_node *rbnode, unsigned int *base, unsigned int *top) { *base = rbnode->base_reg; - *top = rbnode->base_reg + rbnode->blklen - 1; + *top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride); } static unsigned int regcache_rbtree_get_register( @@ -70,7 +71,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, rbnode = rbtree_ctx->cached_rbnode; if (rbnode) { - regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); if (reg >= base_reg && reg <= top_reg) return rbnode; } @@ -78,7 +80,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, node = rbtree_ctx->root.rb_node; while (node) { rbnode = container_of(node, struct regcache_rbtree_node, node); - regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); if (reg >= base_reg && reg <= top_reg) { rbtree_ctx->cached_rbnode = rbnode; return rbnode; @@ -92,7 +95,7 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, return NULL; } -static int regcache_rbtree_insert(struct rb_root *root, +static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root, struct regcache_rbtree_node *rbnode) { struct rb_node **new, *parent; @@ -106,7 +109,7 @@ static int regcache_rbtree_insert(struct rb_root *root, rbnode_tmp = container_of(*new, struct regcache_rbtree_node, node); /* base and top registers of the current rbnode */ - regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp, + regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp, &top_reg_tmp); /* base register of the rbnode to be added */ base_reg = rbnode->base_reg; @@ -138,7 +141,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) unsigned int base, top; int nodes = 0; int registers = 0; - int average; + int this_registers, average; map->lock(map); @@ -146,11 +149,12 @@ static int rbtree_show(struct seq_file *s, void *ignored) node = rb_next(node)) { n = container_of(node, struct regcache_rbtree_node, node); - regcache_rbtree_get_base_top_reg(n, &base, &top); - seq_printf(s, "%x-%x (%d)\n", base, top, top - base + 1); + regcache_rbtree_get_base_top_reg(map, n, &base, &top); + this_registers = ((top - base) / map->reg_stride) + 1; + seq_printf(s, "%x-%x (%d)\n", base, top, this_registers); nodes++; - registers += top - base + 1; + registers += this_registers; } if (nodes) @@ -255,7 +259,7 @@ static int regcache_rbtree_read(struct regmap *map, rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { - reg_tmp = reg - rbnode->base_reg; + reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; *value = regcache_rbtree_get_register(rbnode, reg_tmp, map->cache_word_size); } else { @@ -310,7 +314,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, */ rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { - reg_tmp = reg - rbnode->base_reg; + reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; val = regcache_rbtree_get_register(rbnode, reg_tmp, map->cache_word_size); if (val == value) @@ -321,13 +325,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, /* look for an adjacent register to the one we are about to add */ for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { - rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node); + rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, + node); for (i = 0; i < rbnode_tmp->blklen; i++) { - reg_tmp = rbnode_tmp->base_reg + i; - if (abs(reg_tmp - reg) != 1) + reg_tmp = rbnode_tmp->base_reg + + (i * map->reg_stride); + if (abs(reg_tmp - reg) != map->reg_stride) continue; /* decide where in the block to place our register */ - if (reg_tmp + 1 == reg) + if (reg_tmp + map->reg_stride == reg) pos = i + 1; else pos = i; @@ -357,7 +363,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, return -ENOMEM; } regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size); - regcache_rbtree_insert(&rbtree_ctx->root, rbnode); + regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode); rbtree_ctx->cached_rbnode = rbnode; } @@ -397,7 +403,7 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, end = rbnode->blklen; for (i = base; i < end; i++) { - regtmp = rbnode->base_reg + i; + regtmp = rbnode->base_reg + (i * map->reg_stride); val = regcache_rbtree_get_register(rbnode, i, map->cache_word_size); diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index d4368e8b6f9d..835883bda977 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -59,7 +59,7 @@ static int regcache_hw_init(struct regmap *map) for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (regmap_volatile(map, i)) + if (regmap_volatile(map, i * map->reg_stride)) continue; count++; } @@ -76,9 +76,9 @@ static int regcache_hw_init(struct regmap *map) for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (regmap_volatile(map, i)) + if (regmap_volatile(map, i * map->reg_stride)) continue; - map->reg_defaults[j].reg = i; + map->reg_defaults[j].reg = i * map->reg_stride; map->reg_defaults[j].def = val; j++; } @@ -98,6 +98,10 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) int i; void *tmp_buf; + for (i = 0; i < config->num_reg_defaults; i++) + if (config->reg_defaults[i].reg % map->reg_stride) + return -EINVAL; + if (map->cache_type == REGCACHE_NONE) { map->cache_bypass = true; return 0; @@ -278,6 +282,10 @@ int regcache_sync(struct regmap *map) /* Apply any patch first */ map->cache_bypass = 1; for (i = 0; i < map->patch_regs; i++) { + if (map->patch[i].reg % map->reg_stride) { + ret = -EINVAL; + goto out; + } ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); if (ret != 0) { dev_err(map->dev, "Failed to write %x = %x: %d\n", diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index df97c93efa8e..bb1ff175b962 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -80,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, val_len = 2 * map->format.val_bytes; tot_len = reg_len + val_len + 3; /* : \n */ - for (i = 0; i < map->max_register + 1; i++) { + for (i = 0; i <= map->max_register; i += map->reg_stride) { if (!regmap_readable(map, i)) continue; @@ -197,7 +197,7 @@ static ssize_t regmap_access_read_file(struct file *file, reg_len = regmap_calc_reg_len(map->max_register, buf, count); tot_len = reg_len + 10; /* ': R W V P\n' */ - for (i = 0; i < map->max_register + 1; i++) { + for (i = 0; i <= map->max_register; i += map->reg_stride) { /* Ignore registers which are neither readable nor writable */ if (!regmap_readable(map, i) && !regmap_writeable(map, i)) continue; diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 1befaa7a31cb..56b8136eb36a 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -58,11 +58,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data) * suppress pointless writes. */ for (i = 0; i < d->chip->num_regs; i++) { - ret = regmap_update_bits(d->map, d->chip->mask_base + i, + ret = regmap_update_bits(d->map, d->chip->mask_base + + (i * map->map->reg_stride), d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", - d->chip->mask_base + i); + d->chip->mask_base + (i * map->reg_stride)); } mutex_unlock(&d->lock); @@ -73,7 +74,7 @@ static void regmap_irq_enable(struct irq_data *data) struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); - d->mask_buf[irq_data->reg_offset] &= ~irq_data->mask; + d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; } static void regmap_irq_disable(struct irq_data *data) @@ -81,7 +82,7 @@ static void regmap_irq_disable(struct irq_data *data) struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); - d->mask_buf[irq_data->reg_offset] |= irq_data->mask; + d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; } static struct irq_chip regmap_irq_chip = { @@ -136,17 +137,19 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) data->status_buf[i] &= ~data->mask_buf[i]; if (data->status_buf[i] && chip->ack_base) { - ret = regmap_write(map, chip->ack_base + i, + ret = regmap_write(map, chip->ack_base + + (i * map->reg_stride), data->status_buf[i]); if (ret != 0) dev_err(map->dev, "Failed to ack 0x%x: %d\n", - chip->ack_base + i, ret); + chip->ack_base + (i * map->reg_stride), + ret); } } for (i = 0; i < chip->num_irqs; i++) { - if (data->status_buf[chip->irqs[i].reg_offset] & - chip->irqs[i].mask) { + if (data->status_buf[chip->irqs[i].reg_offset / + map->reg_stride] & chip->irqs[i].mask) { handle_nested_irq(data->irq_base + i); handled = true; } @@ -181,6 +184,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, int cur_irq, i; int ret = -ENOMEM; + for (i = 0; i < chip->num_irqs; i++) { + if (chip->irqs[i].reg_offset % map->reg_stride) + return -EINVAL; + if (chip->irqs[i].reg_offset / map->reg_stride >= + chip->num_regs) + return -EINVAL; + } + irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); if (irq_base < 0) { dev_warn(map->dev, "Failed to allocate IRQs: %d\n", @@ -218,16 +229,17 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, mutex_init(&d->lock); for (i = 0; i < chip->num_irqs; i++) - d->mask_buf_def[chip->irqs[i].reg_offset] + d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] |= chip->irqs[i].mask; /* Mask all the interrupts by default */ for (i = 0; i < chip->num_regs; i++) { d->mask_buf[i] = d->mask_buf_def[i]; - ret = regmap_write(map, chip->mask_base + i, d->mask_buf[i]); + ret = regmap_write(map, chip->mask_base + (i * map->reg_stride), + d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", - chip->mask_base + i, ret); + chip->mask_base + (i * map->reg_stride), ret); goto err_alloc; } } diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index bdf4dc865293..febd6de6c8ac 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -130,6 +130,7 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, const struct regmap_config *config) { struct regmap_mmio_context *ctx; + int min_stride; if (config->reg_bits != 32) return ERR_PTR(-EINVAL); @@ -139,16 +140,28 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, switch (config->val_bits) { case 8: + /* The core treats 0 as 1 */ + min_stride = 0; + break; case 16: + min_stride = 2; + break; case 32: + min_stride = 4; + break; #ifdef CONFIG_64BIT case 64: + min_stride = 8; + break; #endif break; default: return ERR_PTR(-EINVAL); } + if (config->reg_stride < min_stride) + return ERR_PTR(-EINVAL); + ctx = kzalloc(GFP_KERNEL, sizeof(*ctx)); if (!ctx) return ERR_PTR(-ENOMEM); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 40f910162781..8a25006b2a4d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -243,6 +243,10 @@ struct regmap *regmap_init(struct device *dev, map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); map->format.buf_size += map->format.pad_bytes; map->reg_shift = config->pad_bits % 8; + if (config->reg_stride) + map->reg_stride = config->reg_stride; + else + map->reg_stride = 1; map->dev = dev; map->bus = bus; map->bus_context = bus_context; @@ -469,7 +473,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, /* Check for unwritable registers before we start */ if (map->writeable_reg) for (i = 0; i < val_len / map->format.val_bytes; i++) - if (!map->writeable_reg(map->dev, reg + i)) + if (!map->writeable_reg(map->dev, + reg + (i * map->reg_stride))) return -EINVAL; if (!map->cache_bypass && map->format.parse_val) { @@ -478,7 +483,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, for (i = 0; i < val_len / val_bytes; i++) { memcpy(map->work_buf, val + (i * val_bytes), val_bytes); ival = map->format.parse_val(map->work_buf); - ret = regcache_write(map, reg + i, ival); + ret = regcache_write(map, reg + (i * map->reg_stride), + ival); if (ret) { dev_err(map->dev, "Error in caching of register: %u ret: %d\n", @@ -590,6 +596,9 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; + if (reg % map->reg_stride) + return -EINVAL; + map->lock(map); ret = _regmap_write(map, reg, val); @@ -623,6 +632,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, if (val_len % map->format.val_bytes) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; map->lock(map); @@ -657,6 +668,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (!map->format.parse_val) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; map->lock(map); @@ -753,6 +766,9 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; + if (reg % map->reg_stride) + return -EINVAL; + map->lock(map); ret = _regmap_read(map, reg, val); @@ -784,6 +800,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (val_len % map->format.val_bytes) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; map->lock(map); @@ -797,7 +815,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, * cost as we expect to hit the cache. */ for (i = 0; i < val_count; i++) { - ret = _regmap_read(map, reg + i, &v); + ret = _regmap_read(map, reg + (i * map->reg_stride), + &v); if (ret != 0) goto out; @@ -832,6 +851,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, if (!map->format.parse_val) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; if (vol || map->cache_type == REGCACHE_NONE) { ret = regmap_raw_read(map, reg, val, val_bytes * val_count); @@ -842,7 +863,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, map->format.parse_val(val + i); } else { for (i = 0; i < val_count; i++) { - ret = regmap_read(map, reg + i, val + (i * val_bytes)); + ret = regmap_read(map, reg + (i * map->reg_stride), + val + (i * val_bytes)); if (ret != 0) return ret; } diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 680ddd7de60e..0258bcd6258d 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -50,6 +50,9 @@ struct reg_default { * register regions. * * @reg_bits: Number of bits in a register address, mandatory. + * @reg_stride: The register address stride. Valid register addresses are a + * multiple of this value. If set to 0, a value of 1 will be + * used. * @pad_bits: Number of bits of padding between register and value. * @val_bits: Number of bits in a register value, mandatory. * @@ -83,6 +86,7 @@ struct regmap_config { const char *name; int reg_bits; + int reg_stride; int pad_bits; int val_bits; -- cgit v1.2.3 From ce580fe5190dec4d872e7925946b0aec1f694370 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 4 Aug 2011 13:51:11 -0300 Subject: [media] v4l: Introduce integer menu controls Create a new control type called V4L2_CTRL_TYPE_INTEGER_MENU. Integer menu controls are just like menu controls but the menu items are 64-bit integers rather than strings. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Tested-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ctrls.c | 74 +++++++++++++++++++++++++++++++--------- include/linux/videodev2.h | 6 +++- include/media/v4l2-ctrls.h | 6 +++- 3 files changed, 67 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 18015c0a8d31..3e0a72dec994 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -852,7 +852,8 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change ev->u.ctrl.value64 = ctrl->cur.val64; ev->u.ctrl.minimum = ctrl->minimum; ev->u.ctrl.maximum = ctrl->maximum; - if (ctrl->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU + || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) ev->u.ctrl.step = 1; else ev->u.ctrl.step = ctrl->step; @@ -1083,10 +1084,13 @@ static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval) return 0; case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: if (val < ctrl->minimum || val > ctrl->maximum) return -ERANGE; - if (ctrl->qmenu[val][0] == '\0' || - (ctrl->menu_skip_mask & (1 << val))) + if (ctrl->menu_skip_mask & (1 << val)) + return -EINVAL; + if (ctrl->type == V4L2_CTRL_TYPE_MENU && + ctrl->qmenu[val][0] == '\0') return -EINVAL; return 0; @@ -1114,6 +1118,7 @@ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: @@ -1343,7 +1348,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, s32 min, s32 max, u32 step, s32 def, - u32 flags, const char * const *qmenu, void *priv) + u32 flags, const char * const *qmenu, + const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; unsigned sz_extra = 0; @@ -1356,6 +1362,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, (type == V4L2_CTRL_TYPE_INTEGER && step == 0) || (type == V4L2_CTRL_TYPE_BITMASK && max == 0) || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || + (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) || (type == V4L2_CTRL_TYPE_STRING && max == 0)) { handler_set_err(hdl, -ERANGE); return NULL; @@ -1366,6 +1373,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } if ((type == V4L2_CTRL_TYPE_INTEGER || type == V4L2_CTRL_TYPE_MENU || + type == V4L2_CTRL_TYPE_INTEGER_MENU || type == V4L2_CTRL_TYPE_BOOLEAN) && (def < min || def > max)) { handler_set_err(hdl, -ERANGE); @@ -1400,7 +1408,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; - ctrl->qmenu = qmenu; + if (type == V4L2_CTRL_TYPE_MENU) + ctrl->qmenu = qmenu; + else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) + ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; ctrl->cur.val = ctrl->val = ctrl->default_value = def; @@ -1427,6 +1438,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl; const char *name = cfg->name; const char * const *qmenu = cfg->qmenu; + const s64 *qmenu_int = cfg->qmenu_int; enum v4l2_ctrl_type type = cfg->type; u32 flags = cfg->flags; s32 min = cfg->min; @@ -1438,18 +1450,24 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, &def, &flags); - is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU); + is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU || + cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU); if (is_menu) WARN_ON(step); else WARN_ON(cfg->menu_skip_mask); - if (is_menu && qmenu == NULL) + if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) qmenu = v4l2_ctrl_get_menu(cfg->id); + else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU && + qmenu_int == NULL) { + handler_set_err(hdl, -EINVAL); + return NULL; + } ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, - def, flags, qmenu, priv); + def, flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; return ctrl; @@ -1466,12 +1484,13 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type == V4L2_CTRL_TYPE_MENU) { + if (type == V4L2_CTRL_TYPE_MENU + || type == V4L2_CTRL_TYPE_INTEGER_MENU) { handler_set_err(hdl, -EINVAL); return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - min, max, step, def, flags, NULL, NULL); + min, max, step, def, flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1493,7 +1512,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, NULL); + 0, max, mask, def, flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -1659,6 +1678,9 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_MENU: printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]); break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]); + break; case V4L2_CTRL_TYPE_BITMASK: printk(KERN_CONT "0x%08x", ctrl->cur.val); break; @@ -1795,7 +1817,8 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; - if (ctrl->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU + || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) qc->step = 1; else qc->step = ctrl->step; @@ -1825,16 +1848,33 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm) qm->reserved = 0; /* Sanity checks */ - if (ctrl->qmenu == NULL || - i < ctrl->minimum || i > ctrl->maximum) + switch (ctrl->type) { + case V4L2_CTRL_TYPE_MENU: + if (ctrl->qmenu == NULL) + return -EINVAL; + break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + if (ctrl->qmenu_int == NULL) + return -EINVAL; + break; + default: + return -EINVAL; + } + + if (i < ctrl->minimum || i > ctrl->maximum) return -EINVAL; + /* Use mask to see if this menu item should be skipped */ if (ctrl->menu_skip_mask & (1 << i)) return -EINVAL; /* Empty menu items should also be skipped */ - if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') - return -EINVAL; - strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + if (ctrl->type == V4L2_CTRL_TYPE_MENU) { + if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') + return -EINVAL; + strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + } else { + qm->value = ctrl->qmenu_int[i]; + } return 0; } EXPORT_SYMBOL(v4l2_querymenu); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index c9c9a4680cc5..e69cacc9e9ea 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1151,6 +1151,7 @@ enum v4l2_ctrl_type { V4L2_CTRL_TYPE_CTRL_CLASS = 6, V4L2_CTRL_TYPE_STRING = 7, V4L2_CTRL_TYPE_BITMASK = 8, + V4L2_CTRL_TYPE_INTEGER_MENU = 9, }; /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ @@ -1170,7 +1171,10 @@ struct v4l2_queryctrl { struct v4l2_querymenu { __u32 id; __u32 index; - __u8 name[32]; /* Whatever */ + union { + __u8 name[32]; /* Whatever */ + __s64 value; + }; __u32 reserved; }; diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 3dbd06638506..533315bd74e0 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -130,7 +130,10 @@ struct v4l2_ctrl { u32 step; u32 menu_skip_mask; }; - const char * const *qmenu; + union { + const char * const *qmenu; + const s64 *qmenu_int; + }; unsigned long flags; union { s32 val; @@ -220,6 +223,7 @@ struct v4l2_ctrl_config { u32 flags; u32 menu_skip_mask; const char * const *qmenu; + const s64 *qmenu_int; unsigned int is_private:1; }; -- cgit v1.2.3 From ae184cda8d0eebfea6cf217abc3f94a7cfffe24d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 14 Oct 2011 14:14:26 -0300 Subject: [media] v4l: VIDIOC_SUBDEV_S_SELECTION and VIDIOC_SUBDEV_G_SELECTION IOCTLs Add support for VIDIOC_SUBDEV_S_SELECTION and VIDIOC_SUBDEV_G_SELECTION IOCTLs. They replace functionality provided by VIDIOC_SUBDEV_S_CROP and VIDIOC_SUBDEV_G_CROP IOCTLs and also add new functionality (composing). VIDIOC_SUBDEV_G_CROP and VIDIOC_SUBDEV_S_CROP continue to be supported. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-subdev.c | 42 +++++++++++++++++++++++++++++---------- include/linux/v4l2-subdev.h | 41 ++++++++++++++++++++++++++++++++++++++ include/media/v4l2-subdev.h | 21 ++++++++++++++++---- 3 files changed, 90 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 6fe88e965a8c..7d225389bfb1 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -35,14 +35,9 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - /* Allocate try format and crop in the same memory block */ - fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop)) - * sd->entity.num_pads, GFP_KERNEL); - if (fh->try_fmt == NULL) + fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL); + if (fh->pad == NULL) return -ENOMEM; - - fh->try_crop = (struct v4l2_rect *) - (fh->try_fmt + sd->entity.num_pads); #endif return 0; } @@ -50,9 +45,8 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) static void subdev_fh_free(struct v4l2_subdev_fh *fh) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - kfree(fh->try_fmt); - fh->try_fmt = NULL; - fh->try_crop = NULL; + kfree(fh->pad); + fh->pad = NULL; #endif } @@ -293,6 +287,34 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, fie); } + + case VIDIOC_SUBDEV_G_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call( + sd, pad, get_selection, subdev_fh, sel); + } + + case VIDIOC_SUBDEV_S_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call( + sd, pad, set_selection, subdev_fh, sel); + } #endif default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index ed29cbbebfef..812019ee1e06 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -123,6 +123,43 @@ struct v4l2_subdev_frame_interval_enum { __u32 reserved[9]; }; +#define V4L2_SUBDEV_SEL_FLAG_SIZE_GE (1 << 0) +#define V4L2_SUBDEV_SEL_FLAG_SIZE_LE (1 << 1) +#define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG (1 << 2) + +/* active cropping area */ +#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL 0x0000 +/* cropping bounds */ +#define V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS 0x0002 +/* current composing area */ +#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL 0x0100 +/* composing bounds */ +#define V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS 0x0102 + + +/** + * struct v4l2_subdev_selection - selection info + * + * @which: either V4L2_SUBDEV_FORMAT_ACTIVE or V4L2_SUBDEV_FORMAT_TRY + * @pad: pad number, as reported by the media API + * @target: selection target, used to choose one of possible rectangles + * @flags: constraint flags + * @r: coordinates of the selection window + * @reserved: for future use, set to zero for now + * + * Hardware may use multiple helper windows to process a video stream. + * The structure is used to exchange this selection areas between + * an application and a driver. + */ +struct v4l2_subdev_selection { + __u32 which; + __u32 pad; + __u32 target; + __u32 flags; + struct v4l2_rect r; + __u32 reserved[8]; +}; + #define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format) #define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format) #define VIDIOC_SUBDEV_G_FRAME_INTERVAL \ @@ -137,5 +174,9 @@ struct v4l2_subdev_frame_interval_enum { _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum) #define VIDIOC_SUBDEV_G_CROP _IOWR('V', 59, struct v4l2_subdev_crop) #define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) +#define VIDIOC_SUBDEV_G_SELECTION \ + _IOWR('V', 61, struct v4l2_subdev_selection) +#define VIDIOC_SUBDEV_S_SELECTION \ + _IOWR('V', 62, struct v4l2_subdev_selection) #endif diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f0f3358d1b1b..feab950bc8ab 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -466,6 +466,10 @@ struct v4l2_subdev_pad_ops { struct v4l2_subdev_crop *crop); int (*get_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_crop *crop); + int (*get_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel); + int (*set_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel); }; struct v4l2_subdev_ops { @@ -549,8 +553,11 @@ struct v4l2_subdev { struct v4l2_subdev_fh { struct v4l2_fh vfh; #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - struct v4l2_mbus_framefmt *try_fmt; - struct v4l2_rect *try_crop; + struct { + struct v4l2_mbus_framefmt try_fmt; + struct v4l2_rect try_crop; + struct v4l2_rect try_compose; + } *pad; #endif }; @@ -561,13 +568,19 @@ struct v4l2_subdev_fh { static inline struct v4l2_mbus_framefmt * v4l2_subdev_get_try_format(struct v4l2_subdev_fh *fh, unsigned int pad) { - return &fh->try_fmt[pad]; + return &fh->pad[pad].try_fmt; } static inline struct v4l2_rect * v4l2_subdev_get_try_crop(struct v4l2_subdev_fh *fh, unsigned int pad) { - return &fh->try_crop[pad]; + return &fh->pad[pad].try_crop; +} + +static inline struct v4l2_rect * +v4l2_subdev_get_try_compose(struct v4l2_subdev_fh *fh, unsigned int pad) +{ + return &fh->pad[pad].try_compose; } #endif -- cgit v1.2.3 From a3304b0a17495183a2270d4a25978795226597a4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 28 Mar 2012 11:04:24 +0200 Subject: cfg80211/nl80211: clarify TX queue API With the plan to change mac80211's queue API to not map ACs to queues 1:1, it seems necessary to clarify some APIs that act on ACs rather than on queues to spell that out explicitly. Do this. Also verify that the AC number given is valid. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 22 +++++++++++++++------- include/net/cfg80211.h | 4 ++-- include/net/mac80211.h | 2 +- net/mac80211/cfg.c | 10 +++++----- net/mac80211/driver-ops.h | 6 +++--- net/mac80211/driver-trace.h | 13 ++++++------- net/wireless/nl80211.c | 7 +++++-- 7 files changed, 37 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index e474f6e780cc..1f6e44680fb7 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -2223,7 +2223,7 @@ enum nl80211_mesh_setup_params { /** * enum nl80211_txq_attr - TX queue parameter attributes * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved - * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*) * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning * disabled * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form @@ -2236,7 +2236,7 @@ enum nl80211_mesh_setup_params { */ enum nl80211_txq_attr { __NL80211_TXQ_ATTR_INVALID, - NL80211_TXQ_ATTR_QUEUE, + NL80211_TXQ_ATTR_AC, NL80211_TXQ_ATTR_TXOP, NL80211_TXQ_ATTR_CWMIN, NL80211_TXQ_ATTR_CWMAX, @@ -2247,13 +2247,21 @@ enum nl80211_txq_attr { NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 }; -enum nl80211_txq_q { - NL80211_TXQ_Q_VO, - NL80211_TXQ_Q_VI, - NL80211_TXQ_Q_BE, - NL80211_TXQ_Q_BK +enum nl80211_ac { + NL80211_AC_VO, + NL80211_AC_VI, + NL80211_AC_BE, + NL80211_AC_BK, + NL80211_NUM_ACS }; +/* backward compat */ +#define NL80211_TXQ_ATTR_QUEUE NL80211_TXQ_ATTR_AC +#define NL80211_TXQ_Q_VO NL80211_AC_VO +#define NL80211_TXQ_Q_VI NL80211_AC_VI +#define NL80211_TXQ_Q_BE NL80211_AC_BE +#define NL80211_TXQ_Q_BK NL80211_AC_BK + enum nl80211_channel_type { NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 83d800c31e3c..ac9147778a81 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -845,7 +845,7 @@ struct mesh_setup { /** * struct ieee80211_txq_params - TX queue parameters - * @queue: TX queue identifier (NL80211_TXQ_Q_*) + * @ac: AC identifier * @txop: Maximum burst time in units of 32 usecs, 0 meaning disabled * @cwmin: Minimum contention window [a value of the form 2^n-1 in the range * 1..32767] @@ -854,7 +854,7 @@ struct mesh_setup { * @aifs: Arbitration interframe space [0..255] */ struct ieee80211_txq_params { - enum nl80211_txq_q queue; + enum nl80211_ac ac; u16 txop; u16 cwmin; u16 cwmax; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a0e79d13fa8b..43f4609ab5f0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2222,7 +2222,7 @@ struct ieee80211_ops { struct ieee80211_sta *sta, u32 changed); int (*conf_tx)(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, + struct ieee80211_vif *vif, u16 ac, const struct ieee80211_tx_queue_params *params); u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 677d65929780..ef40db5ab3c7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1449,14 +1449,14 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, */ p.uapsd = false; - if (params->queue >= local->hw.queues) + if (params->ac >= local->hw.queues) return -EINVAL; - sdata->tx_conf[params->queue] = p; - if (drv_conf_tx(local, sdata, params->queue, &p)) { + sdata->tx_conf[params->ac] = p; + if (drv_conf_tx(local, sdata, params->ac, &p)) { wiphy_debug(local->hw.wiphy, - "failed to set TX queue parameters for queue %d\n", - params->queue); + "failed to set TX queue parameters for AC %d\n", + params->ac); return -EINVAL; } diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 0eb2bc003058..8ad40f68f2c3 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -492,7 +492,7 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local, } static inline int drv_conf_tx(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, u16 queue, + struct ieee80211_sub_if_data *sdata, u16 ac, const struct ieee80211_tx_queue_params *params) { int ret = -EOPNOTSUPP; @@ -501,10 +501,10 @@ static inline int drv_conf_tx(struct ieee80211_local *local, check_sdata_in_driver(sdata); - trace_drv_conf_tx(local, sdata, queue, params); + trace_drv_conf_tx(local, sdata, ac, params); if (local->ops->conf_tx) ret = local->ops->conf_tx(&local->hw, &sdata->vif, - queue, params); + ac, params); trace_drv_return_int(local, ret); return ret; } diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 7ea544d86436..d1f017a11988 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -705,15 +705,14 @@ TRACE_EVENT(drv_sta_remove, TRACE_EVENT(drv_conf_tx, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - u16 queue, - const struct ieee80211_tx_queue_params *params), + u16 ac, const struct ieee80211_tx_queue_params *params), - TP_ARGS(local, sdata, queue, params), + TP_ARGS(local, sdata, ac, params), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY - __field(u16, queue) + __field(u16, ac) __field(u16, txop) __field(u16, cw_min) __field(u16, cw_max) @@ -724,7 +723,7 @@ TRACE_EVENT(drv_conf_tx, TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; - __entry->queue = queue; + __entry->ac = ac; __entry->txop = params->txop; __entry->cw_max = params->cw_max; __entry->cw_min = params->cw_min; @@ -733,8 +732,8 @@ TRACE_EVENT(drv_conf_tx, ), TP_printk( - LOCAL_PR_FMT VIF_PR_FMT " queue:%d", - LOCAL_PR_ARG, VIF_PR_ARG, __entry->queue + LOCAL_PR_FMT VIF_PR_FMT " AC:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->ac ) ); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e49da2797022..344697df1177 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1104,17 +1104,20 @@ static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { static int parse_txq_params(struct nlattr *tb[], struct ieee80211_txq_params *txq_params) { - if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] || + if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] || !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || !tb[NL80211_TXQ_ATTR_AIFS]) return -EINVAL; - txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]); + txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]); txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]); txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]); txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]); txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]); + if (txq_params->ac >= NL80211_NUM_ACS) + return -EINVAL; + return 0; } -- cgit v1.2.3 From dbf498fbafa2c23139d5a990e94ed78bafbbea19 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Sat, 31 Mar 2012 11:31:32 -0700 Subject: mac80211: Implement mesh synchronization framework This patch adds MBSS extensible synchronization framework (Sec. 13.13.2 of IEEE Std. 802.11-2012). The framework is implemented via an ops table which defines the following functions: rx_bcn_presp() - this is called every time a mesh beacon is received. adjust_tbtt() - this is called immediately before a beacon is about to be transmitted. The default neighbor offset synchronization defined in the standard is implemented. We also provide template functions for vendor specific methods. When neighbor offset synchronization is active (which is the default) mesh neighbors in the same MBSS will track timing offsets to each other and compensate clock drift. In our tests we observed that this mesh synchronization implementation successfully corrected drifts between stations of ~2PPM while introducing a jitter of ~20us. It is also possible to test this framework on mac80211_hwsim simulated phys to see how it behaves under different topologies, over poor links, etc. Signed-off-by: Marco Porsch Signed-off-by: Pavel Zubarev Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 12 ++ net/mac80211/Kconfig | 11 ++ net/mac80211/Makefile | 3 +- net/mac80211/debugfs_sta.c | 5 +- net/mac80211/ieee80211_i.h | 23 ++++ net/mac80211/mesh.c | 21 +++- net/mac80211/mesh.h | 19 +++ net/mac80211/mesh_sync.c | 296 +++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/sta_info.h | 5 + net/mac80211/tx.c | 5 + 10 files changed, 392 insertions(+), 8 deletions(-) create mode 100644 net/mac80211/mesh_sync.c (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 09301b0768d0..db84e2f6b289 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1438,6 +1438,18 @@ enum ieee80211_tdls_actioncode { /* TDLS specific payload type in the LLC/SNAP header */ #define WLAN_TDLS_SNAP_RFTYPE 0x2 +/** + * enum - mesh synchronization method identifier + * + * @IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET: the default synchronization method + * @IEEE80211_SYNC_METHOD_VENDOR: a vendor specific synchronization method + * that will be specified in a vendor specific information element + */ +enum { + IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET = 1, + IEEE80211_SYNC_METHOD_VENDOR = 255, +}; + /** * enum - mesh path selection protocol identifier * diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 96ddb72760b9..8d249d705980 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -225,6 +225,17 @@ config MAC80211_VERBOSE_MHWMP_DEBUG Do not select this option. +config MAC80211_VERBOSE_MESH_SYNC_DEBUG + bool "Verbose mesh mesh synchronization debugging" + depends on MAC80211_DEBUG_MENU + depends on MAC80211_MESH + ---help--- + Selecting this option causes mac80211 to print out very verbose mesh + synchronization debugging messages (when mac80211 is taking part in a + mesh network). + + Do not select this option. + config MAC80211_VERBOSE_TDLS_DEBUG bool "Verbose TDLS debugging" depends on MAC80211_DEBUG_MENU diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 1be7a454aa77..3e9d931bba35 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -38,7 +38,8 @@ mac80211-$(CONFIG_MAC80211_MESH) += \ mesh.o \ mesh_pathtbl.o \ mesh_plink.o \ - mesh_hwmp.o + mesh_hwmp.o \ + mesh_sync.o mac80211-$(CONFIG_PM) += pm.o diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 6d45804d09bc..ceeefd424103 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -63,7 +63,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" int res = scnprintf(buf, sizeof(buf), - "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", TEST(AUTH), TEST(ASSOC), TEST(PS_STA), TEST(PS_DRIVER), TEST(AUTHORIZED), TEST(SHORT_PREAMBLE), @@ -71,7 +71,8 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT), - TEST(INSERTED), TEST(RATE_CONTROL)); + TEST(INSERTED), TEST(RATE_CONTROL), + TEST(TOFFSET_KNOWN)); #undef TEST return simple_read_from_buffer(userbuf, count, ppos, buf, res); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8e7af7cee013..ea9623cbd969 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -554,6 +554,24 @@ struct ieee80211_if_ibss { } state; }; +/** + * struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface + * + * these declarations define the interface, which enables + * vendor-specific mesh synchronization + * + */ +struct ieee802_11_elems; +struct ieee80211_mesh_sync_ops { + void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata, + u16 stype, + struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *elems, + struct ieee80211_rx_status *rx_status); + void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata); + /* add other framework functions here */ +}; + struct ieee80211_if_mesh { struct timer_list housekeeping_timer; struct timer_list mesh_path_timer; @@ -602,6 +620,11 @@ struct ieee80211_if_mesh { IEEE80211_MESH_SEC_AUTHED = 0x1, IEEE80211_MESH_SEC_SECURED = 0x2, } security; + /* Extensible Synchronization Framework */ + struct ieee80211_mesh_sync_ops *sync_ops; + s64 sync_offset_clockdrift_max; + spinlock_t sync_offset_lock; + bool adjusting_tbtt; }; #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index b05fa9ef866c..386dbca1eab3 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -13,9 +13,6 @@ #include "ieee80211_i.h" #include "mesh.h" -#define MESHCONF_CAPAB_ACCEPT_PLINKS 0x01 -#define MESHCONF_CAPAB_FORWARDING 0x08 - #define TMR_RUNNING_HK 0 #define TMR_RUNNING_MP 1 #define TMR_RUNNING_MPR 2 @@ -251,8 +248,10 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) /* Mesh capability */ ifmsh->accepting_plinks = mesh_plink_availables(sdata); *pos = MESHCONF_CAPAB_FORWARDING; - *pos++ |= ifmsh->accepting_plinks ? + *pos |= ifmsh->accepting_plinks ? MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; + *pos++ |= ifmsh->adjusting_tbtt ? + MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00; *pos++ = 0x00; return 0; @@ -573,8 +572,11 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) ieee80211_configure_filter(local); ifmsh->mesh_cc_id = 0; /* Disabled */ - ifmsh->mesh_sp_id = 0; /* Neighbor Offset */ ifmsh->mesh_auth_id = 0; /* Disabled */ + /* register sync ops from extensible synchronization framework */ + ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id); + ifmsh->adjusting_tbtt = false; + ifmsh->sync_offset_clockdrift_max = 0; set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags); ieee80211_mesh_root_setup(ifmsh); ieee80211_queue_work(&local->hw, &sdata->work); @@ -616,6 +618,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, struct ieee80211_rx_status *rx_status) { struct ieee80211_local *local = sdata->local; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee802_11_elems elems; struct ieee80211_channel *channel; u32 supp_rates = 0; @@ -654,6 +657,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, supp_rates = ieee80211_sta_get_rates(local, &elems, band); mesh_neighbour_update(mgmt->sa, supp_rates, sdata, &elems); } + + if (ifmsh->sync_ops) + ifmsh->sync_ops->rx_bcn_presp(sdata, + stype, mgmt, &elems, rx_status); } static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, @@ -721,6 +728,9 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) if (test_and_clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags)) ieee80211_mesh_rootpath(sdata); + + if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) + mesh_sync_adjust_tbtt(sdata); } void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) @@ -761,4 +771,5 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) (unsigned long) sdata); INIT_LIST_HEAD(&ifmsh->preq_queue.list); spin_lock_init(&ifmsh->mesh_preq_queue_lock); + spin_lock_init(&ifmsh->sync_offset_lock); } diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 3e52439ed112..fa7d9704c175 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -18,6 +18,20 @@ /* Data structures */ +/** + * enum mesh_config_capab_flags - mesh config IE capability flags + * + * @MESHCONF_CAPAB_ACCEPT_PLINKS: STA is willing to establish + * additional mesh peerings with other mesh STAs + * @MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs + * @MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure is ongoing + */ +enum mesh_config_capab_flags { + MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0), + MESHCONF_CAPAB_FORWARDING = BIT(3), + MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5), +}; + /** * enum mesh_path_flags - mac80211 mesh path flags * @@ -56,12 +70,15 @@ enum mesh_path_flags { * @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to * grow * @MESH_WORK_ROOT: the mesh root station needs to send a frame + * @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other + * mesh nodes */ enum mesh_deferred_task_flags { MESH_WORK_HOUSEKEEPING, MESH_WORK_GROW_MPATH_TABLE, MESH_WORK_GROW_MPP_TABLE, MESH_WORK_ROOT, + MESH_WORK_DRIFT_ADJUST, }; /** @@ -234,6 +251,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata); void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh); +struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method); /* Mesh paths */ int mesh_nexthop_lookup(struct sk_buff *skb, @@ -327,6 +345,7 @@ void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata); void mesh_plink_quiesce(struct sta_info *sta); void mesh_plink_restart(struct sta_info *sta); +void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata); #else #define mesh_allocated 0 static inline void diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c new file mode 100644 index 000000000000..f78b0139856f --- /dev/null +++ b/net/mac80211/mesh_sync.c @@ -0,0 +1,296 @@ +/* + * Copyright 2011-2012, Pavel Zubarev + * Copyright 2011-2012, Marco Porsch + * Copyright 2011-2012, cozybit Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "ieee80211_i.h" +#include "mesh.h" +#include "driver-ops.h" + +#ifdef CONFIG_MAC80211_VERBOSE_MESH_SYNC_DEBUG +#define msync_dbg(fmt, args...) \ + printk(KERN_DEBUG "Mesh sync (%s): " fmt "\n", sdata->name, ##args) +#else +#define msync_dbg(fmt, args...) do { (void)(0); } while (0) +#endif + +/* This is not in the standard. It represents a tolerable tbtt drift below + * which we do no TSF adjustment. + */ +#define TBTT_MINIMUM_ADJUSTMENT 10 + +struct sync_method { + u8 method; + struct ieee80211_mesh_sync_ops ops; +}; + +/** + * mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT + * + * @ie: information elements of a management frame from the mesh peer + */ +static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie) +{ + return (ie->mesh_config->meshconf_cap & + MESHCONF_CAPAB_TBTT_ADJUSTING) != 0; +} + +void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + /* sdata->vif.bss_conf.beacon_int in 1024us units, 0.04% */ + u64 beacon_int_fraction = sdata->vif.bss_conf.beacon_int * 1024 / 2500; + u64 tsf; + u64 tsfdelta; + + spin_lock_bh(&ifmsh->sync_offset_lock); + + if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) { + msync_dbg("TBTT : max clockdrift=%lld; adjusting", + (long long) ifmsh->sync_offset_clockdrift_max); + tsfdelta = -ifmsh->sync_offset_clockdrift_max; + ifmsh->sync_offset_clockdrift_max = 0; + } else { + msync_dbg("TBTT : max clockdrift=%lld; adjusting by %llu", + (long long) ifmsh->sync_offset_clockdrift_max, + (unsigned long long) beacon_int_fraction); + tsfdelta = -beacon_int_fraction; + ifmsh->sync_offset_clockdrift_max -= beacon_int_fraction; + } + + tsf = drv_get_tsf(local, sdata); + if (tsf != -1ULL) + drv_set_tsf(local, sdata, tsf + tsfdelta); + spin_unlock_bh(&ifmsh->sync_offset_lock); +} + +static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, + u16 stype, + struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *elems, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + u64 t_t, t_r; + + WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); + + /* standard mentions only beacons */ + if (stype != IEEE80211_STYPE_BEACON) + return; + + /* The current tsf is a first approximation for the timestamp + * for the received beacon. Further down we try to get a + * better value from the rx_status->mactime field if + * available. Also we have to call drv_get_tsf() before + * entering the rcu-read section.*/ + t_r = drv_get_tsf(local, sdata); + + rcu_read_lock(); + sta = sta_info_get(sdata, mgmt->sa); + if (!sta) + goto no_sync; + + /* check offset sync conditions (13.13.2.2.1) + * + * TODO also sync to + * dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors + */ + + if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) { + clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); + msync_dbg("STA %pM : is adjusting TBTT", sta->sta.addr); + goto no_sync; + } + + if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) { + /* + * The mactime is defined as the time the first data symbol + * of the frame hits the PHY, and the timestamp of the beacon + * is defined as "the time that the data symbol containing the + * first bit of the timestamp is transmitted to the PHY plus + * the transmitting STA's delays through its local PHY from the + * MAC-PHY interface to its interface with the WM" (802.11 + * 11.1.2) + * + * T_r, in 13.13.2.2.2, is just defined as "the frame reception + * time" but we unless we interpret that time to be the same + * time of the beacon timestamp, the offset calculation will be + * off. Below we adjust t_r to be "the time at which the first + * symbol of the timestamp element in the beacon is received". + * This correction depends on the rate. + * + * Based on similar code in ibss.c + */ + int rate; + + if (rx_status->flag & RX_FLAG_HT) { + /* TODO: + * In principle there could be HT-beacons (Dual Beacon + * HT Operation options), but for now ignore them and + * just use the primary (i.e. non-HT) beacons for + * synchronization. + * */ + goto no_sync; + } else + rate = local->hw.wiphy->bands[rx_status->band]-> + bitrates[rx_status->rate_idx].bitrate; + + /* 24 bytes of header * 8 bits/byte * + * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/ + t_r = rx_status->mactime + (24 * 8 * 10 / rate); + } + + /* Timing offset calculation (see 13.13.2.2.2) */ + t_t = le64_to_cpu(mgmt->u.beacon.timestamp); + sta->t_offset = t_t - t_r; + + if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { + s64 t_clockdrift = sta->t_offset_setpoint + - sta->t_offset; + + msync_dbg("STA %pM : sta->t_offset=%lld," + " sta->t_offset_setpoint=%lld," + " t_clockdrift=%lld", + sta->sta.addr, + (long long) sta->t_offset, + (long long) + sta->t_offset_setpoint, + (long long) t_clockdrift); + rcu_read_unlock(); + + spin_lock_bh(&ifmsh->sync_offset_lock); + if (t_clockdrift > + ifmsh->sync_offset_clockdrift_max) + ifmsh->sync_offset_clockdrift_max + = t_clockdrift; + spin_unlock_bh(&ifmsh->sync_offset_lock); + + } else { + sta->t_offset_setpoint = sta->t_offset; + set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); + msync_dbg("STA %pM : offset was invalid, " + " sta->t_offset=%lld", + sta->sta.addr, + (long long) sta->t_offset); + rcu_read_unlock(); + } + return; + +no_sync: + rcu_read_unlock(); +} + +static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + + WARN_ON(ifmsh->mesh_sp_id + != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); + BUG_ON(!rcu_read_lock_held()); + + spin_lock_bh(&ifmsh->sync_offset_lock); + + if (ifmsh->sync_offset_clockdrift_max > + TBTT_MINIMUM_ADJUSTMENT) { + /* Since ajusting the tsf here would + * require a possibly blocking call + * to the driver tsf setter, we punt + * the tsf adjustment to the mesh tasklet + */ + msync_dbg("TBTT : kicking off TBTT " + "adjustment with " + "clockdrift_max=%lld", + ifmsh->sync_offset_clockdrift_max); + set_bit(MESH_WORK_DRIFT_ADJUST, + &ifmsh->wrkq_flags); + } else { + msync_dbg("TBTT : max clockdrift=%lld; " + "too small to adjust", + (long long) + ifmsh->sync_offset_clockdrift_max); + ifmsh->sync_offset_clockdrift_max = 0; + } + spin_unlock_bh(&ifmsh->sync_offset_lock); +} + +static const u8 *mesh_get_vendor_oui(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + u8 offset; + + if (!ifmsh->ie || !ifmsh->ie_len) + return NULL; + + offset = ieee80211_ie_split_vendor(ifmsh->ie, + ifmsh->ie_len, 0); + + if (!offset) + return NULL; + + return ifmsh->ie + offset + 2; +} + +static void mesh_sync_vendor_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, + u16 stype, + struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *elems, + struct ieee80211_rx_status *rx_status) +{ + const u8 *oui; + + WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR); + msync_dbg("called mesh_sync_vendor_rx_bcn_presp"); + oui = mesh_get_vendor_oui(sdata); + /* here you would implement the vendor offset tracking for this oui */ +} + +static void mesh_sync_vendor_adjust_tbtt(struct ieee80211_sub_if_data *sdata) +{ + const u8 *oui; + + WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR); + msync_dbg("called mesh_sync_vendor_adjust_tbtt"); + oui = mesh_get_vendor_oui(sdata); + /* here you would implement the vendor tsf adjustment for this oui */ +} + +/* global variable */ +static struct sync_method sync_methods[] = { + { + .method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, + .ops = { + .rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp, + .adjust_tbtt = &mesh_sync_offset_adjust_tbtt, + } + }, + { + .method = IEEE80211_SYNC_METHOD_VENDOR, + .ops = { + .rx_bcn_presp = &mesh_sync_vendor_rx_bcn_presp, + .adjust_tbtt = &mesh_sync_vendor_adjust_tbtt, + } + }, +}; + +struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method) +{ + struct ieee80211_mesh_sync_ops *ops = NULL; + u8 i; + + for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) { + if (sync_methods[i].method == method) { + ops = &sync_methods[i].ops; + break; + } + } + return ops; +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b1b4b1413c74..f75f5d9ac06d 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -55,6 +55,7 @@ * @WLAN_STA_4ADDR_EVENT: 4-addr event was already sent for this frame. * @WLAN_STA_INSERTED: This station is inserted into the hash table. * @WLAN_STA_RATE_CONTROL: rate control was initialized for this station. + * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH, @@ -76,6 +77,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_4ADDR_EVENT, WLAN_STA_INSERTED, WLAN_STA_RATE_CONTROL, + WLAN_STA_TOFFSET_KNOWN, }; #define STA_TID_NUM 16 @@ -268,6 +270,7 @@ struct sta_ampdu_mlme { * @plink_timeout: timeout of peer link * @plink_timer: peer link watch timer * @plink_timer_was_running: used by suspend/resume to restore timers + * @t_offset: timing offset relative to this host * @debugfs: debug filesystem info * @dead: set to true when sta is unlinked * @uploaded: set to true when sta is uploaded to the driver @@ -357,6 +360,8 @@ struct sta_info { enum nl80211_plink_state plink_state; u32 plink_timeout; struct timer_list plink_timer; + s64 t_offset; + s64 t_offset_setpoint; #endif #ifdef CONFIG_MAC80211_DEBUGFS diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e0b89780b472..daab5adeb93c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2373,6 +2373,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, IEEE80211_STYPE_BEACON); } else if (ieee80211_vif_is_mesh(&sdata->vif)) { struct ieee80211_mgmt *mgmt; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u8 *pos; int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) + sizeof(mgmt->u.beacon); @@ -2382,6 +2383,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, goto out; #endif + if (ifmsh->sync_ops) + ifmsh->sync_ops->adjust_tbtt( + sdata); + skb = dev_alloc_skb(local->tx_headroom + hdr_len + 2 + /* NULL SSID */ -- cgit v1.2.3 From d299a1f21ea7ffd5114d099b2f92c867c495e8b3 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Sat, 31 Mar 2012 11:31:33 -0700 Subject: {nl,cfg}80211: Support for mesh synchronization Report Toffset to userspace. Let userspace select the mesh synchronization method. Signed-off-by: Marco Porsch Signed-off-by: Pavel Zubarev Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/nl80211.h | 12 ++++++++++++ include/net/cfg80211.h | 14 +++++++++++--- net/mac80211/cfg.c | 8 ++++++++ net/wireless/mesh.c | 3 +++ net/wireless/nl80211.c | 16 ++++++++++++++++ 5 files changed, 50 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 1f6e44680fb7..c6d26328a166 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1685,6 +1685,7 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) + * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1708,6 +1709,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_CONNECTED_TIME, NL80211_STA_INFO_STA_FLAGS, NL80211_STA_INFO_BEACON_LOSS, + NL80211_STA_INFO_T_OFFSET, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -2142,6 +2144,9 @@ enum nl80211_mntr_flags { * * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute * + * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors + * to synchronize to for 11s default synchronization method (see 11C.12.2.2) + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2166,6 +2171,7 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, NL80211_MESHCONF_FORWARDING, NL80211_MESHCONF_RSSI_THRESHOLD, + NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, @@ -2205,6 +2211,11 @@ enum nl80211_meshconf_params { * complete (unsecured) mesh peering without the need of a userspace daemon. * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a + * vendor specific synchronization method or disable it to use the default + * neighbor offset synchronization + * * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use */ enum nl80211_mesh_setup_params { @@ -2214,6 +2225,7 @@ enum nl80211_mesh_setup_params { NL80211_MESH_SETUP_IE, NL80211_MESH_SETUP_USERSPACE_AUTH, NL80211_MESH_SETUP_USERSPACE_AMPE, + NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ac9147778a81..ae3a3bb37bf2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -521,6 +521,7 @@ struct station_parameters { * @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled * @STATION_INFO_STA_FLAGS: @sta_flags filled * @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled + * @STATION_INFO_T_OFFSET: @t_offset filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -542,7 +543,8 @@ enum station_info_flags { STATION_INFO_CONNECTED_TIME = 1<<16, STATION_INFO_ASSOC_REQ_IES = 1<<17, STATION_INFO_STA_FLAGS = 1<<18, - STATION_INFO_BEACON_LOSS_COUNT = 1<<19 + STATION_INFO_BEACON_LOSS_COUNT = 1<<19, + STATION_INFO_T_OFFSET = 1<<20, }; /** @@ -643,6 +645,7 @@ struct sta_bss_parameters { * @assoc_req_ies_len: Length of assoc_req_ies buffer in octets. * @sta_flags: station flags mask & values * @beacon_loss_count: Number of times beacon loss event has triggered. + * @t_offset: Time offset of the station relative to this host. */ struct station_info { u32 filled; @@ -671,6 +674,7 @@ struct station_info { size_t assoc_req_ies_len; u32 beacon_loss_count; + s64 t_offset; /* * Note: Add a new enum station_info_flags value for each new field and @@ -798,6 +802,8 @@ struct mesh_config { /* ttl used in path selection information elements */ u8 element_ttl; bool auto_open_plinks; + /* neighbor offset synchronization */ + u32 dot11MeshNbrOffsetMaxNeighbor; /* HWMP parameters */ u8 dot11MeshHWMPmaxPREQretries; u32 path_refresh_time; @@ -821,6 +827,7 @@ struct mesh_config { * struct mesh_setup - 802.11s mesh setup configuration * @mesh_id: the mesh ID * @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes + * @sync_method: which synchronization method to use * @path_sel_proto: which path selection protocol to use * @path_metric: which metric to use * @ie: vendor information elements (optional) @@ -834,8 +841,9 @@ struct mesh_config { struct mesh_setup { const u8 *mesh_id; u8 mesh_id_len; - u8 path_sel_proto; - u8 path_metric; + u8 sync_method; + u8 path_sel_proto; + u8 path_metric; const u8 *ie; u8 ie_len; bool is_authenticated; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 12226b7743c6..83e08dcb2f5d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -412,6 +412,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->llid = le16_to_cpu(sta->llid); sinfo->plid = le16_to_cpu(sta->plid); sinfo->plink_state = sta->plink_state; + if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { + sinfo->filled |= STATION_INFO_T_OFFSET; + sinfo->t_offset = sta->t_offset; + } #endif } @@ -1235,6 +1239,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, /* now copy the rest of the setup parameters */ ifmsh->mesh_id_len = setup->mesh_id_len; memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len); + ifmsh->mesh_sp_id = setup->sync_method; ifmsh->mesh_pp_id = setup->path_sel_proto; ifmsh->mesh_pm_id = setup->path_metric; ifmsh->security = IEEE80211_MESH_SEC_NONE; @@ -1279,6 +1284,9 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, conf->dot11MeshTTL = nconf->element_ttl; if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) conf->auto_open_plinks = nconf->auto_open_plinks; + if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask)) + conf->dot11MeshNbrOffsetMaxNeighbor = + nconf->dot11MeshNbrOffsetMaxNeighbor; if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask)) conf->dot11MeshHWMPmaxPREQretries = nconf->dot11MeshHWMPmaxPREQretries; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index ba21ab22187b..8c747fa9319b 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -38,6 +38,7 @@ #define MESH_MAX_PREQ_RETRIES 4 +#define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50 const struct mesh_config default_mesh_config = { .dot11MeshRetryTimeout = MESH_RET_T, @@ -48,6 +49,7 @@ const struct mesh_config default_mesh_config = { .element_ttl = MESH_DEFAULT_ELEMENT_TTL, .auto_open_plinks = true, .dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS, + .dot11MeshNbrOffsetMaxNeighbor = MESH_SYNC_NEIGHBOR_OFFSET_MAX, .dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT, .dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT, .dot11MeshHWMPperrMinInterval = MESH_PERR_MIN_INT, @@ -62,6 +64,7 @@ const struct mesh_config default_mesh_config = { }; const struct mesh_setup default_mesh_setup = { + .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, .path_metric = IEEE80211_PATH_METRIC_AIRTIME, .ie = NULL, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 344697df1177..b12a05243d71 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2490,6 +2490,9 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, NLA_PUT(msg, NL80211_STA_INFO_STA_FLAGS, sizeof(struct nl80211_sta_flag_update), &sinfo->sta_flags); + if (sinfo->filled & STATION_INFO_T_OFFSET) + NLA_PUT_U64(msg, NL80211_STA_INFO_T_OFFSET, + sinfo->t_offset); nla_nest_end(msg, sinfoattr); if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES) @@ -3288,6 +3291,8 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, cur_params.element_ttl); NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, cur_params.auto_open_plinks); + NLA_PUT_U32(msg, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, + cur_params.dot11MeshNbrOffsetMaxNeighbor); NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, cur_params.dot11MeshHWMPmaxPREQretries); NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME, @@ -3332,6 +3337,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_TTL] = { .type = NLA_U8 }, [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 }, [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 }, + [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 }, [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 }, @@ -3349,6 +3355,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A static const struct nla_policy nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { + [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 }, [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 }, @@ -3401,6 +3408,9 @@ do {\ mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, + mask, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, + nla_get_u32); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, nla_get_u8); @@ -3458,6 +3468,12 @@ static int nl80211_parse_mesh_setup(struct genl_info *info, nl80211_mesh_setup_params_policy)) return -EINVAL; + if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC]) + setup->sync_method = + (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ? + IEEE80211_SYNC_METHOD_VENDOR : + IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET; + if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL]) setup->path_sel_proto = (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ? -- cgit v1.2.3 From 4d6ddb08acc48368c5b7ac431f9d00db7227d2ed Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 11 Apr 2012 12:05:50 +0900 Subject: sh: clkfwk: Support variable size accesses for MSTP clocks. The bulk of the MSTP users require 32-bit access, but this isn't the case for some of the SH-2A parts, so add in some basic infrastructure to let the CPU define its required access size in preparation. Requested-by: Phil Edworthy Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 38 +++++++++++++++++++++++++++----------- include/linux/sh_clk.h | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 91b6d52f74eb..6cbda4841589 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -2,6 +2,7 @@ * Helper routines for SuperH Clock Pulse Generator blocks (CPG). * * Copyright (C) 2010 Magnus Damm + * Copyright (C) 2010 - 2012 Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -13,26 +14,41 @@ #include #include -static int sh_clk_mstp32_enable(struct clk *clk) +static int sh_clk_mstp_enable(struct clk *clk) { - iowrite32(ioread32(clk->mapped_reg) & ~(1 << clk->enable_bit), - clk->mapped_reg); + if (clk->flags & CLK_ENABLE_REG_8BIT) + iowrite8(ioread8(clk->mapped_reg) & ~(1 << clk->enable_bit), + clk->mapped_reg); + else if (clk->flags & CLK_ENABLE_REG_16BIT) + iowrite16(ioread16(clk->mapped_reg) & ~(1 << clk->enable_bit), + clk->mapped_reg); + else + iowrite32(ioread32(clk->mapped_reg) & ~(1 << clk->enable_bit), + clk->mapped_reg); + return 0; } -static void sh_clk_mstp32_disable(struct clk *clk) +static void sh_clk_mstp_disable(struct clk *clk) { - iowrite32(ioread32(clk->mapped_reg) | (1 << clk->enable_bit), - clk->mapped_reg); + if (clk->flags & CLK_ENABLE_REG_8BIT) + iowrite8(ioread8(clk->mapped_reg) | (1 << clk->enable_bit), + clk->mapped_reg); + else if (clk->flags & CLK_ENABLE_REG_16BIT) + iowrite16(ioread16(clk->mapped_reg) | (1 << clk->enable_bit), + clk->mapped_reg); + else + iowrite32(ioread32(clk->mapped_reg) | (1 << clk->enable_bit), + clk->mapped_reg); } -static struct sh_clk_ops sh_clk_mstp32_clk_ops = { - .enable = sh_clk_mstp32_enable, - .disable = sh_clk_mstp32_disable, +static struct sh_clk_ops sh_clk_mstp_clk_ops = { + .enable = sh_clk_mstp_enable, + .disable = sh_clk_mstp_disable, .recalc = followparent_recalc, }; -int __init sh_clk_mstp32_register(struct clk *clks, int nr) +int __init sh_clk_mstp_register(struct clk *clks, int nr) { struct clk *clkp; int ret = 0; @@ -40,7 +56,7 @@ int __init sh_clk_mstp32_register(struct clk *clks, int nr) for (k = 0; !ret && (k < nr); k++) { clkp = clks + k; - clkp->ops = &sh_clk_mstp32_clk_ops; + clkp->ops = &sh_clk_mstp_clk_ops; ret |= clk_register(clkp); } diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index 0a9d8f2ac519..c513b73cd7cb 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -59,7 +59,15 @@ struct clk { unsigned int nr_freqs; }; -#define CLK_ENABLE_ON_INIT (1 << 0) +#define CLK_ENABLE_ON_INIT BIT(0) + +#define CLK_ENABLE_REG_32BIT BIT(1) /* default access size */ +#define CLK_ENABLE_REG_16BIT BIT(2) +#define CLK_ENABLE_REG_8BIT BIT(3) + +#define CLK_ENABLE_REG_MASK (CLK_ENABLE_REG_32BIT | \ + CLK_ENABLE_REG_16BIT | \ + CLK_ENABLE_REG_8BIT) /* drivers/sh/clk.c */ unsigned long followparent_recalc(struct clk *); @@ -102,7 +110,7 @@ long clk_round_parent(struct clk *clk, unsigned long target, unsigned long *best_freq, unsigned long *parent_freq, unsigned int div_min, unsigned int div_max); -#define SH_CLK_MSTP32(_parent, _enable_reg, _enable_bit, _flags) \ +#define SH_CLK_MSTP(_parent, _enable_reg, _enable_bit, _flags) \ { \ .parent = _parent, \ .enable_reg = (void __iomem *)_enable_reg, \ @@ -110,7 +118,27 @@ long clk_round_parent(struct clk *clk, unsigned long target, .flags = _flags, \ } -int sh_clk_mstp32_register(struct clk *clks, int nr); +#define SH_CLK_MSTP32(_p, _r, _b, _f) \ + SH_CLK_MSTP(_p, _r, _b, _f | CLK_ENABLE_REG_32BIT) + +#define SH_CLK_MSTP16(_p, _r, _b, _f) \ + SH_CLK_MSTP(_p, _r, _b, _f | CLK_ENABLE_REG_16BIT) + +#define SH_CLK_MSTP8(_p, _r, _b, _f) \ + SH_CLK_MSTP(_p, _r, _b, _f | CLK_ENABLE_REG_8BIT) + +int sh_clk_mstp_register(struct clk *clks, int nr); + +/* + * MSTP registration never really cared about access size, despite the + * original enable/disable pairs assuming a 32-bit access. Clocks are + * responsible for defining their access sizes either directly or via the + * clock definition wrappers. + */ +static inline int __deprecated sh_clk_mstp32_register(struct clk *clks, int nr) +{ + return sh_clk_mstp_register(clks, nr); +} #define SH_CLK_DIV4(_parent, _reg, _shift, _div_bitmap, _flags) \ { \ -- cgit v1.2.3 From 2db938bee32e7469ca8ed9bfb3a05535f28c680d Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 21 Feb 2011 17:25:37 +0100 Subject: jbd: Refine commit writeout logic Currently we write out all journal buffers in WRITE_SYNC mode. This improves performance for fsync heavy workloads but hinders performance when writes are mostly asynchronous, most noticably it slows down readers and users complain about slow desktop response etc. So submit writes as asynchronous in the normal case and only submit writes as WRITE_SYNC if we detect someone is waiting for current transaction commit. I've gathered some numbers to back this change. The first is the read latency test. It measures time to read 1 MB after several seconds of sleeping in presence of streaming writes. Top 10 times (out of 90) in us: Before After 2131586 697473 1709932 557487 1564598 535642 1480462 347573 1478579 323153 1408496 222181 1388960 181273 1329565 181070 1252486 172832 1223265 172278 Average: 619377 82180 So the improvement in both maximum and average latency is massive. I've measured fsync throughput by: fs_mark -n 100 -t 1 -s 16384 -d /mnt/fsync/ -S 1 -L 4 in presence of streaming reader. The numbers (fsyncs/s) are: Before After 9.9 6.3 6.8 6.0 6.3 6.2 5.8 6.1 So fsync performance seems unharmed by this change. Signed-off-by: Jan Kara --- fs/jbd/commit.c | 10 +++++++--- fs/jbd/journal.c | 2 ++ fs/jbd/transaction.c | 2 -- include/linux/jbd.h | 15 +++++++++------ include/trace/events/jbd.h | 24 ++++++++---------------- 5 files changed, 26 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index f2b9a571f4cf..9d31e6a39205 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -298,6 +298,7 @@ void journal_commit_transaction(journal_t *journal) int tag_flag; int i; struct blk_plug plug; + int write_op = WRITE; /* * First job: lock down the current transaction and wait for @@ -413,13 +414,16 @@ void journal_commit_transaction(journal_t *journal) jbd_debug (3, "JBD: commit phase 2\n"); + if (tid_geq(journal->j_commit_waited, commit_transaction->t_tid)) + write_op = WRITE_SYNC; + /* * Now start flushing things to disk, in the order they appear * on the transaction lists. Data blocks go first. */ blk_start_plug(&plug); err = journal_submit_data_buffers(journal, commit_transaction, - WRITE_SYNC); + write_op); blk_finish_plug(&plug); /* @@ -478,7 +482,7 @@ void journal_commit_transaction(journal_t *journal) blk_start_plug(&plug); - journal_write_revoke_records(journal, commit_transaction, WRITE_SYNC); + journal_write_revoke_records(journal, commit_transaction, write_op); /* * If we found any dirty or locked buffers, then we should have @@ -649,7 +653,7 @@ start_journal_io: clear_buffer_dirty(bh); set_buffer_uptodate(bh); bh->b_end_io = journal_end_buffer_io_sync; - submit_bh(WRITE_SYNC, bh); + submit_bh(write_op, bh); } cond_resched(); diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c index 0971e9217808..2047fd77bf38 100644 --- a/fs/jbd/journal.c +++ b/fs/jbd/journal.c @@ -563,6 +563,8 @@ int log_wait_commit(journal_t *journal, tid_t tid) spin_unlock(&journal->j_state_lock); #endif spin_lock(&journal->j_state_lock); + if (!tid_geq(journal->j_commit_waited, tid)) + journal->j_commit_waited = tid; while (tid_gt(tid, journal->j_commit_sequence)) { jbd_debug(1, "JBD: want %d, j_commit_sequence=%d\n", tid, journal->j_commit_sequence); diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index b2a7e5244e39..febc10db5ced 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -1433,8 +1433,6 @@ int journal_stop(handle_t *handle) } } - if (handle->h_sync) - transaction->t_synchronous_commit = 1; current->journal_info = NULL; spin_lock(&journal->j_state_lock); spin_lock(&transaction->t_handle_lock); diff --git a/include/linux/jbd.h b/include/linux/jbd.h index d211732b9e99..f265682ae134 100644 --- a/include/linux/jbd.h +++ b/include/linux/jbd.h @@ -479,12 +479,6 @@ struct transaction_s * How many handles used this transaction? [t_handle_lock] */ int t_handle_count; - - /* - * This transaction is being forced and some process is - * waiting for it to finish. - */ - unsigned int t_synchronous_commit:1; }; /** @@ -531,6 +525,8 @@ struct transaction_s * transaction * @j_commit_request: Sequence number of the most recent transaction wanting * commit + * @j_commit_waited: Sequence number of the most recent transaction someone + * is waiting for to commit. * @j_uuid: Uuid of client object. * @j_task: Pointer to the current commit thread for this journal * @j_max_transaction_buffers: Maximum number of metadata buffers to allow in a @@ -695,6 +691,13 @@ struct journal_s */ tid_t j_commit_request; + /* + * Sequence number of the most recent transaction someone is waiting + * for to commit. + * [j_state_lock] + */ + tid_t j_commit_waited; + /* * Journal uuid: identifies the object (filesystem, LVM volume etc) * backed by this journal. This will eventually be replaced by an array diff --git a/include/trace/events/jbd.h b/include/trace/events/jbd.h index aff64d82d713..9305e1b5edc3 100644 --- a/include/trace/events/jbd.h +++ b/include/trace/events/jbd.h @@ -36,19 +36,17 @@ DECLARE_EVENT_CLASS(jbd_commit, TP_STRUCT__entry( __field( dev_t, dev ) - __field( char, sync_commit ) __field( int, transaction ) ), TP_fast_assign( __entry->dev = journal->j_fs_dev->bd_dev; - __entry->sync_commit = commit_transaction->t_synchronous_commit; __entry->transaction = commit_transaction->t_tid; ), - TP_printk("dev %d,%d transaction %d sync %d", + TP_printk("dev %d,%d transaction %d", MAJOR(__entry->dev), MINOR(__entry->dev), - __entry->transaction, __entry->sync_commit) + __entry->transaction) ); DEFINE_EVENT(jbd_commit, jbd_start_commit, @@ -87,19 +85,17 @@ TRACE_EVENT(jbd_drop_transaction, TP_STRUCT__entry( __field( dev_t, dev ) - __field( char, sync_commit ) __field( int, transaction ) ), TP_fast_assign( __entry->dev = journal->j_fs_dev->bd_dev; - __entry->sync_commit = commit_transaction->t_synchronous_commit; __entry->transaction = commit_transaction->t_tid; ), - TP_printk("dev %d,%d transaction %d sync %d", + TP_printk("dev %d,%d transaction %d", MAJOR(__entry->dev), MINOR(__entry->dev), - __entry->transaction, __entry->sync_commit) + __entry->transaction) ); TRACE_EVENT(jbd_end_commit, @@ -109,21 +105,19 @@ TRACE_EVENT(jbd_end_commit, TP_STRUCT__entry( __field( dev_t, dev ) - __field( char, sync_commit ) __field( int, transaction ) __field( int, head ) ), TP_fast_assign( __entry->dev = journal->j_fs_dev->bd_dev; - __entry->sync_commit = commit_transaction->t_synchronous_commit; __entry->transaction = commit_transaction->t_tid; __entry->head = journal->j_tail_sequence; ), - TP_printk("dev %d,%d transaction %d sync %d head %d", + TP_printk("dev %d,%d transaction %d head %d", MAJOR(__entry->dev), MINOR(__entry->dev), - __entry->transaction, __entry->sync_commit, __entry->head) + __entry->transaction, __entry->head) ); TRACE_EVENT(jbd_do_submit_data, @@ -133,19 +127,17 @@ TRACE_EVENT(jbd_do_submit_data, TP_STRUCT__entry( __field( dev_t, dev ) - __field( char, sync_commit ) __field( int, transaction ) ), TP_fast_assign( __entry->dev = journal->j_fs_dev->bd_dev; - __entry->sync_commit = commit_transaction->t_synchronous_commit; __entry->transaction = commit_transaction->t_tid; ), - TP_printk("dev %d,%d transaction %d sync %d", + TP_printk("dev %d,%d transaction %d", MAJOR(__entry->dev), MINOR(__entry->dev), - __entry->transaction, __entry->sync_commit) + __entry->transaction) ); TRACE_EVENT(jbd_cleanup_journal_tail, -- cgit v1.2.3 From 80f7c6683fe0e891ef1db7c967d538b5fdddd22c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 10 Apr 2012 05:15:42 +0000 Subject: team: add support for per-port options This patch allows to create per-port options. That becomes handy for all sorts of stuff, for example for userspace driven link-state, 802.3ad implementation and so on. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 306 +++++++++++++++++++++++------- drivers/net/team/team_mode_activebackup.c | 14 +- drivers/net/team/team_mode_loadbalance.c | 28 ++- include/linux/if_team.h | 30 +-- 4 files changed, 278 insertions(+), 100 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index ea96f822de52..eaf8441753ce 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -81,7 +81,16 @@ EXPORT_SYMBOL(team_port_set_team_mac); * Options handling *******************/ -struct team_option *__team_find_option(struct team *team, const char *opt_name) +struct team_option_inst { /* One for each option instance */ + struct list_head list; + struct team_option *option; + struct team_port *port; /* != NULL if per-port */ + bool changed; + bool removed; +}; + +static struct team_option *__team_find_option(struct team *team, + const char *opt_name) { struct team_option *option; @@ -92,9 +101,121 @@ struct team_option *__team_find_option(struct team *team, const char *opt_name) return NULL; } -int __team_options_register(struct team *team, - const struct team_option *option, - size_t option_count) +static int __team_option_inst_add(struct team *team, struct team_option *option, + struct team_port *port) +{ + struct team_option_inst *opt_inst; + + opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL); + if (!opt_inst) + return -ENOMEM; + opt_inst->option = option; + opt_inst->port = port; + opt_inst->changed = true; + opt_inst->removed = false; + list_add_tail(&opt_inst->list, &team->option_inst_list); + return 0; +} + +static void __team_option_inst_del(struct team_option_inst *opt_inst) +{ + list_del(&opt_inst->list); + kfree(opt_inst); +} + +static void __team_option_inst_del_option(struct team *team, + struct team_option *option) +{ + struct team_option_inst *opt_inst, *tmp; + + list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) { + if (opt_inst->option == option) + __team_option_inst_del(opt_inst); + } +} + +static int __team_option_inst_add_option(struct team *team, + struct team_option *option) +{ + struct team_port *port; + int err; + + if (!option->per_port) + return __team_option_inst_add(team, option, 0); + + list_for_each_entry(port, &team->port_list, list) { + err = __team_option_inst_add(team, option, port); + if (err) + goto inst_del_option; + } + return 0; + +inst_del_option: + __team_option_inst_del_option(team, option); + return err; +} + +static void __team_option_inst_mark_removed_option(struct team *team, + struct team_option *option) +{ + struct team_option_inst *opt_inst; + + list_for_each_entry(opt_inst, &team->option_inst_list, list) { + if (opt_inst->option == option) { + opt_inst->changed = true; + opt_inst->removed = true; + } + } +} + +static void __team_option_inst_del_port(struct team *team, + struct team_port *port) +{ + struct team_option_inst *opt_inst, *tmp; + + list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) { + if (opt_inst->option->per_port && + opt_inst->port == port) + __team_option_inst_del(opt_inst); + } +} + +static int __team_option_inst_add_port(struct team *team, + struct team_port *port) +{ + struct team_option *option; + int err; + + list_for_each_entry(option, &team->option_list, list) { + if (!option->per_port) + continue; + err = __team_option_inst_add(team, option, port); + if (err) + goto inst_del_port; + } + return 0; + +inst_del_port: + __team_option_inst_del_port(team, port); + return err; +} + +static void __team_option_inst_mark_removed_port(struct team *team, + struct team_port *port) +{ + struct team_option_inst *opt_inst; + + list_for_each_entry(opt_inst, &team->option_inst_list, list) { + if (opt_inst->port == port) { + opt_inst->changed = true; + opt_inst->removed = true; + } + } +} + +static int __team_options_register(struct team *team, + const struct team_option *option, + size_t option_count) { int i; struct team_option **dst_opts; @@ -107,26 +228,32 @@ int __team_options_register(struct team *team, for (i = 0; i < option_count; i++, option++) { if (__team_find_option(team, option->name)) { err = -EEXIST; - goto rollback; + goto alloc_rollback; } dst_opts[i] = kmemdup(option, sizeof(*option), GFP_KERNEL); if (!dst_opts[i]) { err = -ENOMEM; - goto rollback; + goto alloc_rollback; } } for (i = 0; i < option_count; i++) { - dst_opts[i]->changed = true; - dst_opts[i]->removed = false; + err = __team_option_inst_add_option(team, dst_opts[i]); + if (err) + goto inst_rollback; list_add_tail(&dst_opts[i]->list, &team->option_list); } kfree(dst_opts); return 0; -rollback: - for (i = 0; i < option_count; i++) +inst_rollback: + for (i--; i >= 0; i--) + __team_option_inst_del_option(team, dst_opts[i]); + + i = option_count - 1; +alloc_rollback: + for (i--; i >= 0; i--) kfree(dst_opts[i]); kfree(dst_opts); @@ -143,10 +270,8 @@ static void __team_options_mark_removed(struct team *team, struct team_option *del_opt; del_opt = __team_find_option(team, option->name); - if (del_opt) { - del_opt->changed = true; - del_opt->removed = true; - } + if (del_opt) + __team_option_inst_mark_removed_option(team, del_opt); } } @@ -161,6 +286,7 @@ static void __team_options_unregister(struct team *team, del_opt = __team_find_option(team, option->name); if (del_opt) { + __team_option_inst_del_option(team, del_opt); list_del(&del_opt->list); kfree(del_opt); } @@ -193,22 +319,42 @@ void team_options_unregister(struct team *team, } EXPORT_SYMBOL(team_options_unregister); -static int team_option_get(struct team *team, struct team_option *option, - void *arg) +static int team_option_port_add(struct team *team, struct team_port *port) { - return option->getter(team, arg); + int err; + + err = __team_option_inst_add_port(team, port); + if (err) + return err; + __team_options_change_check(team); + return 0; } -static int team_option_set(struct team *team, struct team_option *option, - void *arg) +static void team_option_port_del(struct team *team, struct team_port *port) +{ + __team_option_inst_mark_removed_port(team, port); + __team_options_change_check(team); + __team_option_inst_del_port(team, port); +} + +static int team_option_get(struct team *team, + struct team_option_inst *opt_inst, + struct team_gsetter_ctx *ctx) +{ + return opt_inst->option->getter(team, ctx); +} + +static int team_option_set(struct team *team, + struct team_option_inst *opt_inst, + struct team_gsetter_ctx *ctx) { int err; - err = option->setter(team, arg); + err = opt_inst->option->setter(team, ctx); if (err) return err; - option->changed = true; + opt_inst->changed = true; __team_options_change_check(team); return err; } @@ -642,6 +788,13 @@ static int team_port_add(struct team *team, struct net_device *port_dev) goto err_handler_register; } + err = team_option_port_add(team, port); + if (err) { + netdev_err(dev, "Device %s failed to add per-port options\n", + portname); + goto err_option_port_add; + } + team_port_list_add_port(team, port); team_adjust_ops(team); __team_compute_features(team); @@ -651,6 +804,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev) return 0; +err_option_port_add: + netdev_rx_handler_unregister(port_dev); + err_handler_register: netdev_set_master(port_dev, NULL); @@ -690,6 +846,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) __team_port_change_check(port, false); team_port_list_del_port(team, port); team_adjust_ops(team); + team_option_port_del(team, port); netdev_rx_handler_unregister(port_dev); netdev_set_master(port_dev, NULL); vlan_vids_del_by_dev(port_dev, dev); @@ -712,19 +869,15 @@ static int team_port_del(struct team *team, struct net_device *port_dev) static const char team_no_mode_kind[] = "*NOMODE*"; -static int team_mode_option_get(struct team *team, void *arg) +static int team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx) { - const char **str = arg; - - *str = team->mode ? team->mode->kind : team_no_mode_kind; + ctx->data.str_val = team->mode ? team->mode->kind : team_no_mode_kind; return 0; } -static int team_mode_option_set(struct team *team, void *arg) +static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx) { - const char **str = arg; - - return team_change_mode(team, *str); + return team_change_mode(team, ctx->data.str_val); } static const struct team_option team_options[] = { @@ -756,6 +909,7 @@ static int team_init(struct net_device *dev) team_adjust_ops(team); INIT_LIST_HEAD(&team->option_list); + INIT_LIST_HEAD(&team->option_inst_list); err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); if (err) goto err_options_register; @@ -1238,7 +1392,8 @@ static int team_nl_fill_options_get(struct sk_buff *skb, { struct nlattr *option_list; void *hdr; - struct team_option *option; + struct team_option_inst *opt_inst; + int err; hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags, TEAM_CMD_OPTIONS_GET); @@ -1251,50 +1406,61 @@ static int team_nl_fill_options_get(struct sk_buff *skb, if (!option_list) return -EMSGSIZE; - list_for_each_entry(option, &team->option_list, list) { + list_for_each_entry(opt_inst, &team->option_inst_list, list) { struct nlattr *option_item; - long arg; - struct team_option_binary tbinary; + struct team_option *option = opt_inst->option; + struct team_gsetter_ctx ctx; /* Include only changed options if fill all mode is not on */ - if (!fillall && !option->changed) + if (!fillall && !opt_inst->changed) continue; option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); if (!option_item) goto nla_put_failure; if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) goto nla_put_failure; - if (option->changed) { + if (opt_inst->changed) { if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) goto nla_put_failure; - option->changed = false; + opt_inst->changed = false; } - if (option->removed && + if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) goto nla_put_failure; + if (opt_inst->port && + nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX, + opt_inst->port->dev->ifindex)) + goto nla_put_failure; + ctx.port = opt_inst->port; switch (option->type) { case TEAM_OPTION_TYPE_U32: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) goto nla_put_failure; - team_option_get(team, option, &arg); - if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, arg)) + err = team_option_get(team, opt_inst, &ctx); + if (err) + goto errout; + if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, + ctx.data.u32_val)) goto nla_put_failure; break; case TEAM_OPTION_TYPE_STRING: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING)) goto nla_put_failure; - team_option_get(team, option, &arg); + err = team_option_get(team, opt_inst, &ctx); + if (err) + goto errout; if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA, - (char *) arg)) + ctx.data.str_val)) goto nla_put_failure; break; case TEAM_OPTION_TYPE_BINARY: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) goto nla_put_failure; - arg = (long) &tbinary; - team_option_get(team, option, &arg); + err = team_option_get(team, opt_inst, &ctx); + if (err) + goto errout; if (nla_put(skb, TEAM_ATTR_OPTION_DATA, - tbinary.data_len, tbinary.data)) + ctx.data.bin_val.len, ctx.data.bin_val.ptr)) goto nla_put_failure; break; default: @@ -1307,8 +1473,10 @@ static int team_nl_fill_options_get(struct sk_buff *skb, return genlmsg_end(skb, hdr); nla_put_failure: + err = -EMSGSIZE; +errout: genlmsg_cancel(skb, hdr); - return -EMSGSIZE; + return err; } static int team_nl_fill_options_get_all(struct sk_buff *skb, @@ -1354,9 +1522,11 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) } nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) { - struct nlattr *mode_attrs[TEAM_ATTR_OPTION_MAX + 1]; + struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1]; + struct nlattr *attr_port_ifindex; enum team_option_type opt_type; - struct team_option *option; + int opt_port_ifindex = 0; /* != 0 for per-port options */ + struct team_option_inst *opt_inst; char *opt_name; bool opt_found = false; @@ -1364,17 +1534,17 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) err = -EINVAL; goto team_put; } - err = nla_parse_nested(mode_attrs, TEAM_ATTR_OPTION_MAX, + err = nla_parse_nested(opt_attrs, TEAM_ATTR_OPTION_MAX, nl_option, team_nl_option_policy); if (err) goto team_put; - if (!mode_attrs[TEAM_ATTR_OPTION_NAME] || - !mode_attrs[TEAM_ATTR_OPTION_TYPE] || - !mode_attrs[TEAM_ATTR_OPTION_DATA]) { + if (!opt_attrs[TEAM_ATTR_OPTION_NAME] || + !opt_attrs[TEAM_ATTR_OPTION_TYPE] || + !opt_attrs[TEAM_ATTR_OPTION_DATA]) { err = -EINVAL; goto team_put; } - switch (nla_get_u8(mode_attrs[TEAM_ATTR_OPTION_TYPE])) { + switch (nla_get_u8(opt_attrs[TEAM_ATTR_OPTION_TYPE])) { case NLA_U32: opt_type = TEAM_OPTION_TYPE_U32; break; @@ -1388,39 +1558,47 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) goto team_put; } - opt_name = nla_data(mode_attrs[TEAM_ATTR_OPTION_NAME]); - list_for_each_entry(option, &team->option_list, list) { - long arg; + opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]); + attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]; + if (attr_port_ifindex) + opt_port_ifindex = nla_get_u32(attr_port_ifindex); + + list_for_each_entry(opt_inst, &team->option_inst_list, list) { + struct team_option *option = opt_inst->option; struct nlattr *opt_data_attr; - struct team_option_binary tbinary; + struct team_gsetter_ctx ctx; int data_len; + int tmp_ifindex; + tmp_ifindex = opt_inst->port ? + opt_inst->port->dev->ifindex : 0; if (option->type != opt_type || - strcmp(option->name, opt_name)) + strcmp(option->name, opt_name) || + tmp_ifindex != opt_port_ifindex) continue; opt_found = true; - opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA]; + opt_data_attr = opt_attrs[TEAM_ATTR_OPTION_DATA]; data_len = nla_len(opt_data_attr); + ctx.port = opt_inst->port; switch (opt_type) { case TEAM_OPTION_TYPE_U32: - arg = nla_get_u32(opt_data_attr); + ctx.data.u32_val = nla_get_u32(opt_data_attr); break; case TEAM_OPTION_TYPE_STRING: if (data_len > TEAM_STRING_MAX_LEN) { err = -EINVAL; goto team_put; } - arg = (long) nla_data(opt_data_attr); + ctx.data.str_val = nla_data(opt_data_attr); break; case TEAM_OPTION_TYPE_BINARY: - tbinary.data_len = data_len; - tbinary.data = nla_data(opt_data_attr); - arg = (long) &tbinary; + ctx.data.bin_val.len = data_len; + ctx.data.bin_val.ptr = nla_data(opt_data_attr); break; default: BUG(); } - err = team_option_set(team, option, &arg); + err = team_option_set(team, opt_inst, &ctx); if (err) goto team_put; } diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index f4d960e82e29..6cde1ab8fff0 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -59,23 +59,21 @@ static void ab_port_leave(struct team *team, struct team_port *port) RCU_INIT_POINTER(ab_priv(team)->active_port, NULL); } -static int ab_active_port_get(struct team *team, void *arg) +static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx) { - u32 *ifindex = arg; - - *ifindex = 0; if (ab_priv(team)->active_port) - *ifindex = ab_priv(team)->active_port->dev->ifindex; + ctx->data.u32_val = ab_priv(team)->active_port->dev->ifindex; + else + ctx->data.u32_val = 0; return 0; } -static int ab_active_port_set(struct team *team, void *arg) +static int ab_active_port_set(struct team *team, struct team_gsetter_ctx *ctx) { - u32 *ifindex = arg; struct team_port *port; list_for_each_entry_rcu(port, &team->port_list, list) { - if (port->dev->ifindex == *ifindex) { + if (port->dev->ifindex == ctx->data.u32_val) { rcu_assign_pointer(ab_priv(team)->active_port, port); return 0; } diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index ed20f395be6f..167cdb4fe76e 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -52,22 +52,21 @@ drop: return false; } -static int lb_bpf_func_get(struct team *team, void *arg) +static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx) { - struct team_option_binary *tbinary = team_optarg_tbinary(arg); - - memset(tbinary, 0, sizeof(*tbinary)); - if (!lb_priv(team)->orig_fprog) + if (!lb_priv(team)->orig_fprog) { + ctx->data.bin_val.len = 0; + ctx->data.bin_val.ptr = NULL; return 0; - - tbinary->data_len = lb_priv(team)->orig_fprog->len * - sizeof(struct sock_filter); - tbinary->data = lb_priv(team)->orig_fprog->filter; + } + ctx->data.bin_val.len = lb_priv(team)->orig_fprog->len * + sizeof(struct sock_filter); + ctx->data.bin_val.ptr = lb_priv(team)->orig_fprog->filter; return 0; } static int __fprog_create(struct sock_fprog **pfprog, u32 data_len, - void *data) + const void *data) { struct sock_fprog *fprog; struct sock_filter *filter = (struct sock_filter *) data; @@ -93,16 +92,15 @@ static void __fprog_destroy(struct sock_fprog *fprog) kfree(fprog); } -static int lb_bpf_func_set(struct team *team, void *arg) +static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) { - struct team_option_binary *tbinary = team_optarg_tbinary(arg); struct sk_filter *fp = NULL; struct sock_fprog *fprog = NULL; int err; - if (tbinary->data_len) { - err = __fprog_create(&fprog, tbinary->data_len, - tbinary->data); + if (ctx->data.bin_val.len) { + err = __fprog_create(&fprog, ctx->data.bin_val.len, + ctx->data.bin_val.ptr); if (err) return err; err = sk_unattached_filter_create(&fp, fprog); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 41163ac14ab4..6f27c841c9a8 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -71,25 +71,27 @@ enum team_option_type { TEAM_OPTION_TYPE_BINARY, }; +struct team_gsetter_ctx { + union { + u32 u32_val; + const char *str_val; + struct { + const void *ptr; + u32 len; + } bin_val; + } data; + struct team_port *port; +}; + struct team_option { struct list_head list; const char *name; + bool per_port; enum team_option_type type; - int (*getter)(struct team *team, void *arg); - int (*setter)(struct team *team, void *arg); - - /* Custom gennetlink interface related flags */ - bool changed; - bool removed; + int (*getter)(struct team *team, struct team_gsetter_ctx *ctx); + int (*setter)(struct team *team, struct team_gsetter_ctx *ctx); }; -struct team_option_binary { - u32 data_len; - void *data; -}; - -#define team_optarg_tbinary(arg) (*((struct team_option_binary **) arg)) - struct team_mode { struct list_head list; const char *kind; @@ -118,6 +120,7 @@ struct team { struct list_head port_list; struct list_head option_list; + struct list_head option_inst_list; /* list of option instances */ const struct team_mode *mode; struct team_mode_ops ops; @@ -224,6 +227,7 @@ enum { TEAM_ATTR_OPTION_TYPE, /* u8 */ TEAM_ATTR_OPTION_DATA, /* dynamic */ TEAM_ATTR_OPTION_REMOVED, /* flag */ + TEAM_ATTR_OPTION_PORT_IFINDEX, /* u32 */ /* for per-port options */ __TEAM_ATTR_OPTION_MAX, TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1, -- cgit v1.2.3 From 14f066bab19946545130a7379f420af860a02ae8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 10 Apr 2012 05:15:43 +0000 Subject: team: add bool option type Add another (hopefully last) option type. Use NLA_FLAG to implement that. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 40 +++++++++++++++++++++++++++++----------- include/linux/if_team.h | 2 ++ 2 files changed, 31 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index eaf8441753ce..2645fae26d6f 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1463,6 +1463,16 @@ static int team_nl_fill_options_get(struct sk_buff *skb, ctx.data.bin_val.len, ctx.data.bin_val.ptr)) goto nla_put_failure; break; + case TEAM_OPTION_TYPE_BOOL: + if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG)) + goto nla_put_failure; + err = team_option_get(team, opt_inst, &ctx); + if (err) + goto errout; + if (ctx.data.bool_val && + nla_put_flag(skb, TEAM_ATTR_OPTION_DATA)) + goto nla_put_failure; + break; default: BUG(); } @@ -1524,6 +1534,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) { struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1]; struct nlattr *attr_port_ifindex; + struct nlattr *attr_data; enum team_option_type opt_type; int opt_port_ifindex = 0; /* != 0 for per-port options */ struct team_option_inst *opt_inst; @@ -1539,8 +1550,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) if (err) goto team_put; if (!opt_attrs[TEAM_ATTR_OPTION_NAME] || - !opt_attrs[TEAM_ATTR_OPTION_TYPE] || - !opt_attrs[TEAM_ATTR_OPTION_DATA]) { + !opt_attrs[TEAM_ATTR_OPTION_TYPE]) { err = -EINVAL; goto team_put; } @@ -1554,10 +1564,19 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) case NLA_BINARY: opt_type = TEAM_OPTION_TYPE_BINARY; break; + case NLA_FLAG: + opt_type = TEAM_OPTION_TYPE_BOOL; + break; default: goto team_put; } + attr_data = opt_attrs[TEAM_ATTR_OPTION_DATA]; + if (opt_type != TEAM_OPTION_TYPE_BOOL && !attr_data) { + err = -EINVAL; + goto team_put; + } + opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]); attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]; if (attr_port_ifindex) @@ -1565,9 +1584,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) list_for_each_entry(opt_inst, &team->option_inst_list, list) { struct team_option *option = opt_inst->option; - struct nlattr *opt_data_attr; struct team_gsetter_ctx ctx; - int data_len; int tmp_ifindex; tmp_ifindex = opt_inst->port ? @@ -1577,23 +1594,24 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) tmp_ifindex != opt_port_ifindex) continue; opt_found = true; - opt_data_attr = opt_attrs[TEAM_ATTR_OPTION_DATA]; - data_len = nla_len(opt_data_attr); ctx.port = opt_inst->port; switch (opt_type) { case TEAM_OPTION_TYPE_U32: - ctx.data.u32_val = nla_get_u32(opt_data_attr); + ctx.data.u32_val = nla_get_u32(attr_data); break; case TEAM_OPTION_TYPE_STRING: - if (data_len > TEAM_STRING_MAX_LEN) { + if (nla_len(attr_data) > TEAM_STRING_MAX_LEN) { err = -EINVAL; goto team_put; } - ctx.data.str_val = nla_data(opt_data_attr); + ctx.data.str_val = nla_data(attr_data); break; case TEAM_OPTION_TYPE_BINARY: - ctx.data.bin_val.len = data_len; - ctx.data.bin_val.ptr = nla_data(opt_data_attr); + ctx.data.bin_val.len = nla_len(attr_data); + ctx.data.bin_val.ptr = nla_data(attr_data); + break; + case TEAM_OPTION_TYPE_BOOL: + ctx.data.bool_val = attr_data ? true : false; break; default: BUG(); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 6f27c841c9a8..78c84fd9a170 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -69,6 +69,7 @@ enum team_option_type { TEAM_OPTION_TYPE_U32, TEAM_OPTION_TYPE_STRING, TEAM_OPTION_TYPE_BINARY, + TEAM_OPTION_TYPE_BOOL, }; struct team_gsetter_ctx { @@ -79,6 +80,7 @@ struct team_gsetter_ctx { const void *ptr; u32 len; } bin_val; + bool bool_val; } data; struct team_port *port; }; -- cgit v1.2.3 From 71472ec12c61dd305ab4d11822af7ecc4f9717f9 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 10 Apr 2012 05:15:44 +0000 Subject: team: add user_linkup and user_linkup_enabled per-port option Allows userspace to setup linkup for ports. Default is to take linkup directly from ethtool state. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 72 ++++++++++++++++++++++++++++++++++++++++++------- include/linux/if_team.h | 26 ++++++++++++------ 2 files changed, 81 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 2645fae26d6f..e639abecd14f 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -76,6 +76,11 @@ int team_port_set_team_mac(struct team_port *port) } EXPORT_SYMBOL(team_port_set_team_mac); +static void team_refresh_port_linkup(struct team_port *port) +{ + port->linkup = port->user.linkup_enabled ? port->user.linkup : + port->state.linkup; +} /******************* * Options handling @@ -880,6 +885,40 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx) return team_change_mode(team, ctx->data.str_val); } +static int team_user_linkup_option_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + ctx->data.bool_val = ctx->port->user.linkup; + return 0; +} + +static int team_user_linkup_option_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + ctx->port->user.linkup = ctx->data.bool_val; + team_refresh_port_linkup(ctx->port); + return 0; +} + +static int team_user_linkup_en_option_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct team_port *port = ctx->port; + + ctx->data.bool_val = port->user.linkup_enabled; + return 0; +} + +static int team_user_linkup_en_option_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct team_port *port = ctx->port; + + port->user.linkup_enabled = ctx->data.bool_val; + team_refresh_port_linkup(ctx->port); + return 0; +} + static const struct team_option team_options[] = { { .name = "mode", @@ -887,6 +926,20 @@ static const struct team_option team_options[] = { .getter = team_mode_option_get, .setter = team_mode_option_set, }, + { + .name = "user_linkup", + .type = TEAM_OPTION_TYPE_BOOL, + .per_port = true, + .getter = team_user_linkup_option_get, + .setter = team_user_linkup_option_set, + }, + { + .name = "user_linkup_enabled", + .type = TEAM_OPTION_TYPE_BOOL, + .per_port = true, + .getter = team_user_linkup_en_option_get, + .setter = team_user_linkup_en_option_set, + }, }; static int team_init(struct net_device *dev) @@ -1670,10 +1723,10 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb, } if ((port->removed && nla_put_flag(skb, TEAM_ATTR_PORT_REMOVED)) || - (port->linkup && + (port->state.linkup && nla_put_flag(skb, TEAM_ATTR_PORT_LINKUP)) || - nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->speed) || - nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex)) + nla_put_u32(skb, TEAM_ATTR_PORT_SPEED, port->state.speed) || + nla_put_u8(skb, TEAM_ATTR_PORT_DUPLEX, port->state.duplex)) goto nla_put_failure; nla_nest_end(skb, port_item); } @@ -1833,23 +1886,24 @@ static void __team_port_change_check(struct team_port *port, bool linkup) { int err; - if (!port->removed && port->linkup == linkup) + if (!port->removed && port->state.linkup == linkup) return; port->changed = true; - port->linkup = linkup; + port->state.linkup = linkup; + team_refresh_port_linkup(port); if (linkup) { struct ethtool_cmd ecmd; err = __ethtool_get_settings(port->dev, &ecmd); if (!err) { - port->speed = ethtool_cmd_speed(&ecmd); - port->duplex = ecmd.duplex; + port->state.speed = ethtool_cmd_speed(&ecmd); + port->state.duplex = ecmd.duplex; goto send_event; } } - port->speed = 0; - port->duplex = 0; + port->state.speed = 0; + port->state.duplex = 0; send_event: err = team_nl_send_event_port_list_get(port->team); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 78c84fd9a170..5fd5ab171165 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -33,6 +33,24 @@ struct team_port { struct team *team; int index; + bool linkup; /* either state.linkup or user.linkup */ + + struct { + bool linkup; + u32 speed; + u8 duplex; + } state; + + /* Values set by userspace */ + struct { + bool linkup; + bool linkup_enabled; + } user; + + /* Custom gennetlink interface related flags */ + bool changed; + bool removed; + /* * A place for storing original values of the device before it * become a port. @@ -42,14 +60,6 @@ struct team_port { unsigned int mtu; } orig; - bool linkup; - u32 speed; - u8 duplex; - - /* Custom gennetlink interface related flags */ - bool changed; - bool removed; - struct rcu_head rcu; }; -- cgit v1.2.3 From 86f82d561864e902c70282b6f17cf590c0f34691 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 10 Apr 2012 10:16:36 -0700 Subject: cgroup: remove cgroup_subsys->populate() With memcg converted, cgroup_subsys->populate() doesn't have any user left. Remove it. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 1 - kernel/cgroup.c | 3 --- 2 files changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 565c8034e6c8..d3f5fba2c159 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -472,7 +472,6 @@ struct cgroup_subsys { void (*fork)(struct task_struct *task); void (*exit)(struct cgroup *cgrp, struct cgroup *old_cgrp, struct task_struct *task); - int (*populate)(struct cgroup_subsys *ss, struct cgroup *cgrp); void (*post_clone)(struct cgroup *cgrp); void (*bind)(struct cgroup *root); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 2905977e0f33..b2f203f25ec8 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3842,9 +3842,6 @@ static int cgroup_populate_dir(struct cgroup *cgrp) for_each_subsys(cgrp->root, ss) { struct cftype_set *set; - if (ss->populate && (err = ss->populate(ss, cgrp)) < 0) - return err; - list_for_each_entry(set, &ss->cftsets, node) cgroup_addrm_files(cgrp, ss, set->cfts, true); } -- cgit v1.2.3 From 5314526b1743e8e8614293db7d86e480b4fe9824 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Fri, 6 Apr 2012 13:35:47 -0700 Subject: cfg80211: add channel switch notify event The firmware may decide to switch channels while already beaconing, e.g. in response to a cfg80211 connect request on a different vif. Add this event to notify userspace when an AP or GO interface has successfully migrated to a new channel, so it can update its configuration accordingly. Signed-off-by: Thomas Pedersen Signed-off-by: John W. Linville --- include/linux/nl80211.h | 7 +++++++ include/net/cfg80211.h | 11 +++++++++++ net/wireless/mlme.c | 27 +++++++++++++++++++++++++++ net/wireless/nl80211.c | 32 ++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 4 ++++ 5 files changed, 81 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c6d26328a166..1335084b1c69 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -548,6 +548,11 @@ * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether * No Acknowledgement Policy should be applied. * + * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels + * independently of the userspace SME, send this event indicating + * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -689,6 +694,8 @@ enum nl80211_commands { NL80211_CMD_SET_NOACK_MAP, + NL80211_CMD_CH_SWITCH_NOTIFY, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1fd1c4acfc8e..a587867375b2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3354,6 +3354,17 @@ int cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); +/* + * cfg80211_ch_switch_notify - update wdev channel and notify userspace + * @dev: the device which switched channels + * @freq: new channel frequency (in MHz) + * @type: channel type + * + * Acquires wdev_lock, so must only be called from sleepable driver context! + */ +void cfg80211_ch_switch_notify(struct net_device *dev, int freq, + enum nl80211_channel_type type); + /* * cfg80211_calculate_bitrate - calculate actual bitrate (in 100Kbps units) * @rate: given rate_info to calculate bitrate from diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index e14fdcc1d7cd..6801d96bc224 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -930,6 +930,33 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); +void cfg80211_ch_switch_notify(struct net_device *dev, int freq, + enum nl80211_channel_type type) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct ieee80211_channel *chan; + + wdev_lock(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO)) + goto out; + + chan = rdev_freq_to_chan(rdev, freq, type); + if (WARN_ON(!chan)) + goto out; + + wdev->channel = chan; + + nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL); +out: + wdev_unlock(wdev); + return; +} +EXPORT_SYMBOL(cfg80211_ch_switch_notify); + bool cfg80211_rx_spurious_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 40e5620e5fde..c3e82af72bf5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7922,6 +7922,38 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int freq, + enum nl80211_channel_type type, gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 4ffe50df9f31..01a1122c3b33 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -118,6 +118,10 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, int index, const u8 *bssid, bool preauth, gfp_t gfp); +void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, + struct net_device *dev, int freq, + enum nl80211_channel_type type, gfp_t gfp); + bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp); bool nl80211_unexpected_4addr_frame(struct net_device *dev, -- cgit v1.2.3 From a9aa53df6e6c768fc0f25a7c80ba586b0290720a Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 29 Mar 2012 19:18:19 -0400 Subject: svcauth: remove unused define Signed-off-by: Simo Sorce --- include/linux/sunrpc/svcauth.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index 548790e9113b..2e2af101b59c 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -16,7 +16,6 @@ #include #include -#define SVC_CRED_NGROUPS 32 struct svc_cred { uid_t cr_uid; gid_t cr_gid; -- cgit v1.2.3 From db3a35326362624dd4d8473e676d63afa52bedcc Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 28 Mar 2012 19:09:08 +0400 Subject: nfsd: add link to owner cache detail to svc_export structure Without info about owner cache datail it won't be able to find out, which per-net cache detail have to be. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 10 +++++----- include/linux/nfsd/export.h | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 71c5ce35a1a5..99ea4c00240c 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -525,6 +525,7 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) goto out1; exp.ex_client = dom; + exp.cd = cd; /* expiry */ err = -EINVAL; @@ -672,6 +673,7 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem) new->ex_fslocs.locations = NULL; new->ex_fslocs.locations_count = 0; new->ex_fslocs.migrated = 0; + new->cd = item->cd; } static void export_update(struct cache_head *cnew, struct cache_head *citem) @@ -739,8 +741,7 @@ svc_export_lookup(struct svc_export *exp) struct cache_head *ch; int hash = svc_export_hash(exp); - ch = sunrpc_cache_lookup(&svc_export_cache, &exp->h, - hash); + ch = sunrpc_cache_lookup(exp->cd, &exp->h, hash); if (ch) return container_of(ch, struct svc_export, h); else @@ -753,9 +754,7 @@ svc_export_update(struct svc_export *new, struct svc_export *old) struct cache_head *ch; int hash = svc_export_hash(old); - ch = sunrpc_cache_update(&svc_export_cache, &new->h, - &old->h, - hash); + ch = sunrpc_cache_update(old->cd, &new->h, &old->h, hash); if (ch) return container_of(ch, struct svc_export, h); else @@ -797,6 +796,7 @@ static svc_export *exp_get_by_name(svc_client *clp, const struct path *path, key.ex_client = clp; key.ex_path = *path; + key.cd = &svc_export_cache; exp = svc_export_lookup(&key); if (exp == NULL) diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index f85308e688fd..64455292bbba 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -103,6 +103,7 @@ struct svc_export { struct nfsd4_fs_locations ex_fslocs; int ex_nflavors; struct exp_flavor_info ex_flavors[MAX_SECINFO_LIST]; + struct cache_detail *cd; }; /* an "export key" (expkey) maps a filehandlefragement to an -- cgit v1.2.3 From 71234978e81ee515c8025d087a197561b311c183 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 28 Mar 2012 19:09:15 +0400 Subject: nfsd: use cache detail pointer from svc_export structure on cache put Hard-coded pointer is redundant now and can be replaced. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- include/linux/nfsd/export.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 64455292bbba..485c2afa96f7 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -147,7 +147,7 @@ extern struct cache_detail svc_export_cache; static inline void exp_put(struct svc_export *exp) { - cache_put(&exp->h, &svc_export_cache); + cache_put(&exp->h, exp->cd); } static inline void exp_get(struct svc_export *exp) -- cgit v1.2.3 From e3f70eadb7dddfb5a2bb9afff7abfc6ee17a29d0 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Thu, 29 Mar 2012 18:54:33 +0400 Subject: Lockd: pass network namespace to creation and destruction routines v2: dereference of most probably already released nlm_host removed in nlmclnt_done() and reclaimer(). These routines are called from locks reclaimer() kernel thread. This thread works in "init_net" network context and currently relays on persence on lockd thread and it's per-net resources. Thus lockd_up() and lockd_down() can't relay on current network context. So let's pass corrent one into them. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/lockd/clntlock.c | 13 ++++++++----- fs/lockd/svc.c | 7 +++---- fs/nfsd/nfssvc.c | 6 +++--- include/linux/lockd/bind.h | 4 ++-- 4 files changed, 16 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index ba1dc2eebd1e..ca0a08001449 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -56,7 +56,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4; int status; - status = lockd_up(); + status = lockd_up(nlm_init->net); if (status < 0) return ERR_PTR(status); @@ -65,7 +65,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) nlm_init->hostname, nlm_init->noresvport, nlm_init->net); if (host == NULL) { - lockd_down(); + lockd_down(nlm_init->net); return ERR_PTR(-ENOLCK); } @@ -80,8 +80,10 @@ EXPORT_SYMBOL_GPL(nlmclnt_init); */ void nlmclnt_done(struct nlm_host *host) { + struct net *net = host->net; + nlmclnt_release_host(host); - lockd_down(); + lockd_down(net); } EXPORT_SYMBOL_GPL(nlmclnt_done); @@ -220,11 +222,12 @@ reclaimer(void *ptr) struct nlm_wait *block; struct file_lock *fl, *next; u32 nsmstate; + struct net *net = host->net; allow_signal(SIGKILL); down_write(&host->h_rwsem); - lockd_up(); /* note: this cannot fail as lockd is already running */ + lockd_up(net); /* note: this cannot fail as lockd is already running */ dprintk("lockd: reclaiming locks for host %s\n", host->h_name); @@ -275,6 +278,6 @@ restart: /* Release host handle after use */ nlmclnt_release_host(host); - lockd_down(); + lockd_down(net); return 0; } diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index f49b9afc4436..1ead0750cdbb 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -295,11 +295,10 @@ static void lockd_down_net(struct net *net) /* * Bring up the lockd process if it's not already up. */ -int lockd_up(void) +int lockd_up(struct net *net) { struct svc_serv *serv; int error = 0; - struct net *net = current->nsproxy->net_ns; mutex_lock(&nlmsvc_mutex); /* @@ -378,12 +377,12 @@ EXPORT_SYMBOL_GPL(lockd_up); * Decrement the user count and bring down lockd if we're the last. */ void -lockd_down(void) +lockd_down(struct net *net) { mutex_lock(&nlmsvc_mutex); if (nlmsvc_users) { if (--nlmsvc_users) { - lockd_down_net(current->nsproxy->net_ns); + lockd_down_net(net); goto out; } } else { diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 28dfad39f0c5..78e521392df1 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -220,7 +220,7 @@ static int nfsd_startup(unsigned short port, int nrservs) ret = nfsd_init_socks(port); if (ret) goto out_racache; - ret = lockd_up(); + ret = lockd_up(&init_net); if (ret) goto out_racache; ret = nfs4_state_start(); @@ -229,7 +229,7 @@ static int nfsd_startup(unsigned short port, int nrservs) nfsd_up = true; return 0; out_lockd: - lockd_down(); + lockd_down(&init_net); out_racache: nfsd_racache_shutdown(); return ret; @@ -246,7 +246,7 @@ static void nfsd_shutdown(void) if (!nfsd_up) return; nfs4_state_shutdown(); - lockd_down(); + lockd_down(&init_net); nfsd_racache_shutdown(); nfsd_up = false; } diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h index 11a966e5f829..4d24d64578c4 100644 --- a/include/linux/lockd/bind.h +++ b/include/linux/lockd/bind.h @@ -54,7 +54,7 @@ extern void nlmclnt_done(struct nlm_host *host); extern int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl); -extern int lockd_up(void); -extern void lockd_down(void); +extern int lockd_up(struct net *net); +extern void lockd_down(struct net *net); #endif /* LINUX_LOCKD_BIND_H */ -- cgit v1.2.3 From b89109bef4a6a4a8ab5788778ee0addca0787870 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 11 Apr 2012 15:13:14 +0400 Subject: nfsd: pass network context to export caches init/shutdown routines These functions will be called from per-net operations. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 20 ++++++++++---------- fs/nfsd/nfsctl.c | 6 +++--- include/linux/nfsd/export.h | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 84723bc37c59..6453669dcef7 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1228,17 +1228,17 @@ const struct seq_operations nfs_exports_op = { * Initialize the exports module. */ int -nfsd_export_init(void) +nfsd_export_init(struct net *net) { int rv; - dprintk("nfsd: initializing export module.\n"); + dprintk("nfsd: initializing export module (net: %p).\n", net); - rv = cache_register_net(&svc_export_cache, &init_net); + rv = cache_register_net(&svc_export_cache, net); if (rv) return rv; - rv = cache_register_net(&svc_expkey_cache, &init_net); + rv = cache_register_net(&svc_expkey_cache, net); if (rv) - cache_unregister_net(&svc_export_cache, &init_net); + cache_unregister_net(&svc_export_cache, net); return rv; } @@ -1257,14 +1257,14 @@ nfsd_export_flush(void) * Shutdown the exports module. */ void -nfsd_export_shutdown(void) +nfsd_export_shutdown(struct net *net) { - dprintk("nfsd: shutting down export module.\n"); + dprintk("nfsd: shutting down export module (net: %p).\n", net); - cache_unregister_net(&svc_expkey_cache, &init_net); - cache_unregister_net(&svc_export_cache, &init_net); + cache_unregister_net(&svc_expkey_cache, net); + cache_unregister_net(&svc_export_cache, net); svcauth_unix_purge(); - dprintk("nfsd: export shutdown complete.\n"); + dprintk("nfsd: export shutdown complete (net: %p).\n", net); } diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index ae19293e68df..bc76f8ebbe5e 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -1163,7 +1163,7 @@ static int __init init_nfsd(void) retval = nfsd_reply_cache_init(); if (retval) goto out_free_stat; - retval = nfsd_export_init(); + retval = nfsd_export_init(&init_net); if (retval) goto out_free_cache; nfsd_lockd_init(); /* lockd->nfsd callbacks */ @@ -1184,7 +1184,7 @@ out_free_idmap: nfsd_idmap_shutdown(); out_free_lockd: nfsd_lockd_shutdown(); - nfsd_export_shutdown(); + nfsd_export_shutdown(&init_net); out_free_cache: nfsd_reply_cache_shutdown(); out_free_stat: @@ -1201,7 +1201,7 @@ out_unregister_notifier: static void __exit exit_nfsd(void) { - nfsd_export_shutdown(); + nfsd_export_shutdown(&init_net); nfsd_reply_cache_shutdown(); remove_proc_entry("fs/nfs/exports", NULL); remove_proc_entry("fs/nfs", NULL); diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 485c2afa96f7..375096c083d3 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -130,8 +130,8 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp); /* * Function declarations */ -int nfsd_export_init(void); -void nfsd_export_shutdown(void); +int nfsd_export_init(struct net *); +void nfsd_export_shutdown(struct net *); void nfsd_export_flush(void); struct svc_export * rqst_exp_get_by_name(struct svc_rqst *, struct path *); -- cgit v1.2.3 From b3853e0ea1f2ef58f7e7c03e47819e2ae3766dea Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 11 Apr 2012 15:13:21 +0400 Subject: nfsd: make export cache allocated per network namespace context This patch also changes prototypes of nfsd_export_flush() and exp_rootfh(): network namespace parameter added. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 47 ++++++++++++++++++++++++++++++--------------- fs/nfsd/netns.h | 2 ++ fs/nfsd/nfsctl.c | 2 +- fs/nfsd/nfssvc.c | 2 +- include/linux/nfsd/export.h | 4 ++-- 5 files changed, 38 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 688264b55a3a..84d020fc0e37 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -15,11 +15,13 @@ #include #include #include +#include #include #include "nfsd.h" #include "nfsfh.h" +#include "netns.h" #define NFSDDBG_FACILITY NFSDDBG_EXPORT @@ -298,8 +300,6 @@ svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new, #define EXPORT_HASHBITS 8 #define EXPORT_HASHMAX (1<< EXPORT_HASHBITS) -static struct cache_head *export_table[EXPORT_HASHMAX]; - static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc) { int i; @@ -708,10 +708,9 @@ static struct cache_head *svc_export_alloc(void) return NULL; } -struct cache_detail svc_export_cache = { +struct cache_detail svc_export_cache_template = { .owner = THIS_MODULE, .hash_size = EXPORT_HASHMAX, - .hash_table = export_table, .name = "nfsd.export", .cache_put = svc_export_put, .cache_upcall = svc_export_upcall, @@ -835,7 +834,7 @@ static struct svc_export *exp_parent(struct cache_detail *cd, svc_client *clp, * since its harder to fool a kernel module than a user space program. */ int -exp_rootfh(svc_client *clp, char *name, +exp_rootfh(struct net *net, svc_client *clp, char *name, struct knfsd_fh *f, int maxsize) { struct svc_export *exp; @@ -843,7 +842,8 @@ exp_rootfh(svc_client *clp, char *name, struct inode *inode; struct svc_fh fh; int err; - struct cache_detail *cd = &svc_export_cache; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct cache_detail *cd = nn->svc_export_cache; err = -EPERM; /* NB: we probably ought to check that it's NUL-terminated */ @@ -930,7 +930,8 @@ struct svc_export * rqst_exp_get_by_name(struct svc_rqst *rqstp, struct path *path) { struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT); - struct cache_detail *cd = &svc_export_cache; + struct nfsd_net *nn = net_generic(rqstp->rq_xprt->xpt_net, nfsd_net_id); + struct cache_detail *cd = nn->svc_export_cache; if (rqstp->rq_client == NULL) goto gss; @@ -960,7 +961,8 @@ struct svc_export * rqst_exp_find(struct svc_rqst *rqstp, int fsid_type, u32 *fsidv) { struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT); - struct cache_detail *cd = &svc_export_cache; + struct nfsd_net *nn = net_generic(rqstp->rq_xprt->xpt_net, nfsd_net_id); + struct cache_detail *cd = nn->svc_export_cache; if (rqstp->rq_client == NULL) goto gss; @@ -1238,26 +1240,39 @@ int nfsd_export_init(struct net *net) { int rv; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + dprintk("nfsd: initializing export module (net: %p).\n", net); - rv = cache_register_net(&svc_export_cache, net); + nn->svc_export_cache = cache_create_net(&svc_export_cache_template, net); + if (IS_ERR(nn->svc_export_cache)) + return PTR_ERR(nn->svc_export_cache); + rv = cache_register_net(nn->svc_export_cache, net); if (rv) - return rv; + goto destroy_export_cache; + rv = cache_register_net(&svc_expkey_cache, net); if (rv) - cache_unregister_net(&svc_export_cache, net); - return rv; + goto unregister_export_cache; + return 0; +unregister_export_cache: + cache_unregister_net(nn->svc_export_cache, net); +destroy_export_cache: + cache_destroy_net(nn->svc_export_cache, net); + return rv; } /* * Flush exports table - called when last nfsd thread is killed */ void -nfsd_export_flush(void) +nfsd_export_flush(struct net *net) { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + cache_purge(&svc_expkey_cache); - cache_purge(&svc_export_cache); + cache_purge(nn->svc_export_cache); } /* @@ -1266,11 +1281,13 @@ nfsd_export_flush(void) void nfsd_export_shutdown(struct net *net) { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); dprintk("nfsd: shutting down export module (net: %p).\n", net); cache_unregister_net(&svc_expkey_cache, net); - cache_unregister_net(&svc_export_cache, net); + cache_unregister_net(nn->svc_export_cache, net); + cache_destroy_net(nn->svc_export_cache, net); svcauth_unix_purge(); dprintk("nfsd: export shutdown complete (net: %p).\n", net); diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 12e0cff435b4..c1c6242942a9 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -28,6 +28,8 @@ struct cld_net; struct nfsd_net { struct cld_net *cld_net; + + struct cache_detail *svc_export_cache; }; extern int nfsd_net_id; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index bc76f8ebbe5e..ddb9f8787379 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -354,7 +354,7 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size) if (!dom) return -ENOMEM; - len = exp_rootfh(dom, path, &fh, maxsize); + len = exp_rootfh(&init_net, dom, path, &fh, maxsize); auth_domain_put(dom); if (len) return len; diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 78e521392df1..cb4d51d8cbdb 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -261,7 +261,7 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net) printk(KERN_WARNING "nfsd: last server has exited, flushing export " "cache\n"); - nfsd_export_flush(); + nfsd_export_flush(net); } void nfsd_reset_versions(void) diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 375096c083d3..565c2122993f 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -132,13 +132,13 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp); */ int nfsd_export_init(struct net *); void nfsd_export_shutdown(struct net *); -void nfsd_export_flush(void); +void nfsd_export_flush(struct net *); struct svc_export * rqst_exp_get_by_name(struct svc_rqst *, struct path *); struct svc_export * rqst_exp_parent(struct svc_rqst *, struct path *); struct svc_export * rqst_find_fsidzero_export(struct svc_rqst *); -int exp_rootfh(struct auth_domain *, +int exp_rootfh(struct net *, struct auth_domain *, char *path, struct knfsd_fh *, int maxsize); __be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *); __be32 nfserrno(int errno); -- cgit v1.2.3 From e5f06f720eff24e32f1cc08ec03bcc8c4b2d2934 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 11 Apr 2012 15:13:28 +0400 Subject: nfsd: make expkey cache allocated per network namespace context This patch also changes svcauth_unix_purge() function: added network namespace as a parameter and thus loop over all networks was replaced by only one call for ip map cache purge. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 28 +++++++++++++++++----------- fs/nfsd/netns.h | 1 + fs/nfsd/nfsctl.c | 3 ++- include/linux/nfsd/export.h | 2 -- include/linux/sunrpc/svcauth.h | 2 +- net/sunrpc/svcauth_unix.c | 13 ++++--------- 6 files changed, 25 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 84d020fc0e37..dcb52b884519 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -40,7 +40,6 @@ typedef struct svc_export svc_export; #define EXPKEY_HASHBITS 8 #define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS) #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) -static struct cache_head *expkey_table[EXPKEY_HASHMAX]; static void expkey_put(struct kref *ref) { @@ -241,10 +240,9 @@ static struct cache_head *expkey_alloc(void) return NULL; } -static struct cache_detail svc_expkey_cache = { +static struct cache_detail svc_expkey_cache_template = { .owner = THIS_MODULE, .hash_size = EXPKEY_HASHMAX, - .hash_table = expkey_table, .name = "nfsd.fh", .cache_put = expkey_put, .cache_upcall = expkey_upcall, @@ -883,12 +881,13 @@ static struct svc_export *exp_find(struct cache_detail *cd, u32 *fsidv, struct cache_req *reqp) { struct svc_export *exp; - struct svc_expkey *ek = exp_find_key(&svc_expkey_cache, clp, fsid_type, fsidv, reqp); + struct nfsd_net *nn = net_generic(cd->net, nfsd_net_id); + struct svc_expkey *ek = exp_find_key(nn->svc_expkey_cache, clp, fsid_type, fsidv, reqp); if (IS_ERR(ek)) return ERR_CAST(ek); exp = exp_get_by_name(cd, clp, &ek->ek_path, reqp); - cache_put(&ek->h, &svc_expkey_cache); + cache_put(&ek->h, nn->svc_expkey_cache); if (IS_ERR(exp)) return ERR_CAST(exp); @@ -1232,7 +1231,6 @@ const struct seq_operations nfs_exports_op = { .show = e_show, }; - /* * Initialize the exports module. */ @@ -1251,11 +1249,18 @@ nfsd_export_init(struct net *net) if (rv) goto destroy_export_cache; - rv = cache_register_net(&svc_expkey_cache, net); - if (rv) + nn->svc_expkey_cache = cache_create_net(&svc_expkey_cache_template, net); + if (IS_ERR(nn->svc_expkey_cache)) { + rv = PTR_ERR(nn->svc_expkey_cache); goto unregister_export_cache; + } + rv = cache_register_net(nn->svc_expkey_cache, net); + if (rv) + goto destroy_expkey_cache; return 0; +destroy_expkey_cache: + cache_destroy_net(nn->svc_expkey_cache, net); unregister_export_cache: cache_unregister_net(nn->svc_export_cache, net); destroy_export_cache: @@ -1271,7 +1276,7 @@ nfsd_export_flush(struct net *net) { struct nfsd_net *nn = net_generic(net, nfsd_net_id); - cache_purge(&svc_expkey_cache); + cache_purge(nn->svc_expkey_cache); cache_purge(nn->svc_export_cache); } @@ -1285,10 +1290,11 @@ nfsd_export_shutdown(struct net *net) dprintk("nfsd: shutting down export module (net: %p).\n", net); - cache_unregister_net(&svc_expkey_cache, net); + cache_unregister_net(nn->svc_expkey_cache, net); cache_unregister_net(nn->svc_export_cache, net); + cache_destroy_net(nn->svc_expkey_cache, net); cache_destroy_net(nn->svc_export_cache, net); - svcauth_unix_purge(); + svcauth_unix_purge(net); dprintk("nfsd: export shutdown complete (net: %p).\n", net); } diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index c1c6242942a9..9794c6c7d133 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -29,6 +29,7 @@ struct cld_net; struct nfsd_net { struct cld_net *cld_net; + struct cache_detail *svc_expkey_cache; struct cache_detail *svc_export_cache; }; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index ddb9f8787379..b14417740816 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -129,13 +129,14 @@ static int exports_open(struct inode *inode, struct file *file) { int err; struct seq_file *seq; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); err = seq_open(file, &nfs_exports_op); if (err) return err; seq = file->private_data; - seq->private = &svc_export_cache; + seq->private = nn->svc_export_cache; return 0; } diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 565c2122993f..e33f747b173c 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -143,8 +143,6 @@ int exp_rootfh(struct net *, struct auth_domain *, __be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *); __be32 nfserrno(int errno); -extern struct cache_detail svc_export_cache; - static inline void exp_put(struct svc_export *exp) { cache_put(&exp->h, exp->cd); diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index 2e2af101b59c..2c54683b91de 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -130,7 +130,7 @@ extern struct auth_domain *auth_domain_lookup(char *name, struct auth_domain *ne extern struct auth_domain *auth_domain_find(char *name); extern struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr); extern int auth_unix_forget_old(struct auth_domain *dom); -extern void svcauth_unix_purge(void); +extern void svcauth_unix_purge(struct net *net); extern void svcauth_unix_info_release(struct svc_xprt *xpt); extern int svcauth_unix_set_client(struct svc_rqst *rqstp); diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 521d8f7dc833..9c3b9f014468 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -346,17 +346,12 @@ static inline int ip_map_update(struct net *net, struct ip_map *ipm, return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry); } - -void svcauth_unix_purge(void) +void svcauth_unix_purge(struct net *net) { - struct net *net; - - for_each_net(net) { - struct sunrpc_net *sn; + struct sunrpc_net *sn; - sn = net_generic(net, sunrpc_net_id); - cache_purge(sn->ip_map_cache); - } + sn = net_generic(net, sunrpc_net_id); + cache_purge(sn->ip_map_cache); } EXPORT_SYMBOL_GPL(svcauth_unix_purge); -- cgit v1.2.3 From 8112a5c91d781a22d2b631f3295386b0b70de7c8 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 10 Apr 2012 19:43:04 +0200 Subject: NFC: Add a target lost netlink event Some chips are capable of detecting when a tag is out of the field, so they could send a netlink event about it to userspace. Signed-off-by: Samuel Ortiz Signed-off-by: John W. Linville --- include/linux/nfc.h | 1 + net/nfc/netlink.c | 30 ++++++++++++++++++++++++++++++ net/nfc/nfc.h | 1 + 3 files changed, 32 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nfc.h b/include/linux/nfc.h index 39c1fcf089c0..0ae9b5857c83 100644 --- a/include/linux/nfc.h +++ b/include/linux/nfc.h @@ -70,6 +70,7 @@ enum nfc_commands { NFC_EVENT_TARGETS_FOUND, NFC_EVENT_DEVICE_ADDED, NFC_EVENT_DEVICE_REMOVED, + NFC_EVENT_TARGET_LOST, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 6404052d6c07..ebdb605f8dbd 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -183,6 +183,36 @@ free_msg: return -EMSGSIZE; } +int nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, + NFC_EVENT_TARGET_LOST); + if (!hdr) + goto free_msg; + + NLA_PUT_STRING(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)); + NLA_PUT_U32(msg, NFC_ATTR_TARGET_INDEX, target_idx); + + genlmsg_end(msg, hdr); + + genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); + + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +free_msg: + nlmsg_free(msg); + return -EMSGSIZE; +} + int nfc_genl_device_added(struct nfc_dev *dev) { struct sk_buff *msg; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index ec8794c1099c..2c868d2b3c57 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -119,6 +119,7 @@ void nfc_genl_data_init(struct nfc_genl_data *genl_data); void nfc_genl_data_exit(struct nfc_genl_data *genl_data); int nfc_genl_targets_found(struct nfc_dev *dev); +int nfc_genl_target_lost(struct nfc_dev *dev, u32 target_idx); int nfc_genl_device_added(struct nfc_dev *dev); int nfc_genl_device_removed(struct nfc_dev *dev); -- cgit v1.2.3 From 26b5e74d318241d95430d440e43ebbdfab3c70d4 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:30 -0600 Subject: regmap: introduce explicit bus_context for bus callbacks The only context needed by I2C and SPI bus definitions is the device itself; this can be converted to an i2c_client or spi_device in order to perform IO on the device. However, other bus types may need more context in order to perform IO. Enable this by having regmap_init accept a bus_context parameter, and pass this to all bus callbacks. The existing callbacks simply pass the struct device here. Future bus types may pass something else. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap-i2c.c | 13 ++++++++----- drivers/base/regmap/regmap-spi.c | 13 ++++++++----- drivers/base/regmap/regmap.c | 19 +++++++++++++------ include/linux/regmap.h | 10 +++++++--- 5 files changed, 37 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 606b83d75458..4f87633d56e9 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -38,6 +38,7 @@ struct regmap { void *work_buf; /* Scratch buffer used to format I/O */ struct regmap_format format; /* Buffer format */ const struct regmap_bus *bus; + void *bus_context; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 9a3a8c564389..5f6b2478bf17 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -15,8 +15,9 @@ #include #include -static int regmap_i2c_write(struct device *dev, const void *data, size_t count) +static int regmap_i2c_write(void *context, const void *data, size_t count) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); int ret; @@ -29,10 +30,11 @@ static int regmap_i2c_write(struct device *dev, const void *data, size_t count) return -EIO; } -static int regmap_i2c_gather_write(struct device *dev, +static int regmap_i2c_gather_write(void *context, const void *reg, size_t reg_size, const void *val, size_t val_size) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); struct i2c_msg xfer[2]; int ret; @@ -62,10 +64,11 @@ static int regmap_i2c_gather_write(struct device *dev, return -EIO; } -static int regmap_i2c_read(struct device *dev, +static int regmap_i2c_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { + struct device *dev = context; struct i2c_client *i2c = to_i2c_client(dev); struct i2c_msg xfer[2]; int ret; @@ -107,7 +110,7 @@ static struct regmap_bus regmap_i2c = { struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config) { - return regmap_init(&i2c->dev, ®map_i2c, config); + return regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config); } EXPORT_SYMBOL_GPL(regmap_init_i2c); @@ -124,7 +127,7 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c); struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config) { - return devm_regmap_init(&i2c->dev, ®map_i2c, config); + return devm_regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config); } EXPORT_SYMBOL_GPL(devm_regmap_init_i2c); diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 7c0c35a39c33..ffa46a92ad33 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -15,17 +15,19 @@ #include #include -static int regmap_spi_write(struct device *dev, const void *data, size_t count) +static int regmap_spi_write(void *context, const void *data, size_t count) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); return spi_write(spi, data, count); } -static int regmap_spi_gather_write(struct device *dev, +static int regmap_spi_gather_write(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); struct spi_message m; struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, }, @@ -38,10 +40,11 @@ static int regmap_spi_gather_write(struct device *dev, return spi_sync(spi, &m); } -static int regmap_spi_read(struct device *dev, +static int regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) { + struct device *dev = context; struct spi_device *spi = to_spi_device(dev); return spi_write_then_read(spi, reg, reg_size, val, val_size); @@ -66,7 +69,7 @@ static struct regmap_bus regmap_spi = { struct regmap *regmap_init_spi(struct spi_device *spi, const struct regmap_config *config) { - return regmap_init(&spi->dev, ®map_spi, config); + return regmap_init(&spi->dev, ®map_spi, &spi->dev, config); } EXPORT_SYMBOL_GPL(regmap_init_spi); @@ -83,7 +86,7 @@ EXPORT_SYMBOL_GPL(regmap_init_spi); struct regmap *devm_regmap_init_spi(struct spi_device *spi, const struct regmap_config *config) { - return devm_regmap_init(&spi->dev, ®map_spi, config); + return devm_regmap_init(&spi->dev, ®map_spi, &spi->dev, config); } EXPORT_SYMBOL_GPL(devm_regmap_init_spi); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 178989a8949e..8cfe79c6323f 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -184,6 +184,7 @@ static unsigned int regmap_parse_32(void *buf) * * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device + * @bus_context: Data passed to bus-specific callbacks * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to @@ -192,6 +193,7 @@ static unsigned int regmap_parse_32(void *buf) */ struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config) { struct regmap *map; @@ -215,6 +217,7 @@ struct regmap *regmap_init(struct device *dev, map->reg_shift = config->pad_bits % 8; map->dev = dev; map->bus = bus; + map->bus_context = bus_context; map->max_register = config->max_register; map->writeable_reg = config->writeable_reg; map->readable_reg = config->readable_reg; @@ -342,6 +345,7 @@ static void devm_regmap_release(struct device *dev, void *res) * * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device + * @bus_context: Data passed to bus-specific callbacks * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer @@ -351,6 +355,7 @@ static void devm_regmap_release(struct device *dev, void *res) */ struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config) { struct regmap **ptr, *regmap; @@ -359,7 +364,7 @@ struct regmap *devm_regmap_init(struct device *dev, if (!ptr) return ERR_PTR(-ENOMEM); - regmap = regmap_init(dev, bus, config); + regmap = regmap_init(dev, bus, bus_context, config); if (!IS_ERR(regmap)) { *ptr = regmap; devres_add(dev, ptr); @@ -417,6 +422,8 @@ void regmap_exit(struct regmap *map) { regcache_exit(map); regmap_debugfs_exit(map); + if (map->bus->free_context) + map->bus->free_context(map->bus_context); kfree(map->work_buf); kfree(map); } @@ -470,12 +477,12 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, */ if (val == (map->work_buf + map->format.pad_bytes + map->format.reg_bytes)) - ret = map->bus->write(map->dev, map->work_buf, + ret = map->bus->write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes + val_len); else if (map->bus->gather_write) - ret = map->bus->gather_write(map->dev, map->work_buf, + ret = map->bus->gather_write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); @@ -490,7 +497,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, memcpy(buf, map->work_buf, map->format.reg_bytes); memcpy(buf + map->format.reg_bytes + map->format.pad_bytes, val, val_len); - ret = map->bus->write(map->dev, buf, len); + ret = map->bus->write(map->bus_context, buf, len); kfree(buf); } @@ -524,7 +531,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_hw_write_start(map->dev, reg, 1); - ret = map->bus->write(map->dev, map->work_buf, + ret = map->bus->write(map->bus_context, map->work_buf, map->format.buf_size); trace_regmap_hw_write_done(map->dev, reg, 1); @@ -665,7 +672,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, trace_regmap_hw_read_start(map->dev, reg, val_len / map->format.val_bytes); - ret = map->bus->read(map->dev, map->work_buf, + ret = map->bus->read(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes, val, val_len); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a90abb6bfa64..8fd341e613d6 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -97,14 +97,15 @@ struct regmap_config { u8 write_flag_mask; }; -typedef int (*regmap_hw_write)(struct device *dev, const void *data, +typedef int (*regmap_hw_write)(void *context, const void *data, size_t count); -typedef int (*regmap_hw_gather_write)(struct device *dev, +typedef int (*regmap_hw_gather_write)(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len); -typedef int (*regmap_hw_read)(struct device *dev, +typedef int (*regmap_hw_read)(void *context, const void *reg_buf, size_t reg_size, void *val_buf, size_t val_size); +typedef void (*regmap_hw_free_context)(void *context); /** * Description of a hardware bus for the register map infrastructure. @@ -121,11 +122,13 @@ struct regmap_bus { regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_read read; + regmap_hw_free_context free_context; u8 read_flag_mask; }; struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config); struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); @@ -134,6 +137,7 @@ struct regmap *regmap_init_spi(struct spi_device *dev, struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, + void *bus_context, const struct regmap_config *config); struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); -- cgit v1.2.3 From a42678c4c8b5f6d489829ffc3071fa1f08ee99d1 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:28 -0600 Subject: regmap: introduce fast_io busses, and use a spinlock for them Some bus types have very fast IO. For these, acquiring a mutex for every IO operation is a significant overhead. Allow busses to indicate their IO is fast, and enhance regmap to use a spinlock for those busses. [Currently limited to native endian registers -- broonie] Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 8 ++++- drivers/base/regmap/regcache-rbtree.c | 4 +-- drivers/base/regmap/regcache.c | 20 +++++------ drivers/base/regmap/regmap.c | 62 +++++++++++++++++++++++++---------- include/linux/regmap.h | 3 ++ 5 files changed, 67 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 4f87633d56e9..44e3b1c438f4 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -31,8 +31,14 @@ struct regmap_format { unsigned int (*parse_val)(void *buf); }; +typedef void (*regmap_lock)(struct regmap *map); +typedef void (*regmap_unlock)(struct regmap *map); + struct regmap { - struct mutex lock; + struct mutex mutex; + spinlock_t spinlock; + regmap_lock lock; + regmap_unlock unlock; struct device *dev; /* Device we do I/O on */ void *work_buf; /* Scratch buffer used to format I/O */ diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 92b779ee002b..e49e71fab184 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -140,7 +140,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) int registers = 0; int average; - mutex_lock(&map->lock); + map->lock(map); for (node = rb_first(&rbtree_ctx->root); node != NULL; node = rb_next(node)) { @@ -161,7 +161,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) seq_printf(s, "%d nodes, %d registers, average %d registers\n", nodes, registers, average); - mutex_unlock(&map->lock); + map->unlock(map); return 0; } diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 74b69095def6..d4368e8b6f9d 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -264,7 +264,7 @@ int regcache_sync(struct regmap *map) BUG_ON(!map->cache_ops || !map->cache_ops->sync); - mutex_lock(&map->lock); + map->lock(map); /* Remember the initial bypass state */ bypass = map->cache_bypass; dev_dbg(map->dev, "Syncing %s cache\n", @@ -296,7 +296,7 @@ out: trace_regcache_sync(map->dev, name, "stop"); /* Restore the bypass state */ map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -323,7 +323,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, BUG_ON(!map->cache_ops || !map->cache_ops->sync); - mutex_lock(&map->lock); + map->lock(map); /* Remember the initial bypass state */ bypass = map->cache_bypass; @@ -342,7 +342,7 @@ out: trace_regcache_sync(map->dev, name, "stop region"); /* Restore the bypass state */ map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -362,11 +362,11 @@ EXPORT_SYMBOL_GPL(regcache_sync_region); */ void regcache_cache_only(struct regmap *map, bool enable) { - mutex_lock(&map->lock); + map->lock(map); WARN_ON(map->cache_bypass && enable); map->cache_only = enable; trace_regmap_cache_only(map->dev, enable); - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_cache_only); @@ -381,9 +381,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only); */ void regcache_mark_dirty(struct regmap *map) { - mutex_lock(&map->lock); + map->lock(map); map->cache_dirty = true; - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_mark_dirty); @@ -400,11 +400,11 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty); */ void regcache_cache_bypass(struct regmap *map, bool enable) { - mutex_lock(&map->lock); + map->lock(map); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; trace_regmap_cache_bypass(map->dev, enable); - mutex_unlock(&map->lock); + map->unlock(map); } EXPORT_SYMBOL_GPL(regcache_cache_bypass); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 8cfe79c6323f..004a08d54f07 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -179,6 +179,26 @@ static unsigned int regmap_parse_32(void *buf) return b[0]; } +static void regmap_lock_mutex(struct regmap *map) +{ + mutex_lock(&map->mutex); +} + +static void regmap_unlock_mutex(struct regmap *map) +{ + mutex_unlock(&map->mutex); +} + +static void regmap_lock_spinlock(struct regmap *map) +{ + spin_lock(&map->spinlock); +} + +static void regmap_unlock_spinlock(struct regmap *map) +{ + spin_unlock(&map->spinlock); +} + /** * regmap_init(): Initialise register map * @@ -208,7 +228,15 @@ struct regmap *regmap_init(struct device *dev, goto err; } - mutex_init(&map->lock); + if (bus->fast_io) { + spin_lock_init(&map->spinlock); + map->lock = regmap_lock_spinlock; + map->unlock = regmap_unlock_spinlock; + } else { + mutex_init(&map->mutex); + map->lock = regmap_lock_mutex; + map->unlock = regmap_unlock_mutex; + } map->format.buf_size = (config->reg_bits + config->val_bits) / 8; map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; @@ -391,7 +419,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) { int ret; - mutex_lock(&map->lock); + map->lock(map); regcache_exit(map); regmap_debugfs_exit(map); @@ -410,7 +438,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) ret = regcache_init(map, config); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -562,11 +590,11 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_write(map, reg, val); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -593,11 +621,11 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, { int ret; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_raw_write(map, reg, val, val_len); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -627,7 +655,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (!map->format.parse_val) return -EINVAL; - mutex_lock(&map->lock); + map->lock(map); /* No formatting is require if val_byte is 1 */ if (val_bytes == 1) { @@ -648,7 +676,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, kfree(wval); out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_write); @@ -722,11 +750,11 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_read(map, reg, val); - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -751,7 +779,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int v; int ret, i; - mutex_lock(&map->lock); + map->lock(map); if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || map->cache_type == REGCACHE_NONE) { @@ -772,7 +800,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, } out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -825,7 +853,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int ret; unsigned int tmp, orig; - mutex_lock(&map->lock); + map->lock(map); ret = _regmap_read(map, reg, &orig); if (ret != 0) @@ -842,7 +870,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, } out: - mutex_unlock(&map->lock); + map->unlock(map); return ret; } @@ -909,7 +937,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, if (map->patch) return -EBUSY; - mutex_lock(&map->lock); + map->lock(map); bypass = map->cache_bypass; @@ -937,7 +965,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, out: map->cache_bypass = bypass; - mutex_unlock(&map->lock); + map->unlock(map); return ret; } diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 8fd341e613d6..f14588a96eaf 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -110,6 +110,8 @@ typedef void (*regmap_hw_free_context)(void *context); /** * Description of a hardware bus for the register map infrastructure. * + * @fast_io: Register IO is fast. Use a spinlock instead of a mutex + * to perform locking. * @write: Write operation. * @gather_write: Write operation with split register/value, return -ENOTSUPP * if not implemented on a given device. @@ -119,6 +121,7 @@ typedef void (*regmap_hw_free_context)(void *context); * a read. */ struct regmap_bus { + bool fast_io; regmap_hw_write write; regmap_hw_gather_write gather_write; regmap_hw_read read; -- cgit v1.2.3 From ecb44aec86f0a5e37142a971815f91e065645986 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:31 -0600 Subject: regmap: add MMIO bus support This is a basic memory-mapped-IO bus for regmap. It has the following features and limitations: * Registers themselves may be 8, 16, 32, or 64-bit. 64-bit is only supported on 64-bit platforms. * Register offsets are limited to precisely 32-bit. * IO is performed using readl/writel, with no provision for using the __raw_readl or readl_relaxed variants. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/Kconfig | 3 + drivers/base/regmap/Makefile | 1 + drivers/base/regmap/regmap-mmio.c | 217 ++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 6 ++ 4 files changed, 227 insertions(+) create mode 100644 drivers/base/regmap/regmap-mmio.c (limited to 'include/linux') diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 0f6c7fb418e8..9ef0a5326f17 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -14,5 +14,8 @@ config REGMAP_I2C config REGMAP_SPI tristate +config REGMAP_MMIO + tristate + config REGMAP_IRQ bool diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index defd57963c84..5e75d1b683e2 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -3,4 +3,5 @@ obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o +obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c new file mode 100644 index 000000000000..1a7b5ee11abc --- /dev/null +++ b/drivers/base/regmap/regmap-mmio.c @@ -0,0 +1,217 @@ +/* + * Register map access API - MMIO support + * + * Copyright (c) 2012, 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 + +struct regmap_mmio_context { + void __iomem *regs; + unsigned val_bytes; +}; + +static int regmap_mmio_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct regmap_mmio_context *ctx = context; + u32 offset; + + if (reg_size != 4) + return -EIO; + if (val_size % ctx->val_bytes) + return -EIO; + + offset = be32_to_cpup(reg); + + while (val_size) { + switch (ctx->val_bytes) { + case 1: + writeb(*(u8 *)val, ctx->regs + offset); + break; + case 2: + writew(be16_to_cpup(val), ctx->regs + offset); + break; + case 4: + writel(be32_to_cpup(val), ctx->regs + offset); + break; +#ifdef CONFIG_64BIT + case 8: + writeq(be64_to_cpup(val), ctx->regs + offset); + break; +#endif + default: + /* Should be caught by regmap_mmio_check_config */ + return -EIO; + } + val_size -= ctx->val_bytes; + val += ctx->val_bytes; + offset += ctx->val_bytes; + } + + return 0; +} + +static int regmap_mmio_write(void *context, const void *data, size_t count) +{ + if (count < 4) + return -EIO; + return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); +} + +static int regmap_mmio_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct regmap_mmio_context *ctx = context; + u32 offset; + + if (reg_size != 4) + return -EIO; + if (val_size % ctx->val_bytes) + return -EIO; + + offset = be32_to_cpup(reg); + + while (val_size) { + switch (ctx->val_bytes) { + case 1: + *(u8 *)val = readb(ctx->regs + offset); + break; + case 2: + *(u16 *)val = cpu_to_be16(readw(ctx->regs + offset)); + break; + case 4: + *(u32 *)val = cpu_to_be32(readl(ctx->regs + offset)); + break; +#ifdef CONFIG_64BIT + case 8: + *(u64 *)val = cpu_to_be32(readq(ctx->regs + offset)); + break; +#endif + default: + /* Should be caught by regmap_mmio_check_config */ + return -EIO; + } + val_size -= ctx->val_bytes; + val += ctx->val_bytes; + offset += ctx->val_bytes; + } + + return 0; +} + +static void regmap_mmio_free_context(void *context) +{ + kfree(context); +} + +static struct regmap_bus regmap_mmio = { + .fast_io = true, + .write = regmap_mmio_write, + .gather_write = regmap_mmio_gather_write, + .read = regmap_mmio_read, + .free_context = regmap_mmio_free_context, +}; + +struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + if (config->reg_bits != 32) + return ERR_PTR(-EINVAL); + + if (config->pad_bits) + return ERR_PTR(-EINVAL); + + switch (config->val_bits) { + case 8: + case 16: + case 32: +#ifdef CONFIG_64BIT + case 64: +#endif + break; + default: + return ERR_PTR(-EINVAL); + } + + ctx = kzalloc(GFP_KERNEL, sizeof(*ctx)); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->regs = regs; + ctx->val_bytes = config->val_bits / 8; + + return ctx; +} + +/** + * regmap_init_mmio(): Initialise register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + ctx = regmap_mmio_gen_context(regs, config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return regmap_init(dev, ®map_mmio, ctx, config); +} +EXPORT_SYMBOL_GPL(regmap_init_mmio); + +/** + * devm_regmap_init_mmio(): Initialise managed register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + struct regmap_mmio_context *ctx; + + ctx = regmap_mmio_gen_context(regs, config); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return devm_regmap_init(dev, ®map_mmio, ctx, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_mmio); + +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f14588a96eaf..f6abc8d33d64 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -137,6 +137,9 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config); struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, @@ -146,6 +149,9 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *devm_regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); +struct regmap *devm_regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config); void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, -- cgit v1.2.3 From 4b5c0186e48c8d14fd7c38ff99b8d1b9aa475432 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 15:48:29 -0600 Subject: regmap: allow regmap instances to be named Some devices have multiple separate register regions. Logically, one regmap would be created per region. One issue that prevents this is that each instance will attempt to create the same debugfs files. Avoid this by allowing regmaps to be named, and use the name to construct the debugfs directory name. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 3 ++- drivers/base/regmap/regmap-debugfs.c | 14 +++++++++++--- drivers/base/regmap/regmap.c | 4 ++-- include/linux/regmap.h | 5 +++++ 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 44e3b1c438f4..9bc1d270f0bd 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -48,6 +48,7 @@ struct regmap { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; + const char *debugfs_name; #endif unsigned int max_register; @@ -111,7 +112,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, #ifdef CONFIG_DEBUG_FS extern void regmap_debugfs_initcall(void); -extern void regmap_debugfs_init(struct regmap *map); +extern void regmap_debugfs_init(struct regmap *map, const char *name); extern void regmap_debugfs_exit(struct regmap *map); #else static inline void regmap_debugfs_initcall(void) { } diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 251eb70f83e7..df97c93efa8e 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -242,10 +242,17 @@ static const struct file_operations regmap_access_fops = { .llseek = default_llseek, }; -void regmap_debugfs_init(struct regmap *map) +void regmap_debugfs_init(struct regmap *map, const char *name) { - map->debugfs = debugfs_create_dir(dev_name(map->dev), - regmap_debugfs_root); + if (name) { + map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", + dev_name(map->dev), name); + name = map->debugfs_name; + } else { + name = dev_name(map->dev); + } + + map->debugfs = debugfs_create_dir(name, regmap_debugfs_root); if (!map->debugfs) { dev_warn(map->dev, "Failed to create debugfs directory\n"); return; @@ -274,6 +281,7 @@ void regmap_debugfs_init(struct regmap *map) void regmap_debugfs_exit(struct regmap *map) { debugfs_remove_recursive(map->debugfs); + kfree(map->debugfs_name); } void regmap_debugfs_initcall(void) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e6038bc54210..40f910162781 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -346,7 +346,7 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } - regmap_debugfs_init(map); + regmap_debugfs_init(map, config->name); ret = regcache_init(map, config); if (ret < 0) @@ -431,7 +431,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; - regmap_debugfs_init(map); + regmap_debugfs_init(map, config->name); map->cache_bypass = false; map->cache_only = false; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f6abc8d33d64..680ddd7de60e 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -46,6 +46,9 @@ struct reg_default { /** * Configuration for the register map of a device. * + * @name: Optional name of the regmap. Useful when a device has multiple + * register regions. + * * @reg_bits: Number of bits in a register address, mandatory. * @pad_bits: Number of bits of padding between register and value. * @val_bits: Number of bits in a register value, mandatory. @@ -77,6 +80,8 @@ struct reg_default { * @num_reg_defaults_raw: Number of elements in reg_defaults_raw. */ struct regmap_config { + const char *name; + int reg_bits; int pad_bits; int val_bits; -- cgit v1.2.3 From edc9ae420f98dd094e47f8b2d33652858bdc830b Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 9 Apr 2012 13:40:24 -0600 Subject: regmap: implement register striding regmap_config.reg_stride is introduced. All extant register addresses are a multiple of this value. Users of serial-oriented regmap busses will typically set this to 1. Users of the MMIO regmap bus will typically set this based on the value size of their registers, in bytes, so 4 for a 32-bit register. Throughout the regmap code, actual register addresses are used. Wherever the register address is used to index some array of values, the address is divided by the stride to determine the index, or vice-versa. Error- checking is added to all entry-points for register address data to ensure that register addresses actually satisfy the specified stride. The MMIO bus ensures that the specified stride is large enough for the register size. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache-lzo.c | 11 +++++----- drivers/base/regmap/regcache-rbtree.c | 40 ++++++++++++++++++++--------------- drivers/base/regmap/regcache.c | 14 +++++++++--- drivers/base/regmap/regmap-debugfs.c | 4 ++-- drivers/base/regmap/regmap-irq.c | 34 +++++++++++++++++++---------- drivers/base/regmap/regmap-mmio.c | 13 ++++++++++++ drivers/base/regmap/regmap.c | 30 ++++++++++++++++++++++---- include/linux/regmap.h | 4 ++++ 9 files changed, 109 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 99b28fffbd0e..d92e9b1cb83c 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -62,6 +62,7 @@ struct regmap { /* number of bits to (left) shift the reg value when formatting*/ int reg_shift; + int reg_stride; /* regcache specific members */ const struct regcache_ops *cache_ops; diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 483b06d4a380..afd6aa91a0df 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -108,7 +108,7 @@ static int regcache_lzo_decompress_cache_block(struct regmap *map, static inline int regcache_lzo_get_blkindex(struct regmap *map, unsigned int reg) { - return (reg * map->cache_word_size) / + return ((reg / map->reg_stride) * map->cache_word_size) / DIV_ROUND_UP(map->cache_size_raw, regcache_lzo_block_count(map)); } @@ -116,9 +116,10 @@ static inline int regcache_lzo_get_blkindex(struct regmap *map, static inline int regcache_lzo_get_blkpos(struct regmap *map, unsigned int reg) { - return reg % (DIV_ROUND_UP(map->cache_size_raw, - regcache_lzo_block_count(map)) / - map->cache_word_size); + return (reg / map->reg_stride) % + (DIV_ROUND_UP(map->cache_size_raw, + regcache_lzo_block_count(map)) / + map->cache_word_size); } static inline int regcache_lzo_get_blksize(struct regmap *map) @@ -322,7 +323,7 @@ static int regcache_lzo_write(struct regmap *map, } /* set the bit so we know we have to sync this register */ - set_bit(reg, lzo_block->sync_bmp); + set_bit(reg / map->reg_stride, lzo_block->sync_bmp); kfree(tmp_dst); kfree(lzo_block->src); return 0; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index e49e71fab184..e6732cf7c06e 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -39,11 +39,12 @@ struct regcache_rbtree_ctx { }; static inline void regcache_rbtree_get_base_top_reg( + struct regmap *map, struct regcache_rbtree_node *rbnode, unsigned int *base, unsigned int *top) { *base = rbnode->base_reg; - *top = rbnode->base_reg + rbnode->blklen - 1; + *top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride); } static unsigned int regcache_rbtree_get_register( @@ -70,7 +71,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, rbnode = rbtree_ctx->cached_rbnode; if (rbnode) { - regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); if (reg >= base_reg && reg <= top_reg) return rbnode; } @@ -78,7 +80,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, node = rbtree_ctx->root.rb_node; while (node) { rbnode = container_of(node, struct regcache_rbtree_node, node); - regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg); + regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg, + &top_reg); if (reg >= base_reg && reg <= top_reg) { rbtree_ctx->cached_rbnode = rbnode; return rbnode; @@ -92,7 +95,7 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map, return NULL; } -static int regcache_rbtree_insert(struct rb_root *root, +static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root, struct regcache_rbtree_node *rbnode) { struct rb_node **new, *parent; @@ -106,7 +109,7 @@ static int regcache_rbtree_insert(struct rb_root *root, rbnode_tmp = container_of(*new, struct regcache_rbtree_node, node); /* base and top registers of the current rbnode */ - regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp, + regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp, &top_reg_tmp); /* base register of the rbnode to be added */ base_reg = rbnode->base_reg; @@ -138,7 +141,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) unsigned int base, top; int nodes = 0; int registers = 0; - int average; + int this_registers, average; map->lock(map); @@ -146,11 +149,12 @@ static int rbtree_show(struct seq_file *s, void *ignored) node = rb_next(node)) { n = container_of(node, struct regcache_rbtree_node, node); - regcache_rbtree_get_base_top_reg(n, &base, &top); - seq_printf(s, "%x-%x (%d)\n", base, top, top - base + 1); + regcache_rbtree_get_base_top_reg(map, n, &base, &top); + this_registers = ((top - base) / map->reg_stride) + 1; + seq_printf(s, "%x-%x (%d)\n", base, top, this_registers); nodes++; - registers += top - base + 1; + registers += this_registers; } if (nodes) @@ -255,7 +259,7 @@ static int regcache_rbtree_read(struct regmap *map, rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { - reg_tmp = reg - rbnode->base_reg; + reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; *value = regcache_rbtree_get_register(rbnode, reg_tmp, map->cache_word_size); } else { @@ -310,7 +314,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, */ rbnode = regcache_rbtree_lookup(map, reg); if (rbnode) { - reg_tmp = reg - rbnode->base_reg; + reg_tmp = (reg - rbnode->base_reg) / map->reg_stride; val = regcache_rbtree_get_register(rbnode, reg_tmp, map->cache_word_size); if (val == value) @@ -321,13 +325,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, /* look for an adjacent register to the one we are about to add */ for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) { - rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node); + rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, + node); for (i = 0; i < rbnode_tmp->blklen; i++) { - reg_tmp = rbnode_tmp->base_reg + i; - if (abs(reg_tmp - reg) != 1) + reg_tmp = rbnode_tmp->base_reg + + (i * map->reg_stride); + if (abs(reg_tmp - reg) != map->reg_stride) continue; /* decide where in the block to place our register */ - if (reg_tmp + 1 == reg) + if (reg_tmp + map->reg_stride == reg) pos = i + 1; else pos = i; @@ -357,7 +363,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, return -ENOMEM; } regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size); - regcache_rbtree_insert(&rbtree_ctx->root, rbnode); + regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode); rbtree_ctx->cached_rbnode = rbnode; } @@ -397,7 +403,7 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min, end = rbnode->blklen; for (i = base; i < end; i++) { - regtmp = rbnode->base_reg + i; + regtmp = rbnode->base_reg + (i * map->reg_stride); val = regcache_rbtree_get_register(rbnode, i, map->cache_word_size); diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index d4368e8b6f9d..835883bda977 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -59,7 +59,7 @@ static int regcache_hw_init(struct regmap *map) for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (regmap_volatile(map, i)) + if (regmap_volatile(map, i * map->reg_stride)) continue; count++; } @@ -76,9 +76,9 @@ static int regcache_hw_init(struct regmap *map) for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) { val = regcache_get_val(map->reg_defaults_raw, i, map->cache_word_size); - if (regmap_volatile(map, i)) + if (regmap_volatile(map, i * map->reg_stride)) continue; - map->reg_defaults[j].reg = i; + map->reg_defaults[j].reg = i * map->reg_stride; map->reg_defaults[j].def = val; j++; } @@ -98,6 +98,10 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) int i; void *tmp_buf; + for (i = 0; i < config->num_reg_defaults; i++) + if (config->reg_defaults[i].reg % map->reg_stride) + return -EINVAL; + if (map->cache_type == REGCACHE_NONE) { map->cache_bypass = true; return 0; @@ -278,6 +282,10 @@ int regcache_sync(struct regmap *map) /* Apply any patch first */ map->cache_bypass = 1; for (i = 0; i < map->patch_regs; i++) { + if (map->patch[i].reg % map->reg_stride) { + ret = -EINVAL; + goto out; + } ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); if (ret != 0) { dev_err(map->dev, "Failed to write %x = %x: %d\n", diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index df97c93efa8e..bb1ff175b962 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -80,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, val_len = 2 * map->format.val_bytes; tot_len = reg_len + val_len + 3; /* : \n */ - for (i = 0; i < map->max_register + 1; i++) { + for (i = 0; i <= map->max_register; i += map->reg_stride) { if (!regmap_readable(map, i)) continue; @@ -197,7 +197,7 @@ static ssize_t regmap_access_read_file(struct file *file, reg_len = regmap_calc_reg_len(map->max_register, buf, count); tot_len = reg_len + 10; /* ': R W V P\n' */ - for (i = 0; i < map->max_register + 1; i++) { + for (i = 0; i <= map->max_register; i += map->reg_stride) { /* Ignore registers which are neither readable nor writable */ if (!regmap_readable(map, i) && !regmap_writeable(map, i)) continue; diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 1befaa7a31cb..56b8136eb36a 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -58,11 +58,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data) * suppress pointless writes. */ for (i = 0; i < d->chip->num_regs; i++) { - ret = regmap_update_bits(d->map, d->chip->mask_base + i, + ret = regmap_update_bits(d->map, d->chip->mask_base + + (i * map->map->reg_stride), d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", - d->chip->mask_base + i); + d->chip->mask_base + (i * map->reg_stride)); } mutex_unlock(&d->lock); @@ -73,7 +74,7 @@ static void regmap_irq_enable(struct irq_data *data) struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); - d->mask_buf[irq_data->reg_offset] &= ~irq_data->mask; + d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; } static void regmap_irq_disable(struct irq_data *data) @@ -81,7 +82,7 @@ static void regmap_irq_disable(struct irq_data *data) struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); - d->mask_buf[irq_data->reg_offset] |= irq_data->mask; + d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; } static struct irq_chip regmap_irq_chip = { @@ -136,17 +137,19 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) data->status_buf[i] &= ~data->mask_buf[i]; if (data->status_buf[i] && chip->ack_base) { - ret = regmap_write(map, chip->ack_base + i, + ret = regmap_write(map, chip->ack_base + + (i * map->reg_stride), data->status_buf[i]); if (ret != 0) dev_err(map->dev, "Failed to ack 0x%x: %d\n", - chip->ack_base + i, ret); + chip->ack_base + (i * map->reg_stride), + ret); } } for (i = 0; i < chip->num_irqs; i++) { - if (data->status_buf[chip->irqs[i].reg_offset] & - chip->irqs[i].mask) { + if (data->status_buf[chip->irqs[i].reg_offset / + map->reg_stride] & chip->irqs[i].mask) { handle_nested_irq(data->irq_base + i); handled = true; } @@ -181,6 +184,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, int cur_irq, i; int ret = -ENOMEM; + for (i = 0; i < chip->num_irqs; i++) { + if (chip->irqs[i].reg_offset % map->reg_stride) + return -EINVAL; + if (chip->irqs[i].reg_offset / map->reg_stride >= + chip->num_regs) + return -EINVAL; + } + irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); if (irq_base < 0) { dev_warn(map->dev, "Failed to allocate IRQs: %d\n", @@ -218,16 +229,17 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, mutex_init(&d->lock); for (i = 0; i < chip->num_irqs; i++) - d->mask_buf_def[chip->irqs[i].reg_offset] + d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] |= chip->irqs[i].mask; /* Mask all the interrupts by default */ for (i = 0; i < chip->num_regs; i++) { d->mask_buf[i] = d->mask_buf_def[i]; - ret = regmap_write(map, chip->mask_base + i, d->mask_buf[i]); + ret = regmap_write(map, chip->mask_base + (i * map->reg_stride), + d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", - chip->mask_base + i, ret); + chip->mask_base + (i * map->reg_stride), ret); goto err_alloc; } } diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index bdf4dc865293..febd6de6c8ac 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -130,6 +130,7 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, const struct regmap_config *config) { struct regmap_mmio_context *ctx; + int min_stride; if (config->reg_bits != 32) return ERR_PTR(-EINVAL); @@ -139,16 +140,28 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, switch (config->val_bits) { case 8: + /* The core treats 0 as 1 */ + min_stride = 0; + break; case 16: + min_stride = 2; + break; case 32: + min_stride = 4; + break; #ifdef CONFIG_64BIT case 64: + min_stride = 8; + break; #endif break; default: return ERR_PTR(-EINVAL); } + if (config->reg_stride < min_stride) + return ERR_PTR(-EINVAL); + ctx = kzalloc(GFP_KERNEL, sizeof(*ctx)); if (!ctx) return ERR_PTR(-ENOMEM); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 40f910162781..8a25006b2a4d 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -243,6 +243,10 @@ struct regmap *regmap_init(struct device *dev, map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); map->format.buf_size += map->format.pad_bytes; map->reg_shift = config->pad_bits % 8; + if (config->reg_stride) + map->reg_stride = config->reg_stride; + else + map->reg_stride = 1; map->dev = dev; map->bus = bus; map->bus_context = bus_context; @@ -469,7 +473,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, /* Check for unwritable registers before we start */ if (map->writeable_reg) for (i = 0; i < val_len / map->format.val_bytes; i++) - if (!map->writeable_reg(map->dev, reg + i)) + if (!map->writeable_reg(map->dev, + reg + (i * map->reg_stride))) return -EINVAL; if (!map->cache_bypass && map->format.parse_val) { @@ -478,7 +483,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, for (i = 0; i < val_len / val_bytes; i++) { memcpy(map->work_buf, val + (i * val_bytes), val_bytes); ival = map->format.parse_val(map->work_buf); - ret = regcache_write(map, reg + i, ival); + ret = regcache_write(map, reg + (i * map->reg_stride), + ival); if (ret) { dev_err(map->dev, "Error in caching of register: %u ret: %d\n", @@ -590,6 +596,9 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; + if (reg % map->reg_stride) + return -EINVAL; + map->lock(map); ret = _regmap_write(map, reg, val); @@ -623,6 +632,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, if (val_len % map->format.val_bytes) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; map->lock(map); @@ -657,6 +668,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (!map->format.parse_val) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; map->lock(map); @@ -753,6 +766,9 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; + if (reg % map->reg_stride) + return -EINVAL; + map->lock(map); ret = _regmap_read(map, reg, val); @@ -784,6 +800,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (val_len % map->format.val_bytes) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; map->lock(map); @@ -797,7 +815,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, * cost as we expect to hit the cache. */ for (i = 0; i < val_count; i++) { - ret = _regmap_read(map, reg + i, &v); + ret = _regmap_read(map, reg + (i * map->reg_stride), + &v); if (ret != 0) goto out; @@ -832,6 +851,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, if (!map->format.parse_val) return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; if (vol || map->cache_type == REGCACHE_NONE) { ret = regmap_raw_read(map, reg, val, val_bytes * val_count); @@ -842,7 +863,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, map->format.parse_val(val + i); } else { for (i = 0; i < val_count; i++) { - ret = regmap_read(map, reg + i, val + (i * val_bytes)); + ret = regmap_read(map, reg + (i * map->reg_stride), + val + (i * val_bytes)); if (ret != 0) return ret; } diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 680ddd7de60e..0258bcd6258d 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -50,6 +50,9 @@ struct reg_default { * register regions. * * @reg_bits: Number of bits in a register address, mandatory. + * @reg_stride: The register address stride. Valid register addresses are a + * multiple of this value. If set to 0, a value of 1 will be + * used. * @pad_bits: Number of bits of padding between register and value. * @val_bits: Number of bits in a register value, mandatory. * @@ -83,6 +86,7 @@ struct regmap_config { const char *name; int reg_bits; + int reg_stride; int pad_bits; int val_bits; -- cgit v1.2.3 From d57a4282d04810417c4ed2a49cbbeda8b3569b18 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 7 Apr 2012 14:16:53 -0600 Subject: spi/devicetree: Move devicetree support code into spi directory The SPI device tree support code isn't shared by any other subsystem. It can be moved into the core drivers/spi directory and the exported symbol can be removed. Signed-off-by: Grant Likely Cc: Rob Herring --- drivers/of/Kconfig | 6 --- drivers/of/Makefile | 1 - drivers/of/of_spi.c | 99 ---------------------------------------------- drivers/spi/spi-fsl-espi.c | 1 - drivers/spi/spi-fsl-lib.c | 2 +- drivers/spi/spi-ppc4xx.c | 1 - drivers/spi/spi.c | 92 +++++++++++++++++++++++++++++++++++++++++- include/linux/of_spi.h | 23 ----------- 8 files changed, 92 insertions(+), 133 deletions(-) delete mode 100644 drivers/of/of_spi.c delete mode 100644 include/linux/of_spi.h (limited to 'include/linux') diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 8e84ce9765a9..f623f17a0b9f 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -67,12 +67,6 @@ config OF_NET depends on NETDEVICES def_bool y -config OF_SPI - def_tristate SPI - depends on SPI && !SPARC - help - OpenFirmware SPI accessors - config OF_MDIO def_tristate PHYLIB depends on PHYLIB diff --git a/drivers/of/Makefile b/drivers/of/Makefile index aa90e602c8a7..0040d1858665 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_OF_DEVICE) += device.o platform.o obj-$(CONFIG_OF_GPIO) += gpio.o obj-$(CONFIG_OF_I2C) += of_i2c.o obj-$(CONFIG_OF_NET) += of_net.o -obj-$(CONFIG_OF_SPI) += of_spi.o obj-$(CONFIG_OF_SELFTEST) += selftest.o obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o diff --git a/drivers/of/of_spi.c b/drivers/of/of_spi.c deleted file mode 100644 index 6dbc074e4876..000000000000 --- a/drivers/of/of_spi.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SPI OF support routines - * Copyright (C) 2008 Secret Lab Technologies Ltd. - * - * Support routines for deriving SPI device attachments from the device - * tree. - */ - -#include -#include -#include -#include -#include -#include - -/** - * of_register_spi_devices - Register child devices onto the SPI bus - * @master: Pointer to spi_master device - * - * Registers an spi_device for each child node of master node which has a 'reg' - * property. - */ -void of_register_spi_devices(struct spi_master *master) -{ - struct spi_device *spi; - struct device_node *nc; - const __be32 *prop; - int rc; - int len; - - if (!master->dev.of_node) - return; - - for_each_child_of_node(master->dev.of_node, nc) { - /* Alloc an spi_device */ - spi = spi_alloc_device(master); - if (!spi) { - dev_err(&master->dev, "spi_device alloc error for %s\n", - nc->full_name); - spi_dev_put(spi); - continue; - } - - /* Select device driver */ - if (of_modalias_node(nc, spi->modalias, - sizeof(spi->modalias)) < 0) { - dev_err(&master->dev, "cannot find modalias for %s\n", - nc->full_name); - spi_dev_put(spi); - continue; - } - - /* Device address */ - prop = of_get_property(nc, "reg", &len); - if (!prop || len < sizeof(*prop)) { - dev_err(&master->dev, "%s has no 'reg' property\n", - nc->full_name); - spi_dev_put(spi); - continue; - } - spi->chip_select = be32_to_cpup(prop); - - /* Mode (clock phase/polarity/etc.) */ - if (of_find_property(nc, "spi-cpha", NULL)) - spi->mode |= SPI_CPHA; - if (of_find_property(nc, "spi-cpol", NULL)) - spi->mode |= SPI_CPOL; - if (of_find_property(nc, "spi-cs-high", NULL)) - spi->mode |= SPI_CS_HIGH; - - /* Device speed */ - prop = of_get_property(nc, "spi-max-frequency", &len); - if (!prop || len < sizeof(*prop)) { - dev_err(&master->dev, "%s has no 'spi-max-frequency' property\n", - nc->full_name); - spi_dev_put(spi); - continue; - } - spi->max_speed_hz = be32_to_cpup(prop); - - /* IRQ */ - spi->irq = irq_of_parse_and_map(nc, 0); - - /* Store a pointer to the node in the device structure */ - of_node_get(nc); - spi->dev.of_node = nc; - - /* Register the new device */ - request_module(spi->modalias); - rc = spi_add_device(spi); - if (rc) { - dev_err(&master->dev, "spi_device register error %s\n", - nc->full_name); - spi_dev_put(spi); - } - - } -} -EXPORT_SYMBOL(of_register_spi_devices); diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 7523a2429d09..27bdc47b5250 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index 2674fad7f68a..1503574b215a 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include "spi-fsl-lib.h" diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 98ec53285fc7..d95d307a1100 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 3d8f662e4fe9..37c555ec59ab 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2,6 +2,7 @@ * SPI init/core code * * Copyright (C) 2005 David Brownell + * Copyright (C) 2008 Secret Lab Technologies Ltd. * * 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 @@ -19,15 +20,16 @@ */ #include +#include #include #include #include #include #include +#include #include #include #include -#include #include #include #include @@ -798,6 +800,94 @@ err_init_queue: /*-------------------------------------------------------------------------*/ +#if defined(CONFIG_OF) && !defined(CONFIG_SPARC) +/** + * of_register_spi_devices() - Register child devices onto the SPI bus + * @master: Pointer to spi_master device + * + * Registers an spi_device for each child node of master node which has a 'reg' + * property. + */ +static void of_register_spi_devices(struct spi_master *master) +{ + struct spi_device *spi; + struct device_node *nc; + const __be32 *prop; + int rc; + int len; + + if (!master->dev.of_node) + return; + + for_each_child_of_node(master->dev.of_node, nc) { + /* Alloc an spi_device */ + spi = spi_alloc_device(master); + if (!spi) { + dev_err(&master->dev, "spi_device alloc error for %s\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + + /* Select device driver */ + if (of_modalias_node(nc, spi->modalias, + sizeof(spi->modalias)) < 0) { + dev_err(&master->dev, "cannot find modalias for %s\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + + /* Device address */ + prop = of_get_property(nc, "reg", &len); + if (!prop || len < sizeof(*prop)) { + dev_err(&master->dev, "%s has no 'reg' property\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + spi->chip_select = be32_to_cpup(prop); + + /* Mode (clock phase/polarity/etc.) */ + if (of_find_property(nc, "spi-cpha", NULL)) + spi->mode |= SPI_CPHA; + if (of_find_property(nc, "spi-cpol", NULL)) + spi->mode |= SPI_CPOL; + if (of_find_property(nc, "spi-cs-high", NULL)) + spi->mode |= SPI_CS_HIGH; + + /* Device speed */ + prop = of_get_property(nc, "spi-max-frequency", &len); + if (!prop || len < sizeof(*prop)) { + dev_err(&master->dev, "%s has no 'spi-max-frequency' property\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + spi->max_speed_hz = be32_to_cpup(prop); + + /* IRQ */ + spi->irq = irq_of_parse_and_map(nc, 0); + + /* Store a pointer to the node in the device structure */ + of_node_get(nc); + spi->dev.of_node = nc; + + /* Register the new device */ + request_module(spi->modalias); + rc = spi_add_device(spi); + if (rc) { + dev_err(&master->dev, "spi_device register error %s\n", + nc->full_name); + spi_dev_put(spi); + } + + } +} +#else +static void of_register_spi_devices(struct spi_master *master) { } +#endif + static void spi_master_release(struct device *dev) { struct spi_master *master; diff --git a/include/linux/of_spi.h b/include/linux/of_spi.h deleted file mode 100644 index 9e3e70f78ae6..000000000000 --- a/include/linux/of_spi.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * OpenFirmware SPI support routines - * Copyright (C) 2008 Secret Lab Technologies Ltd. - * - * Support routines for deriving SPI device attachments from the device - * tree. - */ - -#ifndef __LINUX_OF_SPI_H -#define __LINUX_OF_SPI_H - -#include - -#if defined(CONFIG_OF_SPI) || defined(CONFIG_OF_SPI_MODULE) -extern void of_register_spi_devices(struct spi_master *master); -#else -static inline void of_register_spi_devices(struct spi_master *master) -{ - return; -} -#endif /* CONFIG_OF_SPI */ - -#endif /* __LINUX_OF_SPI */ -- cgit v1.2.3 From 259e5e6c75a910f3b5e656151dc602f53f9d7548 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Thu, 12 Apr 2012 16:47:50 -0500 Subject: Add PR_{GET,SET}_NO_NEW_PRIVS to prevent execve from granting privs With this change, calling prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) disables privilege granting operations at execve-time. For example, a process will not be able to execute a setuid binary to change their uid or gid if this bit is set. The same is true for file capabilities. Additionally, LSM_UNSAFE_NO_NEW_PRIVS is defined to ensure that LSMs respect the requested behavior. To determine if the NO_NEW_PRIVS bit is set, a task may call prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); It returns 1 if set and 0 if it is not set. If any of the arguments are non-zero, it will return -1 and set errno to -EINVAL. (PR_SET_NO_NEW_PRIVS behaves similarly.) This functionality is desired for the proposed seccomp filter patch series. By using PR_SET_NO_NEW_PRIVS, it allows a task to modify the system call behavior for itself and its child tasks without being able to impact the behavior of a more privileged task. Another potential use is making certain privileged operations unprivileged. For example, chroot may be considered "safe" if it cannot affect privileged tasks. Note, this patch causes execve to fail when PR_SET_NO_NEW_PRIVS is set and AppArmor is in use. It is fixed in a subsequent patch. Signed-off-by: Andy Lutomirski Signed-off-by: Will Drewry Acked-by: Eric Paris Acked-by: Kees Cook v18: updated change desc v17: using new define values as per 3.4 Signed-off-by: James Morris --- fs/exec.c | 10 +++++++++- include/linux/prctl.h | 15 +++++++++++++++ include/linux/sched.h | 2 ++ include/linux/security.h | 1 + kernel/sys.c | 10 ++++++++++ security/apparmor/domain.c | 4 ++++ security/commoncap.c | 7 +++++-- security/selinux/hooks.c | 10 +++++++++- 8 files changed, 55 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/exec.c b/fs/exec.c index b1fd2025e59a..d038968b54b4 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1245,6 +1245,13 @@ static int check_unsafe_exec(struct linux_binprm *bprm) bprm->unsafe |= LSM_UNSAFE_PTRACE; } + /* + * This isn't strictly necessary, but it makes it harder for LSMs to + * mess up. + */ + if (current->no_new_privs) + bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS; + n_fs = 1; spin_lock(&p->fs->lock); rcu_read_lock(); @@ -1288,7 +1295,8 @@ int prepare_binprm(struct linux_binprm *bprm) bprm->cred->euid = current_euid(); bprm->cred->egid = current_egid(); - if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) { + if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) && + !current->no_new_privs) { /* Set-uid? */ if (mode & S_ISUID) { bprm->per_clear |= PER_CLEAR_ON_SETID; diff --git a/include/linux/prctl.h b/include/linux/prctl.h index e0cfec2490aa..78b76e24cc7e 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h @@ -124,4 +124,19 @@ #define PR_SET_CHILD_SUBREAPER 36 #define PR_GET_CHILD_SUBREAPER 37 +/* + * If no_new_privs is set, then operations that grant new privileges (i.e. + * execve) will either fail or not grant them. This affects suid/sgid, + * file capabilities, and LSMs. + * + * Operations that merely manipulate or drop existing privileges (setresuid, + * capset, etc.) will still work. Drop those privileges if you want them gone. + * + * Changing LSM security domain is considered a new privilege. So, for example, + * asking selinux for a specific new context (e.g. with runcon) will result + * in execve returning -EPERM. + */ +#define PR_SET_NO_NEW_PRIVS 38 +#define PR_GET_NO_NEW_PRIVS 39 + #endif /* _LINUX_PRCTL_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 81a173c0897d..ba60897bb447 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1341,6 +1341,8 @@ struct task_struct { * execve */ unsigned in_iowait:1; + /* task may not gain privileges */ + unsigned no_new_privs:1; /* Revert to default priority/policy when forking */ unsigned sched_reset_on_fork:1; diff --git a/include/linux/security.h b/include/linux/security.h index 673afbb8238a..6e1dea93907a 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -144,6 +144,7 @@ struct request_sock; #define LSM_UNSAFE_SHARE 1 #define LSM_UNSAFE_PTRACE 2 #define LSM_UNSAFE_PTRACE_CAP 4 +#define LSM_UNSAFE_NO_NEW_PRIVS 8 #ifdef CONFIG_MMU extern int mmap_min_addr_handler(struct ctl_table *table, int write, diff --git a/kernel/sys.c b/kernel/sys.c index e7006eb6c1e4..b82568b7d201 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1979,6 +1979,16 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, error = put_user(me->signal->is_child_subreaper, (int __user *) arg2); break; + case PR_SET_NO_NEW_PRIVS: + if (arg2 != 1 || arg3 || arg4 || arg5) + return -EINVAL; + + current->no_new_privs = 1; + break; + case PR_GET_NO_NEW_PRIVS: + if (arg2 || arg3 || arg4 || arg5) + return -EINVAL; + return current->no_new_privs ? 1 : 0; default: error = -EINVAL; break; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 6327685c101e..18c88d06e881 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -360,6 +360,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->cred_prepared) return 0; + /* XXX: no_new_privs is not usable with AppArmor yet */ + if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) + return -EPERM; + cxt = bprm->cred->security; BUG_ON(!cxt); diff --git a/security/commoncap.c b/security/commoncap.c index 0cf4b53480a7..edd3918fac02 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -506,14 +506,17 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) skip: /* Don't let someone trace a set[ug]id/setpcap binary with the revised - * credentials unless they have the appropriate permit + * credentials unless they have the appropriate permit. + * + * In addition, if NO_NEW_PRIVS, then ensure we get no new privs. */ if ((new->euid != old->uid || new->egid != old->gid || !cap_issubset(new->cap_permitted, old->cap_permitted)) && bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) { /* downgrade; they get no more than they had, and maybe less */ - if (!capable(CAP_SETUID)) { + if (!capable(CAP_SETUID) || + (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) { new->euid = new->uid; new->egid = new->gid; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d85b793c9321..0b06685787b9 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2016,6 +2016,13 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) new_tsec->sid = old_tsec->exec_sid; /* Reset exec SID on execve. */ new_tsec->exec_sid = 0; + + /* + * Minimize confusion: if no_new_privs and a transition is + * explicitly requested, then fail the exec. + */ + if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) + return -EPERM; } else { /* Check for a default transition on this program. */ rc = security_transition_sid(old_tsec->sid, isec->sid, @@ -2029,7 +2036,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) ad.selinux_audit_data = &sad; ad.u.path = bprm->file->f_path; - if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) || + (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) new_tsec->sid = old_tsec->sid; if (new_tsec->sid == old_tsec->sid) { -- cgit v1.2.3 From 46b325c7eb01482674406701825ff67f561ccdd4 Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Thu, 12 Apr 2012 16:47:52 -0500 Subject: sk_run_filter: add BPF_S_ANC_SECCOMP_LD_W Introduces a new BPF ancillary instruction that all LD calls will be mapped through when skb_run_filter() is being used for seccomp BPF. The rewriting will be done using a secondary chk_filter function that is run after skb_chk_filter. The code change is guarded by CONFIG_SECCOMP_FILTER which is added, along with the seccomp_bpf_load() function later in this series. This is based on http://lkml.org/lkml/2012/3/2/141 Suggested-by: Indan Zupancic Signed-off-by: Will Drewry Acked-by: Eric Dumazet Acked-by: Eric Paris v18: rebase ... v15: include seccomp.h explicitly for when seccomp_bpf_load exists. v14: First cut using a single additional instruction ... v13: made bpf functions generic. Signed-off-by: James Morris --- include/linux/filter.h | 1 + net/core/filter.c | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'include/linux') diff --git a/include/linux/filter.h b/include/linux/filter.h index 8eeb205f298b..aaa2e80630b8 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -228,6 +228,7 @@ enum { BPF_S_ANC_HATYPE, BPF_S_ANC_RXHASH, BPF_S_ANC_CPU, + BPF_S_ANC_SECCOMP_LD_W, }; #endif /* __KERNEL__ */ diff --git a/net/core/filter.c b/net/core/filter.c index 6f755cca4520..491e2e1ec277 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -38,6 +38,7 @@ #include #include #include +#include /* No hurry in this branch * @@ -352,6 +353,11 @@ load_b: A = 0; continue; } +#ifdef CONFIG_SECCOMP_FILTER + case BPF_S_ANC_SECCOMP_LD_W: + A = seccomp_bpf_load(fentry->k); + continue; +#endif default: WARN_RATELIMIT(1, "Unknown code:%u jt:%u tf:%u k:%u\n", fentry->code, fentry->jt, -- cgit v1.2.3 From 0c5fe1b4221c6701224c2601cf3c692e5721103e Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Thu, 12 Apr 2012 16:47:53 -0500 Subject: net/compat.c,linux/filter.h: share compat_sock_fprog Any other users of bpf_*_filter that take a struct sock_fprog from userspace will need to be able to also accept a compat_sock_fprog if the arch supports compat calls. This change allows the existing compat_sock_fprog be shared. Signed-off-by: Will Drewry Acked-by: Serge Hallyn Acked-by: Eric Dumazet Acked-by: Eric Paris v18: tasered by the apostrophe police v14: rebase/nochanges v13: rebase on to 88ebdda6159ffc15699f204c33feb3e431bf9bdc v12: rebase on to linux-next v11: introduction Signed-off-by: James Morris --- include/linux/filter.h | 11 +++++++++++ net/compat.c | 8 -------- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/filter.h b/include/linux/filter.h index aaa2e80630b8..f2e53152e835 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -10,6 +10,7 @@ #ifdef __KERNEL__ #include +#include #endif /* @@ -132,6 +133,16 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */ #ifdef __KERNEL__ +#ifdef CONFIG_COMPAT +/* + * A struct sock_filter is architecture independent. + */ +struct compat_sock_fprog { + u16 len; + compat_uptr_t filter; /* struct sock_filter * */ +}; +#endif + struct sk_buff; struct sock; diff --git a/net/compat.c b/net/compat.c index e055708b8ec9..242c828810ff 100644 --- a/net/compat.c +++ b/net/compat.c @@ -328,14 +328,6 @@ void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm) __scm_destroy(scm); } -/* - * A struct sock_filter is architecture independent. - */ -struct compat_sock_fprog { - u16 len; - compat_uptr_t filter; /* struct sock_filter * */ -}; - static int do_set_attach_filter(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) { -- cgit v1.2.3 From 932ecebb0405b9a41cd18946e6cff8a17d434e23 Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Thu, 12 Apr 2012 16:47:54 -0500 Subject: seccomp: kill the seccomp_t typedef Replaces the seccomp_t typedef with struct seccomp to match modern kernel style. Signed-off-by: Will Drewry Reviewed-by: James Morris Acked-by: Serge Hallyn Acked-by: Eric Paris v18: rebase ... v14: rebase/nochanges v13: rebase on to 88ebdda6159ffc15699f204c33feb3e431bf9bdc v12: rebase on to linux-next v8-v11: no changes v7: struct seccomp_struct -> struct seccomp v6: original inclusion in this series. Signed-off-by: James Morris --- include/linux/sched.h | 2 +- include/linux/seccomp.h | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sched.h b/include/linux/sched.h index ba60897bb447..cad15023f458 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1452,7 +1452,7 @@ struct task_struct { uid_t loginuid; unsigned int sessionid; #endif - seccomp_t seccomp; + struct seccomp seccomp; /* Thread group tracking */ u32 parent_exec_id; diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index cc7a4e9cc7ad..d61f27fcaa97 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -7,7 +7,9 @@ #include #include -typedef struct { int mode; } seccomp_t; +struct seccomp { + int mode; +}; extern void __secure_computing(int); static inline void secure_computing(int this_syscall) @@ -19,7 +21,7 @@ static inline void secure_computing(int this_syscall) extern long prctl_get_seccomp(void); extern long prctl_set_seccomp(unsigned long); -static inline int seccomp_mode(seccomp_t *s) +static inline int seccomp_mode(struct seccomp *s) { return s->mode; } @@ -28,7 +30,7 @@ static inline int seccomp_mode(seccomp_t *s) #include -typedef struct { } seccomp_t; +struct seccomp { }; #define secure_computing(x) do { } while (0) @@ -42,7 +44,7 @@ static inline long prctl_set_seccomp(unsigned long arg2) return -EINVAL; } -static inline int seccomp_mode(seccomp_t *s) +static inline int seccomp_mode(struct seccomp *s) { return 0; } -- cgit v1.2.3 From e2cfabdfd075648216f99c2c03821cf3f47c1727 Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Thu, 12 Apr 2012 16:47:57 -0500 Subject: seccomp: add system call filtering using BPF [This patch depends on luto@mit.edu's no_new_privs patch: https://lkml.org/lkml/2012/1/30/264 The whole series including Andrew's patches can be found here: https://github.com/redpig/linux/tree/seccomp Complete diff here: https://github.com/redpig/linux/compare/1dc65fed...seccomp ] This patch adds support for seccomp mode 2. Mode 2 introduces the ability for unprivileged processes to install system call filtering policy expressed in terms of a Berkeley Packet Filter (BPF) program. This program will be evaluated in the kernel for each system call the task makes and computes a result based on data in the format of struct seccomp_data. A filter program may be installed by calling: struct sock_fprog fprog = { ... }; ... prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &fprog); The return value of the filter program determines if the system call is allowed to proceed or denied. If the first filter program installed allows prctl(2) calls, then the above call may be made repeatedly by a task to further reduce its access to the kernel. All attached programs must be evaluated before a system call will be allowed to proceed. Filter programs will be inherited across fork/clone and execve. However, if the task attaching the filter is unprivileged (!CAP_SYS_ADMIN) the no_new_privs bit will be set on the task. This ensures that unprivileged tasks cannot attach filters that affect privileged tasks (e.g., setuid binary). There are a number of benefits to this approach. A few of which are as follows: - BPF has been exposed to userland for a long time - BPF optimization (and JIT'ing) are well understood - Userland already knows its ABI: system call numbers and desired arguments - No time-of-check-time-of-use vulnerable data accesses are possible. - system call arguments are loaded on access only to minimize copying required for system call policy decisions. Mode 2 support is restricted to architectures that enable HAVE_ARCH_SECCOMP_FILTER. In this patch, the primary dependency is on syscall_get_arguments(). The full desired scope of this feature will add a few minor additional requirements expressed later in this series. Based on discussion, SECCOMP_RET_ERRNO and SECCOMP_RET_TRACE seem to be the desired additional functionality. No architectures are enabled in this patch. Signed-off-by: Will Drewry Acked-by: Serge Hallyn Reviewed-by: Indan Zupancic Acked-by: Eric Paris Reviewed-by: Kees Cook v18: - rebase to v3.4-rc2 - s/chk/check/ (akpm@linux-foundation.org,jmorris@namei.org) - allocate with GFP_KERNEL|__GFP_NOWARN (indan@nul.nu) - add a comment for get_u32 regarding endianness (akpm@) - fix other typos, style mistakes (akpm@) - added acked-by v17: - properly guard seccomp filter needed headers (leann@ubuntu.com) - tighten return mask to 0x7fff0000 v16: - no change v15: - add a 4 instr penalty when counting a path to account for seccomp_filter size (indan@nul.nu) - drop the max insns to 256KB (indan@nul.nu) - return ENOMEM if the max insns limit has been hit (indan@nul.nu) - move IP checks after args (indan@nul.nu) - drop !user_filter check (indan@nul.nu) - only allow explicit bpf codes (indan@nul.nu) - exit_code -> exit_sig v14: - put/get_seccomp_filter takes struct task_struct (indan@nul.nu,keescook@chromium.org) - adds seccomp_chk_filter and drops general bpf_run/chk_filter user - add seccomp_bpf_load for use by net/core/filter.c - lower max per-process/per-hierarchy: 1MB - moved nnp/capability check prior to allocation (all of the above: indan@nul.nu) v13: - rebase on to 88ebdda6159ffc15699f204c33feb3e431bf9bdc v12: - added a maximum instruction count per path (indan@nul.nu,oleg@redhat.com) - removed copy_seccomp (keescook@chromium.org,indan@nul.nu) - reworded the prctl_set_seccomp comment (indan@nul.nu) v11: - reorder struct seccomp_data to allow future args expansion (hpa@zytor.com) - style clean up, @compat dropped, compat_sock_fprog32 (indan@nul.nu) - do_exit(SIGSYS) (keescook@chromium.org, luto@mit.edu) - pare down Kconfig doc reference. - extra comment clean up v10: - seccomp_data has changed again to be more aesthetically pleasing (hpa@zytor.com) - calling convention is noted in a new u32 field using syscall_get_arch. This allows for cross-calling convention tasks to use seccomp filters. (hpa@zytor.com) - lots of clean up (thanks, Indan!) v9: - n/a v8: - use bpf_chk_filter, bpf_run_filter. update load_fns - Lots of fixes courtesy of indan@nul.nu: -- fix up load behavior, compat fixups, and merge alloc code, -- renamed pc and dropped __packed, use bool compat. -- Added a hidden CONFIG_SECCOMP_FILTER to synthesize non-arch dependencies v7: (massive overhaul thanks to Indan, others) - added CONFIG_HAVE_ARCH_SECCOMP_FILTER - merged into seccomp.c - minimal seccomp_filter.h - no config option (part of seccomp) - no new prctl - doesn't break seccomp on systems without asm/syscall.h (works but arg access always fails) - dropped seccomp_init_task, extra free functions, ... - dropped the no-asm/syscall.h code paths - merges with network sk_run_filter and sk_chk_filter v6: - fix memory leak on attach compat check failure - require no_new_privs || CAP_SYS_ADMIN prior to filter installation. (luto@mit.edu) - s/seccomp_struct_/seccomp_/ for macros/functions (amwang@redhat.com) - cleaned up Kconfig (amwang@redhat.com) - on block, note if the call was compat (so the # means something) v5: - uses syscall_get_arguments (indan@nul.nu,oleg@redhat.com, mcgrathr@chromium.org) - uses union-based arg storage with hi/lo struct to handle endianness. Compromises between the two alternate proposals to minimize extra arg shuffling and account for endianness assuming userspace uses offsetof(). (mcgrathr@chromium.org, indan@nul.nu) - update Kconfig description - add include/seccomp_filter.h and add its installation - (naive) on-demand syscall argument loading - drop seccomp_t (eparis@redhat.com) v4: - adjusted prctl to make room for PR_[SG]ET_NO_NEW_PRIVS - now uses current->no_new_privs (luto@mit.edu,torvalds@linux-foundation.com) - assign names to seccomp modes (rdunlap@xenotime.net) - fix style issues (rdunlap@xenotime.net) - reworded Kconfig entry (rdunlap@xenotime.net) v3: - macros to inline (oleg@redhat.com) - init_task behavior fixed (oleg@redhat.com) - drop creator entry and extra NULL check (oleg@redhat.com) - alloc returns -EINVAL on bad sizing (serge.hallyn@canonical.com) - adds tentative use of "always_unprivileged" as per torvalds@linux-foundation.org and luto@mit.edu v2: - (patch 2 only) Signed-off-by: James Morris --- arch/Kconfig | 17 +++ include/linux/Kbuild | 1 + include/linux/seccomp.h | 76 +++++++++- kernel/fork.c | 3 + kernel/seccomp.c | 396 +++++++++++++++++++++++++++++++++++++++++++++--- kernel/sys.c | 2 +- 6 files changed, 472 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/arch/Kconfig b/arch/Kconfig index 684eb5af439d..91c2c730fc1a 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -216,4 +216,21 @@ config HAVE_CMPXCHG_DOUBLE config ARCH_WANT_OLD_COMPAT_IPC bool +config HAVE_ARCH_SECCOMP_FILTER + bool + help + This symbol should be selected by an architecure if it provides + asm/syscall.h, specifically syscall_get_arguments() and + syscall_get_arch(). + +config SECCOMP_FILTER + def_bool y + depends on HAVE_ARCH_SECCOMP_FILTER && SECCOMP && NET + help + Enable tasks to build secure computing environments defined + in terms of Berkeley Packet Filter programs which implement + task-defined system call filtering polices. + + See Documentation/prctl/seccomp_filter.txt for details. + source "kernel/gcov/Kconfig" diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 3c9b616c834a..5c93d6c5d591 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -332,6 +332,7 @@ header-y += scc.h header-y += sched.h header-y += screen_info.h header-y += sdla.h +header-y += seccomp.h header-y += securebits.h header-y += selinux_netlink.h header-y += sem.h diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index d61f27fcaa97..86bb68fc7683 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -1,14 +1,67 @@ #ifndef _LINUX_SECCOMP_H #define _LINUX_SECCOMP_H +#include +#include + + +/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, ) */ +#define SECCOMP_MODE_DISABLED 0 /* seccomp is not in use. */ +#define SECCOMP_MODE_STRICT 1 /* uses hard-coded filter. */ +#define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */ + +/* + * All BPF programs must return a 32-bit value. + * The bottom 16-bits are reserved for future use. + * The upper 16-bits are ordered from least permissive values to most. + * + * The ordering ensures that a min_t() over composed return values always + * selects the least permissive choice. + */ +#define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */ +#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ + +/* Masks for the return value sections. */ +#define SECCOMP_RET_ACTION 0x7fff0000U +#define SECCOMP_RET_DATA 0x0000ffffU + +/** + * struct seccomp_data - the format the BPF program executes over. + * @nr: the system call number + * @arch: indicates system call convention as an AUDIT_ARCH_* value + * as defined in . + * @instruction_pointer: at the time of the system call. + * @args: up to 6 system call arguments always stored as 64-bit values + * regardless of the architecture. + */ +struct seccomp_data { + int nr; + __u32 arch; + __u64 instruction_pointer; + __u64 args[6]; +}; +#ifdef __KERNEL__ #ifdef CONFIG_SECCOMP #include #include +struct seccomp_filter; +/** + * struct seccomp - the state of a seccomp'ed process + * + * @mode: indicates one of the valid values above for controlled + * system calls available to a process. + * @filter: The metadata and ruleset for determining what system calls + * are allowed for a task. + * + * @filter must only be accessed from the context of current as there + * is no locking. + */ struct seccomp { int mode; + struct seccomp_filter *filter; }; extern void __secure_computing(int); @@ -19,7 +72,7 @@ static inline void secure_computing(int this_syscall) } extern long prctl_get_seccomp(void); -extern long prctl_set_seccomp(unsigned long); +extern long prctl_set_seccomp(unsigned long, char __user *); static inline int seccomp_mode(struct seccomp *s) { @@ -31,15 +84,16 @@ static inline int seccomp_mode(struct seccomp *s) #include struct seccomp { }; +struct seccomp_filter { }; -#define secure_computing(x) do { } while (0) +#define secure_computing(x) 0 static inline long prctl_get_seccomp(void) { return -EINVAL; } -static inline long prctl_set_seccomp(unsigned long arg2) +static inline long prctl_set_seccomp(unsigned long arg2, char __user *arg3) { return -EINVAL; } @@ -48,7 +102,21 @@ static inline int seccomp_mode(struct seccomp *s) { return 0; } - #endif /* CONFIG_SECCOMP */ +#ifdef CONFIG_SECCOMP_FILTER +extern void put_seccomp_filter(struct task_struct *tsk); +extern void get_seccomp_filter(struct task_struct *tsk); +extern u32 seccomp_bpf_load(int off); +#else /* CONFIG_SECCOMP_FILTER */ +static inline void put_seccomp_filter(struct task_struct *tsk) +{ + return; +} +static inline void get_seccomp_filter(struct task_struct *tsk) +{ + return; +} +#endif /* CONFIG_SECCOMP_FILTER */ +#endif /* __KERNEL__ */ #endif /* _LINUX_SECCOMP_H */ diff --git a/kernel/fork.c b/kernel/fork.c index b9372a0bff18..f7cf6fb107ec 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -170,6 +171,7 @@ void free_task(struct task_struct *tsk) free_thread_info(tsk->stack); rt_mutex_debug_task_free(tsk); ftrace_graph_exit_task(tsk); + put_seccomp_filter(tsk); free_task_struct(tsk); } EXPORT_SYMBOL(free_task); @@ -1162,6 +1164,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto fork_out; ftrace_graph_init_task(p); + get_seccomp_filter(p); rt_mutex_init_task(p); diff --git a/kernel/seccomp.c b/kernel/seccomp.c index e8d76c5895ea..0aeec1960f91 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -3,16 +3,343 @@ * * Copyright 2004-2005 Andrea Arcangeli * - * This defines a simple but solid secure-computing mode. + * Copyright (C) 2012 Google, Inc. + * Will Drewry + * + * This defines a simple but solid secure-computing facility. + * + * Mode 1 uses a fixed list of allowed system calls. + * Mode 2 allows user-defined system call filters in the form + * of Berkeley Packet Filters/Linux Socket Filters. */ +#include #include -#include -#include #include +#include +#include /* #define SECCOMP_DEBUG 1 */ -#define NR_SECCOMP_MODES 1 + +#ifdef CONFIG_SECCOMP_FILTER +#include +#include +#include +#include +#include +#include + +/** + * struct seccomp_filter - container for seccomp BPF programs + * + * @usage: reference count to manage the object lifetime. + * get/put helpers should be used when accessing an instance + * outside of a lifetime-guarded section. In general, this + * is only needed for handling filters shared across tasks. + * @prev: points to a previously installed, or inherited, filter + * @len: the number of instructions in the program + * @insns: the BPF program instructions to evaluate + * + * seccomp_filter objects are organized in a tree linked via the @prev + * pointer. For any task, it appears to be a singly-linked list starting + * with current->seccomp.filter, the most recently attached or inherited filter. + * However, multiple filters may share a @prev node, by way of fork(), which + * results in a unidirectional tree existing in memory. This is similar to + * how namespaces work. + * + * seccomp_filter objects should never be modified after being attached + * to a task_struct (other than @usage). + */ +struct seccomp_filter { + atomic_t usage; + struct seccomp_filter *prev; + unsigned short len; /* Instruction count */ + struct sock_filter insns[]; +}; + +/* Limit any path through the tree to 256KB worth of instructions. */ +#define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter)) + +static void seccomp_filter_log_failure(int syscall) +{ + int compat = 0; +#ifdef CONFIG_COMPAT + compat = is_compat_task(); +#endif + pr_info("%s[%d]: %ssystem call %d blocked at 0x%lx\n", + current->comm, task_pid_nr(current), + (compat ? "compat " : ""), + syscall, KSTK_EIP(current)); +} + +/** + * get_u32 - returns a u32 offset into data + * @data: a unsigned 64 bit value + * @index: 0 or 1 to return the first or second 32-bits + * + * This inline exists to hide the length of unsigned long. If a 32-bit + * unsigned long is passed in, it will be extended and the top 32-bits will be + * 0. If it is a 64-bit unsigned long, then whatever data is resident will be + * properly returned. + * + * Endianness is explicitly ignored and left for BPF program authors to manage + * as per the specific architecture. + */ +static inline u32 get_u32(u64 data, int index) +{ + return ((u32 *)&data)[index]; +} + +/* Helper for bpf_load below. */ +#define BPF_DATA(_name) offsetof(struct seccomp_data, _name) +/** + * bpf_load: checks and returns a pointer to the requested offset + * @off: offset into struct seccomp_data to load from + * + * Returns the requested 32-bits of data. + * seccomp_check_filter() should assure that @off is 32-bit aligned + * and not out of bounds. Failure to do so is a BUG. + */ +u32 seccomp_bpf_load(int off) +{ + struct pt_regs *regs = task_pt_regs(current); + if (off == BPF_DATA(nr)) + return syscall_get_nr(current, regs); + if (off == BPF_DATA(arch)) + return syscall_get_arch(current, regs); + if (off >= BPF_DATA(args[0]) && off < BPF_DATA(args[6])) { + unsigned long value; + int arg = (off - BPF_DATA(args[0])) / sizeof(u64); + int index = !!(off % sizeof(u64)); + syscall_get_arguments(current, regs, arg, 1, &value); + return get_u32(value, index); + } + if (off == BPF_DATA(instruction_pointer)) + return get_u32(KSTK_EIP(current), 0); + if (off == BPF_DATA(instruction_pointer) + sizeof(u32)) + return get_u32(KSTK_EIP(current), 1); + /* seccomp_check_filter should make this impossible. */ + BUG(); +} + +/** + * seccomp_check_filter - verify seccomp filter code + * @filter: filter to verify + * @flen: length of filter + * + * Takes a previously checked filter (by sk_chk_filter) and + * redirects all filter code that loads struct sk_buff data + * and related data through seccomp_bpf_load. It also + * enforces length and alignment checking of those loads. + * + * Returns 0 if the rule set is legal or -EINVAL if not. + */ +static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen) +{ + int pc; + for (pc = 0; pc < flen; pc++) { + struct sock_filter *ftest = &filter[pc]; + u16 code = ftest->code; + u32 k = ftest->k; + + switch (code) { + case BPF_S_LD_W_ABS: + ftest->code = BPF_S_ANC_SECCOMP_LD_W; + /* 32-bit aligned and not out of bounds. */ + if (k >= sizeof(struct seccomp_data) || k & 3) + return -EINVAL; + continue; + case BPF_S_LD_W_LEN: + ftest->code = BPF_S_LD_IMM; + ftest->k = sizeof(struct seccomp_data); + continue; + case BPF_S_LDX_W_LEN: + ftest->code = BPF_S_LDX_IMM; + ftest->k = sizeof(struct seccomp_data); + continue; + /* Explicitly include allowed calls. */ + case BPF_S_RET_K: + case BPF_S_RET_A: + case BPF_S_ALU_ADD_K: + case BPF_S_ALU_ADD_X: + case BPF_S_ALU_SUB_K: + case BPF_S_ALU_SUB_X: + case BPF_S_ALU_MUL_K: + case BPF_S_ALU_MUL_X: + case BPF_S_ALU_DIV_X: + case BPF_S_ALU_AND_K: + case BPF_S_ALU_AND_X: + case BPF_S_ALU_OR_K: + case BPF_S_ALU_OR_X: + case BPF_S_ALU_LSH_K: + case BPF_S_ALU_LSH_X: + case BPF_S_ALU_RSH_K: + case BPF_S_ALU_RSH_X: + case BPF_S_ALU_NEG: + case BPF_S_LD_IMM: + case BPF_S_LDX_IMM: + case BPF_S_MISC_TAX: + case BPF_S_MISC_TXA: + case BPF_S_ALU_DIV_K: + case BPF_S_LD_MEM: + case BPF_S_LDX_MEM: + case BPF_S_ST: + case BPF_S_STX: + case BPF_S_JMP_JA: + case BPF_S_JMP_JEQ_K: + case BPF_S_JMP_JEQ_X: + case BPF_S_JMP_JGE_K: + case BPF_S_JMP_JGE_X: + case BPF_S_JMP_JGT_K: + case BPF_S_JMP_JGT_X: + case BPF_S_JMP_JSET_K: + case BPF_S_JMP_JSET_X: + continue; + default: + return -EINVAL; + } + } + return 0; +} + +/** + * seccomp_run_filters - evaluates all seccomp filters against @syscall + * @syscall: number of the current system call + * + * Returns valid seccomp BPF response codes. + */ +static u32 seccomp_run_filters(int syscall) +{ + struct seccomp_filter *f; + u32 ret = SECCOMP_RET_KILL; + /* + * All filters in the list are evaluated and the lowest BPF return + * value always takes priority. + */ + for (f = current->seccomp.filter; f; f = f->prev) { + ret = sk_run_filter(NULL, f->insns); + if (ret != SECCOMP_RET_ALLOW) + break; + } + return ret; +} + +/** + * seccomp_attach_filter: Attaches a seccomp filter to current. + * @fprog: BPF program to install + * + * Returns 0 on success or an errno on failure. + */ +static long seccomp_attach_filter(struct sock_fprog *fprog) +{ + struct seccomp_filter *filter; + unsigned long fp_size = fprog->len * sizeof(struct sock_filter); + unsigned long total_insns = fprog->len; + long ret; + + if (fprog->len == 0 || fprog->len > BPF_MAXINSNS) + return -EINVAL; + + for (filter = current->seccomp.filter; filter; filter = filter->prev) + total_insns += filter->len + 4; /* include a 4 instr penalty */ + if (total_insns > MAX_INSNS_PER_PATH) + return -ENOMEM; + + /* + * Installing a seccomp filter requires that the task have + * CAP_SYS_ADMIN in its namespace or be running with no_new_privs. + * This avoids scenarios where unprivileged tasks can affect the + * behavior of privileged children. + */ + if (!current->no_new_privs && + security_capable_noaudit(current_cred(), current_user_ns(), + CAP_SYS_ADMIN) != 0) + return -EACCES; + + /* Allocate a new seccomp_filter */ + filter = kzalloc(sizeof(struct seccomp_filter) + fp_size, + GFP_KERNEL|__GFP_NOWARN); + if (!filter) + return -ENOMEM; + atomic_set(&filter->usage, 1); + filter->len = fprog->len; + + /* Copy the instructions from fprog. */ + ret = -EFAULT; + if (copy_from_user(filter->insns, fprog->filter, fp_size)) + goto fail; + + /* Check and rewrite the fprog via the skb checker */ + ret = sk_chk_filter(filter->insns, filter->len); + if (ret) + goto fail; + + /* Check and rewrite the fprog for seccomp use */ + ret = seccomp_check_filter(filter->insns, filter->len); + if (ret) + goto fail; + + /* + * If there is an existing filter, make it the prev and don't drop its + * task reference. + */ + filter->prev = current->seccomp.filter; + current->seccomp.filter = filter; + return 0; +fail: + kfree(filter); + return ret; +} + +/** + * seccomp_attach_user_filter - attaches a user-supplied sock_fprog + * @user_filter: pointer to the user data containing a sock_fprog. + * + * Returns 0 on success and non-zero otherwise. + */ +long seccomp_attach_user_filter(char __user *user_filter) +{ + struct sock_fprog fprog; + long ret = -EFAULT; + +#ifdef CONFIG_COMPAT + if (is_compat_task()) { + struct compat_sock_fprog fprog32; + if (copy_from_user(&fprog32, user_filter, sizeof(fprog32))) + goto out; + fprog.len = fprog32.len; + fprog.filter = compat_ptr(fprog32.filter); + } else /* falls through to the if below. */ +#endif + if (copy_from_user(&fprog, user_filter, sizeof(fprog))) + goto out; + ret = seccomp_attach_filter(&fprog); +out: + return ret; +} + +/* get_seccomp_filter - increments the reference count of the filter on @tsk */ +void get_seccomp_filter(struct task_struct *tsk) +{ + struct seccomp_filter *orig = tsk->seccomp.filter; + if (!orig) + return; + /* Reference count is bounded by the number of total processes. */ + atomic_inc(&orig->usage); +} + +/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */ +void put_seccomp_filter(struct task_struct *tsk) +{ + struct seccomp_filter *orig = tsk->seccomp.filter; + /* Clean up single-reference branches iteratively. */ + while (orig && atomic_dec_and_test(&orig->usage)) { + struct seccomp_filter *freeme = orig; + orig = orig->prev; + kfree(freeme); + } +} +#endif /* CONFIG_SECCOMP_FILTER */ /* * Secure computing mode 1 allows only read/write/exit/sigreturn. @@ -34,10 +361,11 @@ static int mode1_syscalls_32[] = { void __secure_computing(int this_syscall) { int mode = current->seccomp.mode; - int * syscall; + int exit_sig = 0; + int *syscall; switch (mode) { - case 1: + case SECCOMP_MODE_STRICT: syscall = mode1_syscalls; #ifdef CONFIG_COMPAT if (is_compat_task()) @@ -47,7 +375,16 @@ void __secure_computing(int this_syscall) if (*syscall == this_syscall) return; } while (*++syscall); + exit_sig = SIGKILL; break; +#ifdef CONFIG_SECCOMP_FILTER + case SECCOMP_MODE_FILTER: + if (seccomp_run_filters(this_syscall) == SECCOMP_RET_ALLOW) + return; + seccomp_filter_log_failure(this_syscall); + exit_sig = SIGSYS; + break; +#endif default: BUG(); } @@ -56,7 +393,7 @@ void __secure_computing(int this_syscall) dump_stack(); #endif audit_seccomp(this_syscall); - do_exit(SIGKILL); + do_exit(exit_sig); } long prctl_get_seccomp(void) @@ -64,25 +401,48 @@ long prctl_get_seccomp(void) return current->seccomp.mode; } -long prctl_set_seccomp(unsigned long seccomp_mode) +/** + * prctl_set_seccomp: configures current->seccomp.mode + * @seccomp_mode: requested mode to use + * @filter: optional struct sock_fprog for use with SECCOMP_MODE_FILTER + * + * This function may be called repeatedly with a @seccomp_mode of + * SECCOMP_MODE_FILTER to install additional filters. Every filter + * successfully installed will be evaluated (in reverse order) for each system + * call the task makes. + * + * Once current->seccomp.mode is non-zero, it may not be changed. + * + * Returns 0 on success or -EINVAL on failure. + */ +long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter) { - long ret; + long ret = -EINVAL; - /* can set it only once to be even more secure */ - ret = -EPERM; - if (unlikely(current->seccomp.mode)) + if (current->seccomp.mode && + current->seccomp.mode != seccomp_mode) goto out; - ret = -EINVAL; - if (seccomp_mode && seccomp_mode <= NR_SECCOMP_MODES) { - current->seccomp.mode = seccomp_mode; - set_thread_flag(TIF_SECCOMP); + switch (seccomp_mode) { + case SECCOMP_MODE_STRICT: + ret = 0; #ifdef TIF_NOTSC disable_TSC(); #endif - ret = 0; + break; +#ifdef CONFIG_SECCOMP_FILTER + case SECCOMP_MODE_FILTER: + ret = seccomp_attach_user_filter(filter); + if (ret) + goto out; + break; +#endif + default: + goto out; } - out: + current->seccomp.mode = seccomp_mode; + set_thread_flag(TIF_SECCOMP); +out: return ret; } diff --git a/kernel/sys.c b/kernel/sys.c index b82568b7d201..ba0ae8eea6fb 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1908,7 +1908,7 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, error = prctl_get_seccomp(); break; case PR_SET_SECCOMP: - error = prctl_set_seccomp(arg2); + error = prctl_set_seccomp(arg2, (char __user *)arg3); break; case PR_GET_TSC: error = GET_TSC_CTL(arg2); -- cgit v1.2.3 From 3dc1c1b2d2ed7507ce8a379814ad75745ff97ebe Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 12 Apr 2012 16:47:58 -0500 Subject: seccomp: remove duplicated failure logging This consolidates the seccomp filter error logging path and adds more details to the audit log. Signed-off-by: Will Drewry Signed-off-by: Kees Cook Acked-by: Eric Paris v18: make compat= permanent in the record v15: added a return code to the audit_seccomp path by wad@chromium.org (suggested by eparis@redhat.com) v*: original by keescook@chromium.org Signed-off-by: James Morris --- include/linux/audit.h | 8 ++++---- kernel/auditsc.c | 8 ++++++-- kernel/seccomp.c | 15 +-------------- 3 files changed, 11 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index ed3ef1972496..22f292a917a3 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -463,7 +463,7 @@ extern void audit_putname(const char *name); extern void __audit_inode(const char *name, const struct dentry *dentry); extern void __audit_inode_child(const struct dentry *dentry, const struct inode *parent); -extern void __audit_seccomp(unsigned long syscall); +extern void __audit_seccomp(unsigned long syscall, long signr, int code); extern void __audit_ptrace(struct task_struct *t); static inline int audit_dummy_context(void) @@ -508,10 +508,10 @@ static inline void audit_inode_child(const struct dentry *dentry, } void audit_core_dumps(long signr); -static inline void audit_seccomp(unsigned long syscall) +static inline void audit_seccomp(unsigned long syscall, long signr, int code) { if (unlikely(!audit_dummy_context())) - __audit_seccomp(syscall); + __audit_seccomp(syscall, signr, code); } static inline void audit_ptrace(struct task_struct *t) @@ -634,7 +634,7 @@ extern int audit_signals; #define audit_inode(n,d) do { (void)(d); } while (0) #define audit_inode_child(i,p) do { ; } while (0) #define audit_core_dumps(i) do { ; } while (0) -#define audit_seccomp(i) do { ; } while (0) +#define audit_seccomp(i,s,c) do { ; } while (0) #define auditsc_get_stamp(c,t,s) (0) #define audit_get_loginuid(t) (-1) #define audit_get_sessionid(t) (-1) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index af1de0f34eae..4b96415527b8 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -67,6 +67,7 @@ #include #include #include +#include #include "audit.h" @@ -2710,13 +2711,16 @@ void audit_core_dumps(long signr) audit_log_end(ab); } -void __audit_seccomp(unsigned long syscall) +void __audit_seccomp(unsigned long syscall, long signr, int code) { struct audit_buffer *ab; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND); - audit_log_abend(ab, "seccomp", SIGKILL); + audit_log_abend(ab, "seccomp", signr); audit_log_format(ab, " syscall=%ld", syscall); + audit_log_format(ab, " compat=%d", is_compat_task()); + audit_log_format(ab, " ip=0x%lx", KSTK_EIP(current)); + audit_log_format(ab, " code=0x%x", code); audit_log_end(ab); } diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 0aeec1960f91..0f7c709a523e 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -60,18 +60,6 @@ struct seccomp_filter { /* Limit any path through the tree to 256KB worth of instructions. */ #define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter)) -static void seccomp_filter_log_failure(int syscall) -{ - int compat = 0; -#ifdef CONFIG_COMPAT - compat = is_compat_task(); -#endif - pr_info("%s[%d]: %ssystem call %d blocked at 0x%lx\n", - current->comm, task_pid_nr(current), - (compat ? "compat " : ""), - syscall, KSTK_EIP(current)); -} - /** * get_u32 - returns a u32 offset into data * @data: a unsigned 64 bit value @@ -381,7 +369,6 @@ void __secure_computing(int this_syscall) case SECCOMP_MODE_FILTER: if (seccomp_run_filters(this_syscall) == SECCOMP_RET_ALLOW) return; - seccomp_filter_log_failure(this_syscall); exit_sig = SIGSYS; break; #endif @@ -392,7 +379,7 @@ void __secure_computing(int this_syscall) #ifdef SECCOMP_DEBUG dump_stack(); #endif - audit_seccomp(this_syscall); + audit_seccomp(this_syscall, exit_code, SECCOMP_RET_KILL); do_exit(exit_sig); } -- cgit v1.2.3 From acf3b2c71ed20c53dc69826683417703c2a88059 Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Thu, 12 Apr 2012 16:47:59 -0500 Subject: seccomp: add SECCOMP_RET_ERRNO This change adds the SECCOMP_RET_ERRNO as a valid return value from a seccomp filter. Additionally, it makes the first use of the lower 16-bits for storing a filter-supplied errno. 16-bits is more than enough for the errno-base.h calls. Returning errors instead of immediately terminating processes that violate seccomp policy allow for broader use of this functionality for kernel attack surface reduction. For example, a linux container could maintain a whitelist of pre-existing system calls but drop all new ones with errnos. This would keep a logically static attack surface while providing errnos that may allow for graceful failure without the downside of do_exit() on a bad call. This change also changes the signature of __secure_computing. It appears the only direct caller is the arm entry code and it clobbers any possible return value (register) immediately. Signed-off-by: Will Drewry Acked-by: Serge Hallyn Reviewed-by: Kees Cook Acked-by: Eric Paris v18: - fix up comments and rebase - fix bad var name which was fixed in later revs - remove _int() and just change the __secure_computing signature v16-v17: ... v15: - use audit_seccomp and add a skip label. (eparis@redhat.com) - clean up and pad out return codes (indan@nul.nu) v14: - no change/rebase v13: - rebase on to 88ebdda6159ffc15699f204c33feb3e431bf9bdc v12: - move to WARN_ON if filter is NULL (oleg@redhat.com, luto@mit.edu, keescook@chromium.org) - return immediately for filter==NULL (keescook@chromium.org) - change evaluation to only compare the ACTION so that layered errnos don't result in the lowest one being returned. (keeschook@chromium.org) v11: - check for NULL filter (keescook@chromium.org) v10: - change loaders to fn v9: - n/a v8: - update Kconfig to note new need for syscall_set_return_value. - reordered such that TRAP behavior follows on later. - made the for loop a little less indent-y v7: - introduced Signed-off-by: James Morris --- arch/Kconfig | 6 ++++-- include/linux/seccomp.h | 10 ++++++---- kernel/seccomp.c | 42 ++++++++++++++++++++++++++++++++---------- 3 files changed, 42 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/arch/Kconfig b/arch/Kconfig index 91c2c730fc1a..beaab68c13b7 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -220,8 +220,10 @@ config HAVE_ARCH_SECCOMP_FILTER bool help This symbol should be selected by an architecure if it provides - asm/syscall.h, specifically syscall_get_arguments() and - syscall_get_arch(). + asm/syscall.h, specifically syscall_get_arguments(), + syscall_get_arch(), and syscall_set_return_value(). Additionally, + its system call entry path must respect a return value of -1 from + __secure_computing() and/or secure_computing(). config SECCOMP_FILTER def_bool y diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 86bb68fc7683..b4ce2c816e06 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -12,13 +12,14 @@ /* * All BPF programs must return a 32-bit value. - * The bottom 16-bits are reserved for future use. + * The bottom 16-bits are for optional return data. * The upper 16-bits are ordered from least permissive values to most. * * The ordering ensures that a min_t() over composed return values always * selects the least permissive choice. */ #define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */ +#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */ #define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ /* Masks for the return value sections. */ @@ -64,11 +65,12 @@ struct seccomp { struct seccomp_filter *filter; }; -extern void __secure_computing(int); -static inline void secure_computing(int this_syscall) +extern int __secure_computing(int); +static inline int secure_computing(int this_syscall) { if (unlikely(test_thread_flag(TIF_SECCOMP))) - __secure_computing(this_syscall); + return __secure_computing(this_syscall); + return 0; } extern long prctl_get_seccomp(void); diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 0f7c709a523e..5f78fb6d2212 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -199,15 +199,20 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen) static u32 seccomp_run_filters(int syscall) { struct seccomp_filter *f; - u32 ret = SECCOMP_RET_KILL; + u32 ret = SECCOMP_RET_ALLOW; + + /* Ensure unexpected behavior doesn't result in failing open. */ + if (WARN_ON(current->seccomp.filter == NULL)) + return SECCOMP_RET_KILL; + /* * All filters in the list are evaluated and the lowest BPF return - * value always takes priority. + * value always takes priority (ignoring the DATA). */ for (f = current->seccomp.filter; f; f = f->prev) { - ret = sk_run_filter(NULL, f->insns); - if (ret != SECCOMP_RET_ALLOW) - break; + u32 cur_ret = sk_run_filter(NULL, f->insns); + if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION)) + ret = cur_ret; } return ret; } @@ -346,11 +351,13 @@ static int mode1_syscalls_32[] = { }; #endif -void __secure_computing(int this_syscall) +int __secure_computing(int this_syscall) { int mode = current->seccomp.mode; int exit_sig = 0; int *syscall; + u32 ret = SECCOMP_RET_KILL; + int data; switch (mode) { case SECCOMP_MODE_STRICT: @@ -361,14 +368,26 @@ void __secure_computing(int this_syscall) #endif do { if (*syscall == this_syscall) - return; + return 0; } while (*++syscall); exit_sig = SIGKILL; break; #ifdef CONFIG_SECCOMP_FILTER case SECCOMP_MODE_FILTER: - if (seccomp_run_filters(this_syscall) == SECCOMP_RET_ALLOW) - return; + ret = seccomp_run_filters(this_syscall); + data = ret & SECCOMP_RET_DATA; + switch (ret & SECCOMP_RET_ACTION) { + case SECCOMP_RET_ERRNO: + /* Set the low-order 16-bits as a errno. */ + syscall_set_return_value(current, task_pt_regs(current), + -data, 0); + goto skip; + case SECCOMP_RET_ALLOW: + return 0; + case SECCOMP_RET_KILL: + default: + break; + } exit_sig = SIGSYS; break; #endif @@ -379,8 +398,11 @@ void __secure_computing(int this_syscall) #ifdef SECCOMP_DEBUG dump_stack(); #endif - audit_seccomp(this_syscall, exit_code, SECCOMP_RET_KILL); + audit_seccomp(this_syscall, exit_sig, ret); do_exit(exit_sig); +skip: + audit_seccomp(this_syscall, exit_sig, ret); + return -1; } long prctl_get_seccomp(void) -- cgit v1.2.3 From bb6ea4301a1109afdacaee576fedbfcd7152fc86 Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Thu, 12 Apr 2012 16:48:01 -0500 Subject: seccomp: Add SECCOMP_RET_TRAP Adds a new return value to seccomp filters that triggers a SIGSYS to be delivered with the new SYS_SECCOMP si_code. This allows in-process system call emulation, including just specifying an errno or cleanly dumping core, rather than just dying. Suggested-by: Markus Gutschke Suggested-by: Julien Tinnes Signed-off-by: Will Drewry Acked-by: Eric Paris v18: - acked-by, rebase - don't mention secure_computing_int() anymore v15: - use audit_seccomp/skip - pad out error spacing; clean up switch (indan@nul.nu) v14: - n/a v13: - rebase on to 88ebdda6159ffc15699f204c33feb3e431bf9bdc v12: - rebase on to linux-next v11: - clarify the comment (indan@nul.nu) - s/sigtrap/sigsys v10: - use SIGSYS, syscall_get_arch, updates arch/Kconfig note suggested-by (though original suggestion had other behaviors) v9: - changes to SIGILL v8: - clean up based on changes to dependent patches v7: - introduction Signed-off-by: James Morris --- arch/Kconfig | 14 +++++++++----- include/asm-generic/siginfo.h | 2 +- include/linux/seccomp.h | 1 + kernel/seccomp.c | 26 ++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/arch/Kconfig b/arch/Kconfig index beaab68c13b7..66aef13f6038 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -219,11 +219,15 @@ config ARCH_WANT_OLD_COMPAT_IPC config HAVE_ARCH_SECCOMP_FILTER bool help - This symbol should be selected by an architecure if it provides - asm/syscall.h, specifically syscall_get_arguments(), - syscall_get_arch(), and syscall_set_return_value(). Additionally, - its system call entry path must respect a return value of -1 from - __secure_computing() and/or secure_computing(). + This symbol should be selected by an architecure if it provides: + asm/syscall.h: + - syscall_get_arch() + - syscall_get_arguments() + - syscall_rollback() + - syscall_set_return_value() + SIGSYS siginfo_t support must be implemented. + __secure_computing()/secure_computing()'s return value must be + checked, with -1 resulting in the syscall being skipped. config SECCOMP_FILTER def_bool y diff --git a/include/asm-generic/siginfo.h b/include/asm-generic/siginfo.h index 31306f55eb02..af5d0350f84c 100644 --- a/include/asm-generic/siginfo.h +++ b/include/asm-generic/siginfo.h @@ -93,7 +93,7 @@ typedef struct siginfo { /* SIGSYS */ struct { - void __user *_call_addr; /* calling insn */ + void __user *_call_addr; /* calling user insn */ int _syscall; /* triggering system call number */ unsigned int _arch; /* AUDIT_ARCH_* of syscall */ } _sigsys; diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index b4ce2c816e06..317ccb78cf40 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -19,6 +19,7 @@ * selects the least permissive choice. */ #define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */ +#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */ #define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */ #define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 5f78fb6d2212..9c3830692a08 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -332,6 +332,26 @@ void put_seccomp_filter(struct task_struct *tsk) kfree(freeme); } } + +/** + * seccomp_send_sigsys - signals the task to allow in-process syscall emulation + * @syscall: syscall number to send to userland + * @reason: filter-supplied reason code to send to userland (via si_errno) + * + * Forces a SIGSYS with a code of SYS_SECCOMP and related sigsys info. + */ +static void seccomp_send_sigsys(int syscall, int reason) +{ + struct siginfo info; + memset(&info, 0, sizeof(info)); + info.si_signo = SIGSYS; + info.si_code = SYS_SECCOMP; + info.si_call_addr = (void __user *)KSTK_EIP(current); + info.si_errno = reason; + info.si_arch = syscall_get_arch(current, task_pt_regs(current)); + info.si_syscall = syscall; + force_sig_info(SIGSYS, &info, current); +} #endif /* CONFIG_SECCOMP_FILTER */ /* @@ -382,6 +402,12 @@ int __secure_computing(int this_syscall) syscall_set_return_value(current, task_pt_regs(current), -data, 0); goto skip; + case SECCOMP_RET_TRAP: + /* Show the handler the original registers. */ + syscall_rollback(current, task_pt_regs(current)); + /* Let the filter pass back 16 bits of data. */ + seccomp_send_sigsys(this_syscall, data); + goto skip; case SECCOMP_RET_ALLOW: return 0; case SECCOMP_RET_KILL: -- cgit v1.2.3 From fb0fadf9b213f55ca9368f3edafe51101d5d2deb Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Thu, 12 Apr 2012 16:48:02 -0500 Subject: ptrace,seccomp: Add PTRACE_SECCOMP support This change adds support for a new ptrace option, PTRACE_O_TRACESECCOMP, and a new return value for seccomp BPF programs, SECCOMP_RET_TRACE. When a tracer specifies the PTRACE_O_TRACESECCOMP ptrace option, the tracer will be notified, via PTRACE_EVENT_SECCOMP, for any syscall that results in a BPF program returning SECCOMP_RET_TRACE. The 16-bit SECCOMP_RET_DATA mask of the BPF program return value will be passed as the ptrace_message and may be retrieved using PTRACE_GETEVENTMSG. If the subordinate process is not using seccomp filter, then no system call notifications will occur even if the option is specified. If there is no tracer with PTRACE_O_TRACESECCOMP when SECCOMP_RET_TRACE is returned, the system call will not be executed and an -ENOSYS errno will be returned to userspace. This change adds a dependency on the system call slow path. Any future efforts to use the system call fast path for seccomp filter will need to address this restriction. Signed-off-by: Will Drewry Acked-by: Eric Paris v18: - rebase - comment fatal_signal check - acked-by - drop secure_computing_int comment v17: - ... v16: - update PT_TRACE_MASK to 0xbf4 so that STOP isn't clear on SETOPTIONS call (indan@nul.nu) [note PT_TRACE_MASK disappears in linux-next] v15: - add audit support for non-zero return codes - clean up style (indan@nul.nu) v14: - rebase/nochanges v13: - rebase on to 88ebdda6159ffc15699f204c33feb3e431bf9bdc (Brings back a change to ptrace.c and the masks.) v12: - rebase to linux-next - use ptrace_event and update arch/Kconfig to mention slow-path dependency - drop all tracehook changes and inclusion (oleg@redhat.com) v11: - invert the logic to just make it a PTRACE_SYSCALL accelerator (indan@nul.nu) v10: - moved to PTRACE_O_SECCOMP / PT_TRACE_SECCOMP v9: - n/a v8: - guarded PTRACE_SECCOMP use with an ifdef v7: - introduced Signed-off-by: James Morris --- arch/Kconfig | 10 +++++----- include/linux/ptrace.h | 5 ++++- include/linux/seccomp.h | 1 + kernel/seccomp.c | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/arch/Kconfig b/arch/Kconfig index 66aef13f6038..c024b3ed6675 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -219,15 +219,15 @@ config ARCH_WANT_OLD_COMPAT_IPC config HAVE_ARCH_SECCOMP_FILTER bool help - This symbol should be selected by an architecure if it provides: - asm/syscall.h: + An arch should select this symbol if it provides all of these things: - syscall_get_arch() - syscall_get_arguments() - syscall_rollback() - syscall_set_return_value() - SIGSYS siginfo_t support must be implemented. - __secure_computing()/secure_computing()'s return value must be - checked, with -1 resulting in the syscall being skipped. + - SIGSYS siginfo_t support + - secure_computing is called from a ptrace_event()-safe context + - secure_computing return value is checked and a return value of -1 + results in the system call being skipped immediately. config SECCOMP_FILTER def_bool y diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 5c719627c2aa..597e4fdb97fe 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -58,6 +58,7 @@ #define PTRACE_EVENT_EXEC 4 #define PTRACE_EVENT_VFORK_DONE 5 #define PTRACE_EVENT_EXIT 6 +#define PTRACE_EVENT_SECCOMP 7 /* Extended result codes which enabled by means other than options. */ #define PTRACE_EVENT_STOP 128 @@ -69,8 +70,9 @@ #define PTRACE_O_TRACEEXEC (1 << PTRACE_EVENT_EXEC) #define PTRACE_O_TRACEVFORKDONE (1 << PTRACE_EVENT_VFORK_DONE) #define PTRACE_O_TRACEEXIT (1 << PTRACE_EVENT_EXIT) +#define PTRACE_O_TRACESECCOMP (1 << PTRACE_EVENT_SECCOMP) -#define PTRACE_O_MASK 0x0000007f +#define PTRACE_O_MASK 0x000000ff #include @@ -98,6 +100,7 @@ #define PT_TRACE_EXEC PT_EVENT_FLAG(PTRACE_EVENT_EXEC) #define PT_TRACE_VFORK_DONE PT_EVENT_FLAG(PTRACE_EVENT_VFORK_DONE) #define PT_TRACE_EXIT PT_EVENT_FLAG(PTRACE_EVENT_EXIT) +#define PT_TRACE_SECCOMP PT_EVENT_FLAG(PTRACE_EVENT_SECCOMP) /* single stepping state bits (used on ARM and PA-RISC) */ #define PT_SINGLESTEP_BIT 31 diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 317ccb78cf40..5818e869651b 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -21,6 +21,7 @@ #define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */ #define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */ #define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */ +#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */ #define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ /* Masks for the return value sections. */ diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 9c3830692a08..d9db6ec46bc9 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -24,6 +24,7 @@ #ifdef CONFIG_SECCOMP_FILTER #include #include +#include #include #include #include @@ -408,6 +409,21 @@ int __secure_computing(int this_syscall) /* Let the filter pass back 16 bits of data. */ seccomp_send_sigsys(this_syscall, data); goto skip; + case SECCOMP_RET_TRACE: + /* Skip these calls if there is no tracer. */ + if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) + goto skip; + /* Allow the BPF to provide the event message */ + ptrace_event(PTRACE_EVENT_SECCOMP, data); + /* + * The delivery of a fatal signal during event + * notification may silently skip tracer notification. + * Terminating the task now avoids executing a system + * call that may not be intended. + */ + if (fatal_signal_pending(current)) + break; + return 0; case SECCOMP_RET_ALLOW: return 0; case SECCOMP_RET_KILL: -- cgit v1.2.3 From cbc91f71b51b8335f1fc7ccfca8011f31a717367 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Wed, 11 Apr 2012 16:05:27 +0530 Subject: uprobes/core: Decrement uprobe count before the pages are unmapped Uprobes has a callback (uprobe_munmap()) in the unmap path to maintain the uprobes count. In the exit path this callback gets called in unlink_file_vma(). However by the time unlink_file_vma() is called, the pages would have been unmapped (in unmap_vmas()) and the task->rss_stat counts accounted (in zap_pte_range()). If the exiting process has probepoints, uprobe_munmap() checks if the breakpoint instruction was around before decrementing the probe count. This results in a file backed page being reread by uprobe_munmap() and hence it does not find the breakpoint. This patch fixes this problem by moving the callback to unmap_single_vma(). Since unmap_single_vma() may not unmap the complete vma, add start and end parameters to uprobe_munmap(). This bug became apparent courtesy of commit c3f0327f8e9d ("mm: add rss counters consistency check"). Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Anton Arapov Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120411103527.23245.9835.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- include/linux/uprobes.h | 5 +++-- kernel/events/uprobes.c | 4 ++-- mm/memory.c | 3 +++ mm/mmap.c | 8 ++++---- 4 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index d594d3b3ad4c..efe4b3308c74 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -107,7 +107,7 @@ extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_mmap(struct vm_area_struct *vma); -extern void uprobe_munmap(struct vm_area_struct *vma); +extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end); extern void uprobe_free_utask(struct task_struct *t); extern void uprobe_copy_process(struct task_struct *t); extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); @@ -134,7 +134,8 @@ static inline int uprobe_mmap(struct vm_area_struct *vma) { return 0; } -static inline void uprobe_munmap(struct vm_area_struct *vma) +static inline void +uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end) { } static inline void uprobe_notify_resume(struct pt_regs *regs) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index c5caeecea1dc..985be4d80fe8 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1112,7 +1112,7 @@ int uprobe_mmap(struct vm_area_struct *vma) /* * Called in context of a munmap of a vma. */ -void uprobe_munmap(struct vm_area_struct *vma) +void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end) { struct list_head tmp_list; struct uprobe *uprobe, *u; @@ -1138,7 +1138,7 @@ void uprobe_munmap(struct vm_area_struct *vma) list_del(&uprobe->pending_list); vaddr = vma_address(vma, uprobe->offset); - if (vaddr >= vma->vm_start && vaddr < vma->vm_end) { + if (vaddr >= start && vaddr < end) { /* * An unregister could have removed the probe before * unmap. So check before we decrement the count. diff --git a/mm/memory.c b/mm/memory.c index 6105f475fa86..bf8b4035277d 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1307,6 +1307,9 @@ static void unmap_single_vma(struct mmu_gather *tlb, if (end <= vma->vm_start) return; + if (vma->vm_file) + uprobe_munmap(vma, start, end); + if (vma->vm_flags & VM_ACCOUNT) *nr_accounted += (end - start) >> PAGE_SHIFT; diff --git a/mm/mmap.c b/mm/mmap.c index b17a39f31a5e..15c21a150402 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -218,7 +218,6 @@ void unlink_file_vma(struct vm_area_struct *vma) mutex_lock(&mapping->i_mmap_mutex); __remove_shared_vm_struct(vma, file, mapping); mutex_unlock(&mapping->i_mmap_mutex); - uprobe_munmap(vma); } } @@ -548,10 +547,11 @@ again: remove_next = 1 + (end > next->vm_end); mapping = file->f_mapping; if (!(vma->vm_flags & VM_NONLINEAR)) { root = &mapping->i_mmap; - uprobe_munmap(vma); + uprobe_munmap(vma, vma->vm_start, vma->vm_end); if (adjust_next) - uprobe_munmap(next); + uprobe_munmap(next, next->vm_start, + next->vm_end); } mutex_lock(&mapping->i_mmap_mutex); @@ -632,7 +632,7 @@ again: remove_next = 1 + (end > next->vm_end); if (remove_next) { if (file) { - uprobe_munmap(next); + uprobe_munmap(next, next->vm_start, next->vm_end); fput(file); if (next->vm_flags & VM_EXECUTABLE) removed_exe_file_vma(mm); -- cgit v1.2.3 From c72e118334a2590f4f07d9e51490b902c33f5280 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 12 Apr 2012 22:16:05 +0000 Subject: inet: makes syn_ack_timeout mandatory There are two struct request_sock_ops providers, tcp and dccp. inet_csk_reqsk_queue_prune() can avoid testing syn_ack_timeout being NULL if we make it non NULL like syn_ack_timeout Signed-off-by: Eric Dumazet Cc: Gerrit Renker Cc: dccp@vger.kernel.org Signed-off-by: David S. Miller --- include/linux/dccp.h | 2 ++ net/dccp/ipv4.c | 6 ++++++ net/dccp/ipv6.c | 1 + net/ipv4/inet_connection_sock.c | 3 +-- 4 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index eaf95a023af4..d16294e2a118 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -549,6 +549,8 @@ static inline const char *dccp_role(const struct sock *sk) return NULL; } +extern void dccp_syn_ack_timeout(struct sock *sk, struct request_sock *req); + #endif /* __KERNEL__ */ #endif /* _LINUX_DCCP_H */ diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index caf6e1734b62..07f5579ca756 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -574,6 +574,11 @@ static void dccp_v4_reqsk_destructor(struct request_sock *req) kfree(inet_rsk(req)->opt); } +void dccp_syn_ack_timeout(struct sock *sk, struct request_sock *req) +{ +} +EXPORT_SYMBOL(dccp_syn_ack_timeout); + static struct request_sock_ops dccp_request_sock_ops __read_mostly = { .family = PF_INET, .obj_size = sizeof(struct dccp_request_sock), @@ -581,6 +586,7 @@ static struct request_sock_ops dccp_request_sock_ops __read_mostly = { .send_ack = dccp_reqsk_send_ack, .destructor = dccp_v4_reqsk_destructor, .send_reset = dccp_v4_ctl_send_reset, + .syn_ack_timeout = dccp_syn_ack_timeout, }; int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 4dc588f520e0..e923ac95bb04 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -343,6 +343,7 @@ static struct request_sock_ops dccp6_request_sock_ops = { .send_ack = dccp_reqsk_send_ack, .destructor = dccp_v6_reqsk_destructor, .send_reset = dccp_v6_ctl_send_reset, + .syn_ack_timeout = dccp_syn_ack_timeout, }; static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index c12396f2785f..d19f32aca6ca 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -555,8 +555,7 @@ void inet_csk_reqsk_queue_prune(struct sock *parent, syn_ack_recalc(req, thresh, max_retries, queue->rskq_defer_accept, &expire, &resend); - if (req->rsk_ops->syn_ack_timeout) - req->rsk_ops->syn_ack_timeout(parent, req); + req->rsk_ops->syn_ack_timeout(parent, req); if (!expire && (!resend || !req->rsk_ops->rtx_syn_ack(parent, req, NULL) || -- cgit v1.2.3 From 586d17c5a01bf1ae4e215adc6c48457eee5482bc Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 11 Apr 2012 20:43:52 +0000 Subject: virtio-net: send gratuitous packets when needed As hypervior does not have the knowledge of guest network configuration, it's better to ask guest to send gratuitous packets when needed. This patch implements VIRTIO_NET_F_GUEST_ANNOUNCE feature: hypervisor would notice the guest when it thinks it's time for guest to announce the link presnece. Guest tests VIRTIO_NET_S_ANNOUNCE bit during config change interrupt and woule send gratuitous packets through netif_notify_peers() and ack the notification through ctrl vq. We need to make sure the atomicy of read and ack in guest otherwise we may ack more times than being notified. This is done through handling the whole config change interrupt in an non-reentrant workqueue. Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 64 ++++++++++++++++++++++++++++++++++++++++++---- include/linux/virtio_net.h | 14 ++++++++++ 2 files changed, 73 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index af8acc85f4bb..fa58c7869954 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -66,12 +66,21 @@ struct virtnet_info { /* Host will merge rx buffers for big packets (shake it! shake it!) */ bool mergeable_rx_bufs; + /* enable config space updates */ + bool config_enable; + /* Active statistics */ struct virtnet_stats __percpu *stats; /* Work struct for refilling if we run low on memory. */ struct delayed_work refill; + /* Work struct for config space updates */ + struct work_struct config_work; + + /* Lock for config space updates */ + struct mutex config_lock; + /* Chain pages by the private ptr. */ struct page *pages; @@ -780,6 +789,16 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, return status == VIRTIO_NET_OK; } +static void virtnet_ack_link_announce(struct virtnet_info *vi) +{ + rtnl_lock(); + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_ANNOUNCE, + VIRTIO_NET_CTRL_ANNOUNCE_ACK, NULL, + 0, 0)) + dev_warn(&vi->dev->dev, "Failed to ack link announce.\n"); + rtnl_unlock(); +} + static int virtnet_close(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); @@ -951,20 +970,31 @@ static const struct net_device_ops virtnet_netdev = { #endif }; -static void virtnet_update_status(struct virtnet_info *vi) +static void virtnet_config_changed_work(struct work_struct *work) { + struct virtnet_info *vi = + container_of(work, struct virtnet_info, config_work); u16 v; + mutex_lock(&vi->config_lock); + if (!vi->config_enable) + goto done; + if (virtio_config_val(vi->vdev, VIRTIO_NET_F_STATUS, offsetof(struct virtio_net_config, status), &v) < 0) - return; + goto done; + + if (v & VIRTIO_NET_S_ANNOUNCE) { + netif_notify_peers(vi->dev); + virtnet_ack_link_announce(vi); + } /* Ignore unknown (future) status bits */ v &= VIRTIO_NET_S_LINK_UP; if (vi->status == v) - return; + goto done; vi->status = v; @@ -975,13 +1005,15 @@ static void virtnet_update_status(struct virtnet_info *vi) netif_carrier_off(vi->dev); netif_stop_queue(vi->dev); } +done: + mutex_unlock(&vi->config_lock); } static void virtnet_config_changed(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; - virtnet_update_status(vi); + queue_work(system_nrt_wq, &vi->config_work); } static int init_vqs(struct virtnet_info *vi) @@ -1075,6 +1107,9 @@ static int virtnet_probe(struct virtio_device *vdev) goto free; INIT_DELAYED_WORK(&vi->refill, refill_work); + mutex_init(&vi->config_lock); + vi->config_enable = true; + INIT_WORK(&vi->config_work, virtnet_config_changed_work); sg_init_table(vi->rx_sg, ARRAY_SIZE(vi->rx_sg)); sg_init_table(vi->tx_sg, ARRAY_SIZE(vi->tx_sg)); @@ -1110,7 +1145,7 @@ static int virtnet_probe(struct virtio_device *vdev) otherwise get link status from config. */ if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { netif_carrier_off(dev); - virtnet_update_status(vi); + queue_work(system_nrt_wq, &vi->config_work); } else { vi->status = VIRTIO_NET_S_LINK_UP; netif_carrier_on(dev); @@ -1169,10 +1204,17 @@ static void __devexit virtnet_remove(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; + /* Prevent config work handler from accessing the device. */ + mutex_lock(&vi->config_lock); + vi->config_enable = false; + mutex_unlock(&vi->config_lock); + unregister_netdev(vi->dev); remove_vq_common(vi); + flush_work(&vi->config_work); + free_percpu(vi->stats); free_netdev(vi->dev); } @@ -1182,6 +1224,11 @@ static int virtnet_freeze(struct virtio_device *vdev) { struct virtnet_info *vi = vdev->priv; + /* Prevent config work handler from accessing the device */ + mutex_lock(&vi->config_lock); + vi->config_enable = false; + mutex_unlock(&vi->config_lock); + virtqueue_disable_cb(vi->rvq); virtqueue_disable_cb(vi->svq); if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) @@ -1195,6 +1242,8 @@ static int virtnet_freeze(struct virtio_device *vdev) remove_vq_common(vi); + flush_work(&vi->config_work); + return 0; } @@ -1215,6 +1264,10 @@ static int virtnet_restore(struct virtio_device *vdev) if (!try_fill_recv(vi, GFP_KERNEL)) queue_delayed_work(system_nrt_wq, &vi->refill, 0); + mutex_lock(&vi->config_lock); + vi->config_enable = true; + mutex_unlock(&vi->config_lock); + return 0; } #endif @@ -1232,6 +1285,7 @@ static unsigned int features[] = { VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, + VIRTIO_NET_F_GUEST_ANNOUNCE, }; static struct virtio_driver virtio_net_driver = { diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 970d5a2a9047..2470f541af50 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -49,8 +49,11 @@ #define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ #define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ #define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce device on the + * network */ #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ +#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */ struct virtio_net_config { /* The config defining mac address (if VIRTIO_NET_F_MAC) */ @@ -152,4 +155,15 @@ struct virtio_net_ctrl_mac { #define VIRTIO_NET_CTRL_VLAN_ADD 0 #define VIRTIO_NET_CTRL_VLAN_DEL 1 +/* + * Control link announce acknowledgement + * + * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that + * driver has recevied the notification; device would clear the + * VIRTIO_NET_S_ANNOUNCE bit in the status field after it receives + * this command. + */ +#define VIRTIO_NET_CTRL_ANNOUNCE 3 + #define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0 + #endif /* _LINUX_VIRTIO_NET_H */ -- cgit v1.2.3 From 35fca53e15a696adbea300a981df4bbfb09a76d6 Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Sun, 15 Apr 2012 20:42:28 +0800 Subject: mmzone: fix comment typo coelesce -> coalesce Signed-off-by: Wang YanQing Signed-off-by: Jiri Kosina --- include/linux/mmzone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index dff711509661..41aa49b74821 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -30,7 +30,7 @@ /* * PAGE_ALLOC_COSTLY_ORDER is the order at which allocations are deemed * costly to service. That is between allocation orders which should - * coelesce naturally under reasonable reclaim pressure and those which + * coalesce naturally under reasonable reclaim pressure and those which * will not. */ #define PAGE_ALLOC_COSTLY_ORDER 3 -- cgit v1.2.3 From 95c961747284a6b83a5e2d81240e214b0fa3464d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 15 Apr 2012 05:58:06 +0000 Subject: net: cleanup unsigned to unsigned int Use of "unsigned int" is preferred to bare "unsigned" in net tree. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/ip_vs.h | 12 ++++---- include/linux/netdevice.h | 14 +++++----- include/linux/netfilter/ipset/ip_set.h | 8 +++--- include/linux/netfilter/nf_conntrack_h323_types.h | 12 ++++---- include/linux/netfilter/nfnetlink.h | 2 +- include/linux/socket.h | 4 +-- include/net/addrconf.h | 2 +- include/net/af_unix.h | 2 +- include/net/compat.h | 8 +++--- include/net/dn.h | 2 +- include/net/dn_fib.h | 6 ++-- include/net/dn_route.h | 4 +-- include/net/dst_ops.h | 2 +- include/net/icmp.h | 2 +- include/net/if_inet6.h | 2 +- include/net/ip6_route.h | 2 +- include/net/ip_fib.h | 4 +-- include/net/ip_vs.h | 16 +++++------ include/net/ipv6.h | 2 +- include/net/llc_c_ev.h | 2 +- include/net/neighbour.h | 4 +-- include/net/netns/hash.h | 2 +- include/net/nfc/nfc.h | 2 +- include/net/pkt_sched.h | 2 +- include/net/route.h | 6 ++-- include/net/sctp/structs.h | 8 +++--- include/net/tcp.h | 2 +- include/net/udp.h | 2 +- include/net/wimax.h | 4 +-- include/net/x25.h | 2 +- net/802/fc.c | 2 +- net/802/fddi.c | 2 +- net/802/hippi.c | 2 +- net/802/tr.c | 4 +-- net/9p/client.c | 6 ++-- net/9p/trans_fd.c | 2 +- net/atm/br2684.c | 2 +- net/atm/mpoa_proc.c | 2 +- net/atm/signaling.c | 2 +- net/ax25/ax25_ip.c | 4 +-- net/batman-adv/bat_sysfs.c | 6 ++-- net/bridge/br_forward.c | 2 +- net/bridge/br_multicast.c | 10 +++---- net/bridge/br_netlink.c | 2 +- net/bridge/br_private_stp.h | 7 ++--- net/bridge/br_stp.c | 4 +-- net/bridge/br_stp_timer.c | 6 ++-- net/bridge/br_sysfs_br.c | 2 +- net/caif/caif_socket.c | 2 +- net/ceph/auth_x.h | 6 ++-- net/ceph/ceph_common.c | 4 +-- net/ceph/ceph_hash.c | 6 ++-- net/ceph/crush/mapper.c | 8 +++--- net/ceph/debugfs.c | 6 ++-- net/ceph/messenger.c | 16 +++++------ net/ceph/mon_client.c | 10 +++---- net/ceph/osd_client.c | 2 +- net/ceph/osdmap.c | 14 +++++----- net/compat.c | 10 +++---- net/core/datagram.c | 6 ++-- net/core/dev.c | 7 +++-- net/core/filter.c | 2 +- net/core/neighbour.c | 2 +- net/core/net-sysfs.c | 8 +++--- net/core/pktgen.c | 20 ++++++------- net/core/rtnetlink.c | 6 ++-- net/core/skbuff.c | 2 +- net/core/sock.c | 2 +- net/dccp/ccids/ccid3.c | 12 ++++---- net/dccp/dccp.h | 8 +++--- net/dccp/input.c | 10 +++---- net/decnet/af_decnet.c | 6 ++-- net/decnet/dn_fib.c | 5 ++-- net/decnet/dn_nsp_in.c | 2 +- net/decnet/dn_nsp_out.c | 4 +-- net/decnet/dn_route.c | 18 ++++++------ net/decnet/dn_rules.c | 4 +-- net/dns_resolver/dns_key.c | 2 +- net/dns_resolver/internal.h | 2 +- net/ethernet/eth.c | 2 +- net/ieee802154/6lowpan.c | 2 +- net/ieee802154/dgram.c | 6 ++-- net/ieee802154/raw.c | 2 +- net/ipv4/arp.c | 4 +-- net/ipv4/devinet.c | 2 +- net/ipv4/fib_frontend.c | 10 +++---- net/ipv4/inet_connection_sock.c | 3 +- net/ipv4/inet_timewait_sock.c | 2 +- net/ipv4/ip_options.c | 2 +- net/ipv4/ip_sockglue.c | 4 +-- net/ipv4/ipconfig.c | 2 +- net/ipv4/netfilter.c | 2 +- net/ipv4/netfilter/arp_tables.c | 2 +- net/ipv4/netfilter/ip_tables.c | 2 +- net/ipv4/netfilter/nf_nat_sip.c | 2 +- net/ipv4/ping.c | 8 ++++-- net/ipv4/route.c | 20 ++++++------- net/ipv4/sysctl_net_ipv4.c | 2 +- net/ipv4/tcp.c | 8 +++--- net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_output.c | 24 ++++++++-------- net/ipv4/tcp_probe.c | 2 +- net/ipv4/udp.c | 2 +- net/ipv6/addrconf_core.c | 2 +- net/ipv6/datagram.c | 2 +- net/ipv6/ip6_flowlabel.c | 4 +-- net/ipv6/ip6_tunnel.c | 2 +- net/ipv6/ipv6_sockglue.c | 2 +- net/ipv6/netfilter/ip6_tables.c | 2 +- net/ipv6/route.c | 11 ++++---- net/ipv6/xfrm6_tunnel.c | 6 ++-- net/irda/ircomm/ircomm_tty_ioctl.c | 2 +- net/key/af_key.c | 4 +-- net/l2tp/l2tp_core.h | 16 +++++------ net/llc/af_llc.c | 2 +- net/netfilter/ipset/ip_set_core.c | 6 ++-- net/netfilter/ipvs/ip_vs_app.c | 2 +- net/netfilter/ipvs/ip_vs_conn.c | 34 +++++++++++------------ net/netfilter/ipvs/ip_vs_core.c | 2 +- net/netfilter/ipvs/ip_vs_ctl.c | 26 ++++++++--------- net/netfilter/ipvs/ip_vs_dh.c | 2 +- net/netfilter/ipvs/ip_vs_ftp.c | 2 +- net/netfilter/ipvs/ip_vs_lblc.c | 6 ++-- net/netfilter/ipvs/ip_vs_lblcr.c | 6 ++-- net/netfilter/ipvs/ip_vs_proto.c | 12 ++++---- net/netfilter/ipvs/ip_vs_sh.c | 2 +- net/netfilter/ipvs/ip_vs_sync.c | 4 +-- net/netfilter/nf_conntrack_h323_main.c | 2 +- net/netfilter/nfnetlink.c | 2 +- net/netfilter/xt_recent.c | 2 +- net/netrom/nr_dev.c | 2 +- net/nfc/core.c | 4 +-- net/nfc/nfc.h | 2 +- net/openvswitch/vport-netdev.c | 4 +-- net/packet/af_packet.c | 2 +- net/phonet/af_phonet.c | 2 +- net/phonet/pep.c | 8 +++--- net/phonet/pn_dev.c | 2 +- net/phonet/socket.c | 12 ++++---- net/phonet/sysctl.c | 3 +- net/rose/rose_dev.c | 2 +- net/rose/rose_subr.c | 2 +- net/rxrpc/af_rxrpc.c | 8 +++--- net/rxrpc/ar-ack.c | 6 ++-- net/rxrpc/ar-call.c | 4 +-- net/rxrpc/ar-input.c | 2 +- net/rxrpc/ar-internal.h | 16 +++++------ net/rxrpc/ar-key.c | 22 +++++++-------- net/rxrpc/rxkad.c | 6 ++-- net/sctp/associola.c | 4 +-- net/sctp/output.c | 4 +-- net/sctp/outqueue.c | 2 +- net/sctp/sm_sideeffect.c | 4 +-- net/sctp/sm_statefuns.c | 4 +-- net/socket.c | 18 ++++++------ net/sunrpc/auth_gss/gss_krb5_mech.c | 2 +- net/sunrpc/cache.c | 2 +- net/sunrpc/timer.c | 6 ++-- net/sunrpc/xdr.c | 2 +- net/sunrpc/xprt.c | 2 +- net/tipc/link.c | 2 +- net/tipc/socket.c | 8 +++--- net/unix/af_unix.c | 21 +++++++------- net/wimax/stack.c | 5 ++-- net/wireless/wext-core.c | 6 ++-- net/x25/x25_facilities.c | 4 +-- net/xfrm/xfrm_hash.h | 8 +++--- 167 files changed, 461 insertions(+), 455 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ip_vs.h b/include/linux/ip_vs.h index 4deb3834d62c..be0ef3df4acb 100644 --- a/include/linux/ip_vs.h +++ b/include/linux/ip_vs.h @@ -125,8 +125,8 @@ struct ip_vs_service_user { /* virtual service options */ char sched_name[IP_VS_SCHEDNAME_MAXLEN]; - unsigned flags; /* virtual service flags */ - unsigned timeout; /* persistent timeout in sec */ + unsigned int flags; /* virtual service flags */ + unsigned int timeout; /* persistent timeout in sec */ __be32 netmask; /* persistent netmask */ }; @@ -137,7 +137,7 @@ struct ip_vs_dest_user { __be16 port; /* real server options */ - unsigned conn_flags; /* connection flags */ + unsigned int conn_flags; /* connection flags */ int weight; /* destination weight */ /* thresholds for active connections */ @@ -187,8 +187,8 @@ struct ip_vs_service_entry { /* service options */ char sched_name[IP_VS_SCHEDNAME_MAXLEN]; - unsigned flags; /* virtual service flags */ - unsigned timeout; /* persistent timeout */ + unsigned int flags; /* virtual service flags */ + unsigned int timeout; /* persistent timeout */ __be32 netmask; /* persistent netmask */ /* number of real servers */ @@ -202,7 +202,7 @@ struct ip_vs_service_entry { struct ip_vs_dest_entry { __be32 addr; /* destination address */ __be16 port; - unsigned conn_flags; /* connection flags */ + unsigned int conn_flags; /* connection flags */ int weight; /* destination weight */ __u32 u_threshold; /* upper threshold */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5cbaa20f1659..d3122321d8b1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -288,7 +288,7 @@ struct hh_cache { struct header_ops { int (*create) (struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, - const void *saddr, unsigned len); + const void *saddr, unsigned int len); int (*parse)(const struct sk_buff *skb, unsigned char *haddr); int (*rebuild)(struct sk_buff *skb); int (*cache)(const struct neighbour *neigh, struct hh_cache *hh, __be16 type); @@ -1689,7 +1689,7 @@ static inline void *skb_gro_network_header(struct sk_buff *skb) static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, const void *saddr, - unsigned len) + unsigned int len) { if (!dev->header_ops || !dev->header_ops->create) return 0; @@ -1740,7 +1740,7 @@ struct softnet_data { unsigned int input_queue_head; unsigned int input_queue_tail; #endif - unsigned dropped; + unsigned int dropped; struct sk_buff_head input_pkt_queue; struct napi_struct backlog; }; @@ -1925,7 +1925,7 @@ static inline void netdev_sent_queue(struct net_device *dev, unsigned int bytes) } static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue, - unsigned pkts, unsigned bytes) + unsigned int pkts, unsigned int bytes) { #ifdef CONFIG_BQL if (unlikely(!bytes)) @@ -1949,7 +1949,7 @@ static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue, } static inline void netdev_completed_queue(struct net_device *dev, - unsigned pkts, unsigned bytes) + unsigned int pkts, unsigned int bytes) { netdev_tx_completed_queue(netdev_get_tx_queue(dev, 0), pkts, bytes); } @@ -2144,9 +2144,9 @@ extern void netdev_rx_handler_unregister(struct net_device *dev); extern bool dev_valid_name(const char *name); extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *); extern int dev_ethtool(struct net *net, struct ifreq *); -extern unsigned dev_get_flags(const struct net_device *); +extern unsigned int dev_get_flags(const struct net_device *); extern int __dev_change_flags(struct net_device *, unsigned int flags); -extern int dev_change_flags(struct net_device *, unsigned); +extern int dev_change_flags(struct net_device *, unsigned int); extern void __dev_notify_flags(struct net_device *, unsigned int old_flags); extern int dev_change_name(struct net_device *, const char *); extern int dev_set_alias(struct net_device *, const char *, size_t); diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index d6d549cf1f23..2edc64cab739 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -478,8 +478,8 @@ union ip_set_name_index { #define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ struct ip_set_req_get_set { - unsigned op; - unsigned version; + unsigned int op; + unsigned int version; union ip_set_name_index set; }; @@ -488,8 +488,8 @@ struct ip_set_req_get_set { #define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ struct ip_set_req_version { - unsigned op; - unsigned version; + unsigned int op; + unsigned int version; }; #endif /*_IP_SET_H */ diff --git a/include/linux/netfilter/nf_conntrack_h323_types.h b/include/linux/netfilter/nf_conntrack_h323_types.h index f35b6b4801e7..b0821f45fbe4 100644 --- a/include/linux/netfilter/nf_conntrack_h323_types.h +++ b/include/linux/netfilter/nf_conntrack_h323_types.h @@ -7,12 +7,12 @@ typedef struct TransportAddress_ipAddress { /* SEQUENCE */ int options; /* No use */ - unsigned ip; + unsigned int ip; } TransportAddress_ipAddress; typedef struct TransportAddress_ip6Address { /* SEQUENCE */ int options; /* No use */ - unsigned ip; + unsigned int ip; } TransportAddress_ip6Address; typedef struct TransportAddress { /* CHOICE */ @@ -96,12 +96,12 @@ typedef struct DataType { /* CHOICE */ typedef struct UnicastAddress_iPAddress { /* SEQUENCE */ int options; /* No use */ - unsigned network; + unsigned int network; } UnicastAddress_iPAddress; typedef struct UnicastAddress_iP6Address { /* SEQUENCE */ int options; /* No use */ - unsigned network; + unsigned int network; } UnicastAddress_iP6Address; typedef struct UnicastAddress { /* CHOICE */ @@ -698,7 +698,7 @@ typedef struct RegistrationRequest { /* SEQUENCE */ } options; RegistrationRequest_callSignalAddress callSignalAddress; RegistrationRequest_rasAddress rasAddress; - unsigned timeToLive; + unsigned int timeToLive; } RegistrationRequest; typedef struct RegistrationConfirm_callSignalAddress { /* SEQUENCE OF */ @@ -730,7 +730,7 @@ typedef struct RegistrationConfirm { /* SEQUENCE */ eRegistrationConfirm_genericData = (1 << 12), } options; RegistrationConfirm_callSignalAddress callSignalAddress; - unsigned timeToLive; + unsigned int timeToLive; } RegistrationConfirm; typedef struct UnregistrationRequest_callSignalAddress { /* SEQUENCE OF */ diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 6fd1f0d07e64..a1048c1587d1 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -80,7 +80,7 @@ extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n); extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); extern int nfnetlink_has_listeners(struct net *net, unsigned int group); -extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group, +extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group, int echo, gfp_t flags); extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); diff --git a/include/linux/socket.h b/include/linux/socket.h index b84bbd48b874..25d6322fb635 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -68,13 +68,13 @@ struct msghdr { __kernel_size_t msg_iovlen; /* Number of blocks */ void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */ __kernel_size_t msg_controllen; /* Length of cmsg list */ - unsigned msg_flags; + unsigned int msg_flags; }; /* For recvmmsg/sendmmsg */ struct mmsghdr { struct msghdr msg_hdr; - unsigned msg_len; + unsigned int msg_len; }; /* diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 757a17638b1b..27f450ba9514 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -92,7 +92,7 @@ extern void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr); static inline unsigned long addrconf_timeout_fixup(u32 timeout, - unsigned unit) + unsigned int unit) { if (timeout == 0xffffffff) return ~0UL; diff --git a/include/net/af_unix.h b/include/net/af_unix.h index ca68e2cef230..2ee33da36a7a 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -22,7 +22,7 @@ extern struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1]; struct unix_address { atomic_t refcnt; int len; - unsigned hash; + unsigned int hash; struct sockaddr_un name[0]; }; diff --git a/include/net/compat.h b/include/net/compat.h index a974ae92d182..6e9565324989 100644 --- a/include/net/compat.h +++ b/include/net/compat.h @@ -42,12 +42,12 @@ extern int compat_sock_get_timestampns(struct sock *, struct timespec __user *); extern int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *); extern int verify_compat_iovec(struct msghdr *, struct iovec *, struct sockaddr_storage *, int); -extern asmlinkage long compat_sys_sendmsg(int,struct compat_msghdr __user *,unsigned); +extern asmlinkage long compat_sys_sendmsg(int,struct compat_msghdr __user *,unsigned int); extern asmlinkage long compat_sys_sendmmsg(int, struct compat_mmsghdr __user *, - unsigned, unsigned); -extern asmlinkage long compat_sys_recvmsg(int,struct compat_msghdr __user *,unsigned); + unsigned int, unsigned int); +extern asmlinkage long compat_sys_recvmsg(int,struct compat_msghdr __user *,unsigned int); extern asmlinkage long compat_sys_recvmmsg(int, struct compat_mmsghdr __user *, - unsigned, unsigned, + unsigned int, unsigned int, struct compat_timespec __user *); extern asmlinkage long compat_sys_getsockopt(int, int, int, char __user *, int __user *); extern int put_cmsg_compat(struct msghdr*, int, int, int, void *); diff --git a/include/net/dn.h b/include/net/dn.h index 814af0b9387d..c88bf4ebd330 100644 --- a/include/net/dn.h +++ b/include/net/dn.h @@ -199,7 +199,7 @@ static inline void dn_sk_ports_copy(struct flowidn *fld, struct dn_scp *scp) fld->fld_dport = scp->addrrem; } -extern unsigned dn_mss_from_pmtu(struct net_device *dev, int mtu); +extern unsigned int dn_mss_from_pmtu(struct net_device *dev, int mtu); #define DN_MENUVER_ACC 0x01 #define DN_MENUVER_USR 0x02 diff --git a/include/net/dn_fib.h b/include/net/dn_fib.h index 782ef7cb4930..1ee9d4bda30d 100644 --- a/include/net/dn_fib.h +++ b/include/net/dn_fib.h @@ -31,7 +31,7 @@ struct dn_fib_res { struct dn_fib_nh { struct net_device *nh_dev; - unsigned nh_flags; + unsigned int nh_flags; unsigned char nh_scope; int nh_weight; int nh_power; @@ -45,7 +45,7 @@ struct dn_fib_info { int fib_treeref; atomic_t fib_clntref; int fib_dead; - unsigned fib_flags; + unsigned int fib_flags; int fib_protocol; __le16 fib_prefsrc; __u32 fib_priority; @@ -140,7 +140,7 @@ extern void dn_fib_table_cleanup(void); */ extern void dn_fib_rules_init(void); extern void dn_fib_rules_cleanup(void); -extern unsigned dnet_addr_type(__le16 addr); +extern unsigned int dnet_addr_type(__le16 addr); extern int dn_fib_lookup(struct flowidn *fld, struct dn_fib_res *res); extern int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb); diff --git a/include/net/dn_route.h b/include/net/dn_route.h index 81712cfa1ddf..c507e05d172f 100644 --- a/include/net/dn_route.h +++ b/include/net/dn_route.h @@ -76,8 +76,8 @@ struct dn_route { __le16 rt_src_map; __le16 rt_dst_map; - unsigned rt_flags; - unsigned rt_type; + unsigned int rt_flags; + unsigned int rt_type; }; static inline bool dn_is_input_route(struct dn_route *rt) diff --git a/include/net/dst_ops.h b/include/net/dst_ops.h index e1c2ee0eef47..3682a0a076c1 100644 --- a/include/net/dst_ops.h +++ b/include/net/dst_ops.h @@ -12,7 +12,7 @@ struct sk_buff; struct dst_ops { unsigned short family; __be16 protocol; - unsigned gc_thresh; + unsigned int gc_thresh; int (*gc)(struct dst_ops *ops); struct dst_entry * (*check)(struct dst_entry *, __u32 cookie); diff --git a/include/net/icmp.h b/include/net/icmp.h index ce70a581d95c..9ac2524d1402 100644 --- a/include/net/icmp.h +++ b/include/net/icmp.h @@ -25,7 +25,7 @@ struct icmp_err { int errno; - unsigned fatal:1; + unsigned int fatal:1; }; extern const struct icmp_err icmp_err_convert[]; diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 51a7031b4aa3..50f325fd0691 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -120,7 +120,7 @@ struct ifmcaddr6 { unsigned char mca_crcount; unsigned long mca_sfcount[2]; struct timer_list mca_timer; - unsigned mca_flags; + unsigned int mca_flags; int mca_users; atomic_t mca_refcnt; spinlock_t mca_lock; diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 2ad92ca4e6f3..c062b6773cc7 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -146,7 +146,7 @@ struct rt6_rtnl_dump_arg { extern int rt6_dump_route(struct rt6_info *rt, void *p_arg); extern void rt6_ifdown(struct net *net, struct net_device *dev); -extern void rt6_mtu_change(struct net_device *dev, unsigned mtu); +extern void rt6_mtu_change(struct net_device *dev, unsigned int mtu); extern void rt6_remove_prefsrc(struct inet6_ifaddr *ifp); diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 10422ef14e28..78df0866cc38 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -49,7 +49,7 @@ struct fib_nh { struct net_device *nh_dev; struct hlist_node nh_hash; struct fib_info *nh_parent; - unsigned nh_flags; + unsigned int nh_flags; unsigned char nh_scope; #ifdef CONFIG_IP_ROUTE_MULTIPATH int nh_weight; @@ -74,7 +74,7 @@ struct fib_info { struct net *fib_net; int fib_treeref; atomic_t fib_clntref; - unsigned fib_flags; + unsigned int fib_flags; unsigned char fib_dead; unsigned char fib_protocol; unsigned char fib_scope; diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 2bdee51ba30d..a903a828bcef 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -580,8 +580,8 @@ struct ip_vs_service_user_kern { /* virtual service options */ char *sched_name; char *pe_name; - unsigned flags; /* virtual service flags */ - unsigned timeout; /* persistent timeout in sec */ + unsigned int flags; /* virtual service flags */ + unsigned int timeout; /* persistent timeout in sec */ u32 netmask; /* persistent netmask */ }; @@ -592,7 +592,7 @@ struct ip_vs_dest_user_kern { u16 port; /* real server options */ - unsigned conn_flags; /* connection flags */ + unsigned int conn_flags; /* connection flags */ int weight; /* destination weight */ /* thresholds for active connections */ @@ -616,8 +616,8 @@ struct ip_vs_service { union nf_inet_addr addr; /* IP address for virtual service */ __be16 port; /* port number for the service */ __u32 fwmark; /* firewall mark of the service */ - unsigned flags; /* service status flags */ - unsigned timeout; /* persistent timeout in ticks */ + unsigned int flags; /* service status flags */ + unsigned int timeout; /* persistent timeout in ticks */ __be32 netmask; /* grouping granularity */ struct net *net; @@ -647,7 +647,7 @@ struct ip_vs_dest { u16 af; /* address family */ __be16 port; /* port number of the server */ union nf_inet_addr addr; /* IP address of the server */ - volatile unsigned flags; /* dest status flags */ + volatile unsigned int flags; /* dest status flags */ atomic_t conn_flags; /* flags to copy to conn */ atomic_t weight; /* server weight */ @@ -953,7 +953,7 @@ static inline int sysctl_sync_ver(struct netns_ipvs *ipvs) * IPVS core functions * (from ip_vs_core.c) */ -extern const char *ip_vs_proto_name(unsigned proto); +extern const char *ip_vs_proto_name(unsigned int proto); extern void ip_vs_init_hash_table(struct list_head *table, int rows); #define IP_VS_INIT_HASH_TABLE(t) ip_vs_init_hash_table((t), ARRAY_SIZE((t))) @@ -1014,7 +1014,7 @@ extern void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport); struct ip_vs_conn *ip_vs_conn_new(const struct ip_vs_conn_param *p, const union nf_inet_addr *daddr, - __be16 dport, unsigned flags, + __be16 dport, unsigned int flags, struct ip_vs_dest *dest, __u32 fwmark); extern void ip_vs_conn_expire_now(struct ip_vs_conn *cp); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index e4170a22fc6f..2d3fca37c6a6 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -345,7 +345,7 @@ static inline int ipv6_addr_equal(const struct in6_addr *a1, static inline int __ipv6_prefix_equal(const __be32 *a1, const __be32 *a2, unsigned int prefixlen) { - unsigned pdw, pbi; + unsigned int pdw, pbi; /* check complete u32 in prefix */ pdw = prefixlen >> 5; diff --git a/include/net/llc_c_ev.h b/include/net/llc_c_ev.h index 23a409381fa9..6ca3113df39e 100644 --- a/include/net/llc_c_ev.h +++ b/include/net/llc_c_ev.h @@ -264,6 +264,6 @@ extern int llc_conn_ev_qlfy_set_status_rst_done(struct sock *sk, static __inline__ int llc_conn_space(struct sock *sk, struct sk_buff *skb) { return atomic_read(&sk->sk_rmem_alloc) + skb->truesize < - (unsigned)sk->sk_rcvbuf; + (unsigned int)sk->sk_rcvbuf; } #endif /* LLC_C_EV_H */ diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 63ebd70ba693..6cdfeedb650b 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -322,7 +322,7 @@ static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) #ifdef CONFIG_BRIDGE_NETFILTER static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb) { - unsigned seq, hh_alen; + unsigned int seq, hh_alen; do { seq = read_seqbegin(&hh->hh_lock); @@ -335,7 +335,7 @@ static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb) static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb) { - unsigned seq; + unsigned int seq; int hh_len; do { diff --git a/include/net/netns/hash.h b/include/net/netns/hash.h index 548d78f2cc47..c06ac58ca107 100644 --- a/include/net/netns/hash.h +++ b/include/net/netns/hash.h @@ -5,7 +5,7 @@ struct net; -static inline unsigned net_hash_mix(struct net *net) +static inline unsigned int net_hash_mix(struct net *net) { #ifdef CONFIG_NET_NS /* diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index bac070bf3514..79955a238ccc 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -86,7 +86,7 @@ struct nfc_genl_data { }; struct nfc_dev { - unsigned idx; + unsigned int idx; struct nfc_target *targets; int n_targets; int targets_generation; diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index fffdc603f4c8..66f5ac370f92 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -107,7 +107,7 @@ extern int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp, /* Calculate maximal size of packet seen by hard_start_xmit routine of this device. */ -static inline unsigned psched_mtu(const struct net_device *dev) +static inline unsigned int psched_mtu(const struct net_device *dev) { return dev->mtu + dev->hard_header_len; } diff --git a/include/net/route.h b/include/net/route.h index b1c0d5b564c2..ed2b78e2375d 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -50,7 +50,7 @@ struct rtable { __be32 rt_key_src; int rt_genid; - unsigned rt_flags; + unsigned int rt_flags; __u16 rt_type; __u8 rt_key_tos; @@ -185,8 +185,8 @@ extern unsigned short ip_rt_frag_needed(struct net *net, const struct iphdr *iph unsigned short new_mtu, struct net_device *dev); extern void ip_rt_send_redirect(struct sk_buff *skb); -extern unsigned inet_addr_type(struct net *net, __be32 addr); -extern unsigned inet_dev_addr_type(struct net *net, const struct net_device *dev, __be32 addr); +extern unsigned int inet_addr_type(struct net *net, __be32 addr); +extern unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev, __be32 addr); extern void ip_rt_multicast_event(struct in_device *); extern int ip_rt_ioctl(struct net *, unsigned int cmd, void __user *arg); extern void ip_rt_get_source(u8 *src, struct sk_buff *skb, struct rtable *rt); diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 88949a994538..e4652fe58958 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1145,10 +1145,10 @@ struct sctp_outq { /* Data pending that has never been transmitted. */ struct list_head out_chunk_list; - unsigned out_qlen; /* Total length of queued data chunks. */ + unsigned int out_qlen; /* Total length of queued data chunks. */ /* Error of send failed, may used in SCTP_SEND_FAILED event. */ - unsigned error; + unsigned int error; /* These are control chunks we want to send. */ struct list_head control_chunk_list; @@ -2000,8 +2000,8 @@ void sctp_assoc_update(struct sctp_association *old, __u32 sctp_association_get_next_tsn(struct sctp_association *); void sctp_assoc_sync_pmtu(struct sctp_association *); -void sctp_assoc_rwnd_increase(struct sctp_association *, unsigned); -void sctp_assoc_rwnd_decrease(struct sctp_association *, unsigned); +void sctp_assoc_rwnd_increase(struct sctp_association *, unsigned int); +void sctp_assoc_rwnd_decrease(struct sctp_association *, unsigned int); void sctp_assoc_set_primary(struct sctp_association *, struct sctp_transport *); void sctp_assoc_del_nonprimary_peers(struct sctp_association *, diff --git a/include/net/tcp.h b/include/net/tcp.h index 057f0168462e..025c31aa65df 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1226,7 +1226,7 @@ extern void tcp_put_md5sig_pool(void); extern int tcp_md5_hash_header(struct tcp_md5sig_pool *, const struct tcphdr *); extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *, - unsigned header_len); + unsigned int header_len); extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key); diff --git a/include/net/udp.h b/include/net/udp.h index 9671f5f1df9c..e66fc297b105 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -81,7 +81,7 @@ struct udp_table { extern struct udp_table udp_table; extern void udp_table_init(struct udp_table *, const char *); static inline struct udp_hslot *udp_hashslot(struct udp_table *table, - struct net *net, unsigned num) + struct net *net, unsigned int num) { return &table->hash[udp_hashfn(net, num, table->mask)]; } diff --git a/include/net/wimax.h b/include/net/wimax.h index 322ff4fbdb4a..bbb74f990cab 100644 --- a/include/net/wimax.h +++ b/include/net/wimax.h @@ -423,8 +423,8 @@ struct wimax_dev { int (*op_reset)(struct wimax_dev *wimax_dev); struct rfkill *rfkill; - unsigned rf_hw; - unsigned rf_sw; + unsigned int rf_hw; + unsigned int rf_sw; char name[32]; struct dentry *debugfs_dentry; diff --git a/include/net/x25.h b/include/net/x25.h index a06119a05129..b4a8a8923128 100644 --- a/include/net/x25.h +++ b/include/net/x25.h @@ -305,7 +305,7 @@ static inline void x25_unregister_sysctl(void) {}; #endif /* CONFIG_SYSCTL */ struct x25_skb_cb { - unsigned flags; + unsigned int flags; }; #define X25_SKB_CB(s) ((struct x25_skb_cb *) ((s)->cb)) diff --git a/net/802/fc.c b/net/802/fc.c index b324e31401a9..05eea6b98bb8 100644 --- a/net/802/fc.c +++ b/net/802/fc.c @@ -35,7 +35,7 @@ static int fc_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, - const void *daddr, const void *saddr, unsigned len) + const void *daddr, const void *saddr, unsigned int len) { struct fch_hdr *fch; int hdr_len; diff --git a/net/802/fddi.c b/net/802/fddi.c index 5ab25cd4314b..9cda40661e0d 100644 --- a/net/802/fddi.c +++ b/net/802/fddi.c @@ -51,7 +51,7 @@ static int fddi_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, - const void *daddr, const void *saddr, unsigned len) + const void *daddr, const void *saddr, unsigned int len) { int hl = FDDI_K_SNAP_HLEN; struct fddihdr *fddi; diff --git a/net/802/hippi.c b/net/802/hippi.c index 056794e66375..51a1f530417d 100644 --- a/net/802/hippi.c +++ b/net/802/hippi.c @@ -45,7 +45,7 @@ static int hippi_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, - const void *daddr, const void *saddr, unsigned len) + const void *daddr, const void *saddr, unsigned int len) { struct hippi_hdr *hip = (struct hippi_hdr *)skb_push(skb, HIPPI_HLEN); struct hippi_cb *hcb = (struct hippi_cb *) skb->cb; diff --git a/net/802/tr.c b/net/802/tr.c index b9a3a145e348..e65f0b8de0c4 100644 --- a/net/802/tr.c +++ b/net/802/tr.c @@ -101,7 +101,7 @@ static inline unsigned long rif_hash(const unsigned char *addr) static int tr_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, - const void *daddr, const void *saddr, unsigned len) + const void *daddr, const void *saddr, unsigned int len) { struct trh_hdr *trh; int hdr_len; @@ -193,7 +193,7 @@ __be16 tr_type_trans(struct sk_buff *skb, struct net_device *dev) struct trh_hdr *trh; struct trllc *trllc; - unsigned riflen=0; + unsigned int riflen=0; skb->dev = dev; skb_reset_mac_header(skb); diff --git a/net/9p/client.c b/net/9p/client.c index b23a17c431c8..a170893d70e0 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1530,7 +1530,7 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, p9_debug(P9_DEBUG_9P, ">>> TREAD fid %d offset %llu %d\n", - fid->fid, (long long unsigned) offset, count); + fid->fid, (unsigned long long) offset, count); err = 0; clnt = fid->clnt; @@ -1605,7 +1605,7 @@ p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, struct p9_req_t *req; p9_debug(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %d\n", - fid->fid, (long long unsigned) offset, count); + fid->fid, (unsigned long long) offset, count); err = 0; clnt = fid->clnt; @@ -2040,7 +2040,7 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) char *dataptr; p9_debug(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n", - fid->fid, (long long unsigned) offset, count); + fid->fid, (unsigned long long) offset, count); err = 0; clnt = fid->clnt; diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index fccae26fa674..6449bae15702 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -513,7 +513,7 @@ error: clear_bit(Wworksched, &m->wsched); } -static int p9_pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key) +static int p9_pollwake(wait_queue_t *wait, unsigned int mode, int sync, void *key) { struct p9_poll_wait *pwait = container_of(wait, struct p9_poll_wait, wait); diff --git a/net/atm/br2684.c b/net/atm/br2684.c index 353fccf1cde3..4819d31533e0 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -73,7 +73,7 @@ struct br2684_vcc { #ifdef CONFIG_ATM_BR2684_IPFILTER struct br2684_filter filter; #endif /* CONFIG_ATM_BR2684_IPFILTER */ - unsigned copies_needed, copies_failed; + unsigned int copies_needed, copies_failed; }; struct br2684_dev { diff --git a/net/atm/mpoa_proc.c b/net/atm/mpoa_proc.c index 53e500292271..5bdd300db0f7 100644 --- a/net/atm/mpoa_proc.c +++ b/net/atm/mpoa_proc.c @@ -207,7 +207,7 @@ static ssize_t proc_mpc_write(struct file *file, const char __user *buff, size_t nbytes, loff_t *ppos) { char *page, *p; - unsigned len; + unsigned int len; if (nbytes == 0) return 0; diff --git a/net/atm/signaling.c b/net/atm/signaling.c index 509c8ac02b63..86767ca908a3 100644 --- a/net/atm/signaling.c +++ b/net/atm/signaling.c @@ -166,7 +166,7 @@ void sigd_enq2(struct atm_vcc *vcc, enum atmsvc_msg_type type, { struct sk_buff *skb; struct atmsvc_msg *msg; - static unsigned session = 0; + static unsigned int session = 0; pr_debug("%d (0x%p)\n", (int)type, vcc); while (!(skb = alloc_skb(sizeof(struct atmsvc_msg), GFP_KERNEL))) diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index 846ae4e2b115..67de6b33f2c3 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -48,7 +48,7 @@ int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, - const void *saddr, unsigned len) + const void *saddr, unsigned int len) { unsigned char *buff; @@ -219,7 +219,7 @@ put: int ax25_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, - const void *saddr, unsigned len) + const void *saddr, unsigned int len) { return -AX25_HEADER_LEN; } diff --git a/net/batman-adv/bat_sysfs.c b/net/batman-adv/bat_sysfs.c index c6efd687ca75..2c816883ca13 100644 --- a/net/batman-adv/bat_sysfs.c +++ b/net/batman-adv/bat_sysfs.c @@ -149,7 +149,7 @@ static int store_bool_attr(char *buff, size_t count, atomic_read(attr) == 1 ? "enabled" : "disabled", enabled == 1 ? "enabled" : "disabled"); - atomic_set(attr, (unsigned)enabled); + atomic_set(attr, (unsigned int)enabled); return count; } @@ -268,7 +268,7 @@ static ssize_t store_vis_mode(struct kobject *kobj, struct attribute *attr, "client" : "server", vis_mode_tmp == VIS_TYPE_CLIENT_UPDATE ? "client" : "server"); - atomic_set(&bat_priv->vis_mode, (unsigned)vis_mode_tmp); + atomic_set(&bat_priv->vis_mode, (unsigned int)vis_mode_tmp); return count; } @@ -354,7 +354,7 @@ static ssize_t store_gw_mode(struct kobject *kobj, struct attribute *attr, curr_gw_mode_str, buff); gw_deselect(bat_priv); - atomic_set(&bat_priv->gw_mode, (unsigned)gw_mode_tmp); + atomic_set(&bat_priv->gw_mode, (unsigned int)gw_mode_tmp); return count; } diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 61f65344e711..aab8470707c4 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -34,7 +34,7 @@ static inline int should_deliver(const struct net_bridge_port *p, p->state == BR_STATE_FORWARDING); } -static inline unsigned packet_length(const struct sk_buff *skb) +static inline unsigned int packet_length(const struct sk_buff *skb) { return skb->len - (skb->protocol == htons(ETH_P_8021Q) ? VLAN_HLEN : 0); } diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 27ca25ed7021..020e463f2225 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -512,8 +512,8 @@ static struct net_bridge_mdb_entry *br_multicast_get_group( struct net_bridge_mdb_htable *mdb; struct net_bridge_mdb_entry *mp; struct hlist_node *p; - unsigned count = 0; - unsigned max; + unsigned int count = 0; + unsigned int max; int elasticity; int err; @@ -1281,8 +1281,8 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, struct sk_buff *skb2 = skb; const struct iphdr *iph; struct igmphdr *ih; - unsigned len; - unsigned offset; + unsigned int len; + unsigned int offset; int err; /* We treat OOM as packet loss for now. */ @@ -1382,7 +1382,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, u8 icmp6_type; u8 nexthdr; __be16 frag_off; - unsigned len; + unsigned int len; int offset; int err; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 346b368d8698..df38108f6973 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -88,7 +88,7 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port) int err = -ENOBUFS; br_debug(port->br, "port %u(%s) event %d\n", - (unsigned)port->port_no, port->dev->name, event); + (unsigned int)port->port_no, port->dev->name, event); skb = nlmsg_new(br_nlmsg_size(), GFP_ATOMIC); if (skb == NULL) diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h index 05ed9bc7e426..0c0fe36e7aa9 100644 --- a/net/bridge/br_private_stp.h +++ b/net/bridge/br_private_stp.h @@ -29,10 +29,9 @@ #define BR_MIN_PATH_COST 1 #define BR_MAX_PATH_COST 65535 -struct br_config_bpdu -{ - unsigned topology_change:1; - unsigned topology_change_ack:1; +struct br_config_bpdu { + unsigned int topology_change:1; + unsigned int topology_change_ack:1; bridge_id root; int root_path_cost; bridge_id bridge_id; diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 8c836d96ba76..af9a12099ba4 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -32,7 +32,7 @@ static const char *const br_port_state_names[] = { void br_log_state(const struct net_bridge_port *p) { br_info(p->br, "port %u(%s) entered %s state\n", - (unsigned) p->port_no, p->dev->name, + (unsigned int) p->port_no, p->dev->name, br_port_state_names[p->state]); } @@ -478,7 +478,7 @@ void br_received_tcn_bpdu(struct net_bridge_port *p) { if (br_is_designated_port(p)) { br_info(p->br, "port %u(%s) received tcn bpdu\n", - (unsigned) p->port_no, p->dev->name); + (unsigned int) p->port_no, p->dev->name); br_topology_change_detection(p->br); br_topology_change_acknowledge(p); diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c index 58de2a0f9975..a6747e673426 100644 --- a/net/bridge/br_stp_timer.c +++ b/net/bridge/br_stp_timer.c @@ -56,7 +56,7 @@ static void br_message_age_timer_expired(unsigned long arg) return; br_info(br, "port %u(%s) neighbor %.2x%.2x.%pM lost\n", - (unsigned) p->port_no, p->dev->name, + (unsigned int) p->port_no, p->dev->name, id->prio[0], id->prio[1], &id->addr); /* @@ -84,7 +84,7 @@ static void br_forward_delay_timer_expired(unsigned long arg) struct net_bridge *br = p->br; br_debug(br, "port %u(%s) forward delay timer\n", - (unsigned) p->port_no, p->dev->name); + (unsigned int) p->port_no, p->dev->name); spin_lock(&br->lock); if (p->state == BR_STATE_LISTENING) { p->state = BR_STATE_LEARNING; @@ -131,7 +131,7 @@ static void br_hold_timer_expired(unsigned long arg) struct net_bridge_port *p = (struct net_bridge_port *) arg; br_debug(p->br, "port %u(%s) hold timer expired\n", - (unsigned) p->port_no, p->dev->name); + (unsigned int) p->port_no, p->dev->name); spin_lock(&p->br->lock); if (p->config_pending) diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index c236c0e43984..766fd7fb0295 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -297,7 +297,7 @@ static ssize_t store_group_addr(struct device *d, const char *buf, size_t len) { struct net_bridge *br = to_bridge(d); - unsigned new_addr[6]; + unsigned int new_addr[6]; int i; if (!capable(CAP_NET_ADMIN)) diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index ce47ee9f48c8..0dccdb3c7d26 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -130,7 +130,7 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= - (unsigned)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) { + (unsigned int)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) { if (net_ratelimit()) pr_debug("sending flow OFF (queue len = %d %d)\n", atomic_read(&cf_sk->sk.sk_rmem_alloc), diff --git a/net/ceph/auth_x.h b/net/ceph/auth_x.h index e02da7a5c5a1..f459e93b774f 100644 --- a/net/ceph/auth_x.h +++ b/net/ceph/auth_x.h @@ -13,7 +13,7 @@ */ struct ceph_x_ticket_handler { struct rb_node node; - unsigned service; + unsigned int service; struct ceph_crypto_key session_key; struct ceph_timespec validity; @@ -27,7 +27,7 @@ struct ceph_x_ticket_handler { struct ceph_x_authorizer { struct ceph_buffer *buf; - unsigned service; + unsigned int service; u64 nonce; char reply_buf[128]; /* big enough for encrypted blob */ }; @@ -38,7 +38,7 @@ struct ceph_x_info { bool starting; u64 server_challenge; - unsigned have_keys; + unsigned int have_keys; struct rb_root ticket_handlers; struct ceph_x_authorizer auth_authorizer; diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index cc913193d992..a776f751edbf 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -441,8 +441,8 @@ EXPORT_SYMBOL(ceph_client_id); * create a fresh client instance */ struct ceph_client *ceph_create_client(struct ceph_options *opt, void *private, - unsigned supported_features, - unsigned required_features) + unsigned int supported_features, + unsigned int required_features) { struct ceph_client *client; struct ceph_entity_addr *myaddr = NULL; diff --git a/net/ceph/ceph_hash.c b/net/ceph/ceph_hash.c index 0a1b53bce76d..67bb1f11e613 100644 --- a/net/ceph/ceph_hash.c +++ b/net/ceph/ceph_hash.c @@ -20,7 +20,7 @@ c = c - a; c = c - b; c = c ^ (b >> 15); \ } while (0) -unsigned ceph_str_hash_rjenkins(const char *str, unsigned length) +unsigned int ceph_str_hash_rjenkins(const char *str, unsigned int length) { const unsigned char *k = (const unsigned char *)str; __u32 a, b, c; /* the internal state */ @@ -81,7 +81,7 @@ unsigned ceph_str_hash_rjenkins(const char *str, unsigned length) /* * linux dcache hash */ -unsigned ceph_str_hash_linux(const char *str, unsigned length) +unsigned int ceph_str_hash_linux(const char *str, unsigned int length) { unsigned long hash = 0; unsigned char c; @@ -94,7 +94,7 @@ unsigned ceph_str_hash_linux(const char *str, unsigned length) } -unsigned ceph_str_hash(int type, const char *s, unsigned len) +unsigned int ceph_str_hash(int type, const char *s, unsigned int len) { switch (type) { case CEPH_STR_HASH_LINUX: diff --git a/net/ceph/crush/mapper.c b/net/ceph/crush/mapper.c index b79747c4b645..854ac53f56cd 100644 --- a/net/ceph/crush/mapper.c +++ b/net/ceph/crush/mapper.c @@ -68,8 +68,8 @@ int crush_find_rule(struct crush_map *map, int ruleset, int type, int size) static int bucket_perm_choose(struct crush_bucket *bucket, int x, int r) { - unsigned pr = r % bucket->size; - unsigned i, s; + unsigned int pr = r % bucket->size; + unsigned int i, s; /* start a new permutation if @x has changed */ if (bucket->perm_x != x || bucket->perm_n == 0) { @@ -100,13 +100,13 @@ static int bucket_perm_choose(struct crush_bucket *bucket, for (i = 0; i < bucket->perm_n; i++) dprintk(" perm_choose have %d: %d\n", i, bucket->perm[i]); while (bucket->perm_n <= pr) { - unsigned p = bucket->perm_n; + unsigned int p = bucket->perm_n; /* no point in swapping the final entry */ if (p < bucket->size - 1) { i = crush_hash32_3(bucket->hash, x, bucket->id, p) % (bucket->size - p); if (i) { - unsigned t = bucket->perm[p + i]; + unsigned int t = bucket->perm[p + i]; bucket->perm[p + i] = bucket->perm[p]; bucket->perm[p] = t; } diff --git a/net/ceph/debugfs.c b/net/ceph/debugfs.c index 27d4ea315d12..54b531a01121 100644 --- a/net/ceph/debugfs.c +++ b/net/ceph/debugfs.c @@ -94,9 +94,9 @@ static int monc_show(struct seq_file *s, void *p) mutex_lock(&monc->mutex); if (monc->have_mdsmap) - seq_printf(s, "have mdsmap %u\n", (unsigned)monc->have_mdsmap); + seq_printf(s, "have mdsmap %u\n", (unsigned int)monc->have_mdsmap); if (monc->have_osdmap) - seq_printf(s, "have osdmap %u\n", (unsigned)monc->have_osdmap); + seq_printf(s, "have osdmap %u\n", (unsigned int)monc->have_osdmap); if (monc->want_next_osdmap) seq_printf(s, "want next osdmap\n"); @@ -146,7 +146,7 @@ static int osdc_show(struct seq_file *s, void *pp) if (req->r_reassert_version.epoch) seq_printf(s, "\t%u'%llu", - (unsigned)le32_to_cpu(req->r_reassert_version.epoch), + (unsigned int)le32_to_cpu(req->r_reassert_version.epoch), le64_to_cpu(req->r_reassert_version.version)); else seq_printf(s, "\t"); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index f0993af2ae4d..36fa6bf68498 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -699,7 +699,7 @@ static int prepare_write_connect(struct ceph_messenger *msgr, struct ceph_connection *con, int include_banner) { - unsigned global_seq = get_global_seq(con->msgr, 0); + unsigned int global_seq = get_global_seq(con->msgr, 0); int proto; switch (con->peer_name.type) { @@ -816,7 +816,7 @@ static void iter_bio_next(struct bio **bio_iter, int *seg) static int write_partial_msg_pages(struct ceph_connection *con) { struct ceph_msg *msg = con->out_msg; - unsigned data_len = le32_to_cpu(msg->hdr.data_len); + unsigned int data_len = le32_to_cpu(msg->hdr.data_len); size_t len; bool do_datacrc = !con->msgr->nocrc; int ret; @@ -1554,7 +1554,7 @@ static struct ceph_msg *ceph_alloc_msg(struct ceph_connection *con, static int read_partial_message_pages(struct ceph_connection *con, struct page **pages, - unsigned data_len, bool do_datacrc) + unsigned int data_len, bool do_datacrc) { void *p; int ret; @@ -1587,7 +1587,7 @@ static int read_partial_message_pages(struct ceph_connection *con, #ifdef CONFIG_BLOCK static int read_partial_message_bio(struct ceph_connection *con, struct bio **bio_iter, int *bio_seg, - unsigned data_len, bool do_datacrc) + unsigned int data_len, bool do_datacrc) { struct bio_vec *bv = bio_iovec_idx(*bio_iter, *bio_seg); void *p; @@ -1629,7 +1629,7 @@ static int read_partial_message(struct ceph_connection *con) struct ceph_msg *m = con->in_msg; int ret; int to, left; - unsigned front_len, middle_len, data_len; + unsigned int front_len, middle_len, data_len; bool do_datacrc = !con->msgr->nocrc; int skip; u64 seq; @@ -2345,9 +2345,9 @@ void ceph_con_revoke_message(struct ceph_connection *con, struct ceph_msg *msg) { mutex_lock(&con->mutex); if (con->in_msg && con->in_msg == msg) { - unsigned front_len = le32_to_cpu(con->in_hdr.front_len); - unsigned middle_len = le32_to_cpu(con->in_hdr.middle_len); - unsigned data_len = le32_to_cpu(con->in_hdr.data_len); + unsigned int front_len = le32_to_cpu(con->in_hdr.front_len); + unsigned int middle_len = le32_to_cpu(con->in_hdr.middle_len); + unsigned int data_len = le32_to_cpu(con->in_hdr.data_len); /* skip rest of message */ dout("con_revoke_pages %p msg %p revoked\n", con, msg); diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 1845cde26227..10d6008d31f2 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -168,7 +168,7 @@ static bool __sub_expired(struct ceph_mon_client *monc) */ static void __schedule_delayed(struct ceph_mon_client *monc) { - unsigned delay; + unsigned int delay; if (monc->cur_mon < 0 || __sub_expired(monc)) delay = 10 * HZ; @@ -184,7 +184,7 @@ static void __schedule_delayed(struct ceph_mon_client *monc) static void __send_subscribe(struct ceph_mon_client *monc) { dout("__send_subscribe sub_sent=%u exp=%u want_osd=%d\n", - (unsigned)monc->sub_sent, __sub_expired(monc), + (unsigned int)monc->sub_sent, __sub_expired(monc), monc->want_next_osdmap); if ((__sub_expired(monc) && !monc->sub_sent) || monc->want_next_osdmap == 1) { @@ -201,7 +201,7 @@ static void __send_subscribe(struct ceph_mon_client *monc) if (monc->want_next_osdmap) { dout("__send_subscribe to 'osdmap' %u\n", - (unsigned)monc->have_osdmap); + (unsigned int)monc->have_osdmap); ceph_encode_string(&p, end, "osdmap", 6); i = p; i->have = cpu_to_le64(monc->have_osdmap); @@ -211,7 +211,7 @@ static void __send_subscribe(struct ceph_mon_client *monc) } if (monc->want_mdsmap) { dout("__send_subscribe to 'mdsmap' %u+\n", - (unsigned)monc->have_mdsmap); + (unsigned int)monc->have_mdsmap); ceph_encode_string(&p, end, "mdsmap", 6); i = p; i->have = cpu_to_le64(monc->have_mdsmap); @@ -236,7 +236,7 @@ static void __send_subscribe(struct ceph_mon_client *monc) static void handle_subscribe_ack(struct ceph_mon_client *monc, struct ceph_msg *msg) { - unsigned seconds; + unsigned int seconds; struct ceph_mon_subscribe_ack *h = msg->front.iov_base; if (msg->front.iov_len < sizeof(*h)) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 5e254055c910..1b0ef3c4d393 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1214,7 +1214,7 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg, } if (!req->r_got_reply) { - unsigned bytes; + unsigned int bytes; req->r_result = le32_to_cpu(rhead->result); bytes = le32_to_cpu(msg->hdr.data_len); diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 29ad46ec9dcf..56e561a69004 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -38,7 +38,7 @@ done: /* maps */ -static int calc_bits_of(unsigned t) +static int calc_bits_of(unsigned int t) { int b = 0; while (t) { @@ -154,7 +154,7 @@ static struct crush_map *crush_decode(void *pbyval, void *end) magic = ceph_decode_32(p); if (magic != CRUSH_MAGIC) { pr_err("crush_decode magic %x != current %x\n", - (unsigned)magic, (unsigned)CRUSH_MAGIC); + (unsigned int)magic, (unsigned int)CRUSH_MAGIC); goto bad; } c->max_buckets = ceph_decode_32(p); @@ -460,7 +460,7 @@ static void __remove_pg_pool(struct rb_root *root, struct ceph_pg_pool_info *pi) static int __decode_pool(void **p, void *end, struct ceph_pg_pool_info *pi) { - unsigned n, m; + unsigned int n, m; ceph_decode_copy(p, &pi->v, sizeof(pi->v)); calc_pg_masks(pi); @@ -970,7 +970,7 @@ void ceph_calc_file_object_mapping(struct ceph_file_layout *layout, objsetno = stripeno / su_per_object; *ono = objsetno * sc + stripepos; - dout("objset %u * sc %u = ono %u\n", objsetno, sc, (unsigned)*ono); + dout("objset %u * sc %u = ono %u\n", objsetno, sc, (unsigned int)*ono); /* *oxoff = *off % layout->fl_stripe_unit; # offset in su */ t = off; @@ -998,12 +998,12 @@ int ceph_calc_object_layout(struct ceph_object_layout *ol, struct ceph_file_layout *fl, struct ceph_osdmap *osdmap) { - unsigned num, num_mask; + unsigned int num, num_mask; struct ceph_pg pgid; s32 preferred = (s32)le32_to_cpu(fl->fl_pg_preferred); int poolid = le32_to_cpu(fl->fl_pg_pool); struct ceph_pg_pool_info *pool; - unsigned ps; + unsigned int ps; BUG_ON(!osdmap); @@ -1045,7 +1045,7 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid, struct ceph_pg_mapping *pg; struct ceph_pg_pool_info *pool; int ruleno; - unsigned poolid, ps, pps, t; + unsigned int poolid, ps, pps, t; int preferred; poolid = le32_to_cpu(pgid.pool); diff --git a/net/compat.c b/net/compat.c index e055708b8ec9..e240441a2317 100644 --- a/net/compat.c +++ b/net/compat.c @@ -741,13 +741,13 @@ static unsigned char nas[21] = { }; #undef AL -asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags) +asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned int flags) { return sys_sendmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } asmlinkage long compat_sys_sendmmsg(int fd, struct compat_mmsghdr __user *mmsg, - unsigned vlen, unsigned int flags) + unsigned int vlen, unsigned int flags) { return __sys_sendmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT); @@ -758,20 +758,20 @@ asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, uns return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } -asmlinkage long compat_sys_recv(int fd, void __user *buf, size_t len, unsigned flags) +asmlinkage long compat_sys_recv(int fd, void __user *buf, size_t len, unsigned int flags) { return sys_recv(fd, buf, len, flags | MSG_CMSG_COMPAT); } asmlinkage long compat_sys_recvfrom(int fd, void __user *buf, size_t len, - unsigned flags, struct sockaddr __user *addr, + unsigned int flags, struct sockaddr __user *addr, int __user *addrlen) { return sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr, addrlen); } asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, - unsigned vlen, unsigned int flags, + unsigned int vlen, unsigned int flags, struct compat_timespec __user *timeout) { int datagrams; diff --git a/net/core/datagram.c b/net/core/datagram.c index e4fbfd6e2bd4..ae6acf6a3dea 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -65,7 +65,7 @@ static inline int connection_based(struct sock *sk) return sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM; } -static int receiver_wake_function(wait_queue_t *wait, unsigned mode, int sync, +static int receiver_wake_function(wait_queue_t *wait, unsigned int mode, int sync, void *key) { unsigned long bits = (unsigned long)key; @@ -158,7 +158,7 @@ out_noerr: * quite explicitly by POSIX 1003.1g, don't change them without having * the standard around please. */ -struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, +struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, int *peeked, int *off, int *err) { struct sk_buff *skb; @@ -216,7 +216,7 @@ no_packet: } EXPORT_SYMBOL(__skb_recv_datagram); -struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, +struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned int flags, int noblock, int *err) { int peeked, off = 0; diff --git a/net/core/dev.c b/net/core/dev.c index 9bb8f87c4cda..c93812733f1d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -208,7 +208,8 @@ static inline void dev_base_seq_inc(struct net *net) static inline struct hlist_head *dev_name_hash(struct net *net, const char *name) { - unsigned hash = full_name_hash(name, strnlen(name, IFNAMSIZ)); + unsigned int hash = full_name_hash(name, strnlen(name, IFNAMSIZ)); + return &net->dev_name_head[hash_32(hash, NETDEV_HASHBITS)]; } @@ -4618,9 +4619,9 @@ void dev_set_rx_mode(struct net_device *dev) * * Get the combination of flag bits exported through APIs to userspace. */ -unsigned dev_get_flags(const struct net_device *dev) +unsigned int dev_get_flags(const struct net_device *dev) { - unsigned flags; + unsigned int flags; flags = (dev->flags & ~(IFF_PROMISC | IFF_ALLMULTI | diff --git a/net/core/filter.c b/net/core/filter.c index 95d05a6012d1..47a5f055e7f3 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -531,7 +531,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) * Compare this with conditional jumps below, * where offsets are limited. --ANK (981016) */ - if (ftest->k >= (unsigned)(flen-pc-1)) + if (ftest->k >= (unsigned int)(flen-pc-1)) return -EINVAL; break; case BPF_S_JMP_JEQ_K: diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 2d3e6fcdc9c9..434eed8c6185 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -890,7 +890,7 @@ static void neigh_timer_handler(unsigned long arg) { unsigned long now, next; struct neighbour *neigh = (struct neighbour *)arg; - unsigned state; + unsigned int state; int notify = 0; write_lock(&neigh->lock); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 97d0f2453a0e..fdf9e61d0651 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -231,7 +231,7 @@ NETDEVICE_SHOW(flags, fmt_hex); static int change_flags(struct net_device *net, unsigned long new_flags) { - return dev_change_flags(net, (unsigned) new_flags); + return dev_change_flags(net, (unsigned int) new_flags); } static ssize_t store_flags(struct device *dev, struct device_attribute *attr, @@ -581,7 +581,7 @@ static ssize_t store_rps_map(struct netdev_rx_queue *queue, return err; } - map = kzalloc(max_t(unsigned, + map = kzalloc(max_t(unsigned int, RPS_MAP_SIZE(cpumask_weight(mask)), L1_CACHE_BYTES), GFP_KERNEL); if (!map) { @@ -902,7 +902,7 @@ static ssize_t bql_set_hold_time(struct netdev_queue *queue, const char *buf, size_t len) { struct dql *dql = &queue->dql; - unsigned value; + unsigned int value; int err; err = kstrtouint(buf, 10, &value); @@ -1106,7 +1106,7 @@ static ssize_t store_xps_map(struct netdev_queue *queue, return err; } - new_dev_maps = kzalloc(max_t(unsigned, + new_dev_maps = kzalloc(max_t(unsigned int, XPS_DEV_MAPS_SIZE, L1_CACHE_BYTES), GFP_KERNEL); if (!new_dev_maps) { free_cpumask_var(mask); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 4d8ce93cd503..ffb5d382f241 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -320,7 +320,7 @@ struct pktgen_dev { (see RFC 3260, sec. 4) */ /* MPLS */ - unsigned nr_labels; /* Depth of stack, 0 = no MPLS */ + unsigned int nr_labels; /* Depth of stack, 0 = no MPLS */ __be32 labels[MAX_MPLS_LABELS]; /* VLAN/SVLAN (802.1Q/Q-in-Q) */ @@ -373,10 +373,10 @@ struct pktgen_dev { */ char odevname[32]; struct flow_state *flows; - unsigned cflows; /* Concurrent flows (config) */ - unsigned lflow; /* Flow length (config) */ - unsigned nflows; /* accumulated flows (stats) */ - unsigned curfl; /* current sequenced flow (state)*/ + unsigned int cflows; /* Concurrent flows (config) */ + unsigned int lflow; /* Flow length (config) */ + unsigned int nflows; /* accumulated flows (stats) */ + unsigned int curfl; /* current sequenced flow (state)*/ u16 queue_map_min; u16 queue_map_max; @@ -592,7 +592,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v) pkt_dev->src_mac_count, pkt_dev->dst_mac_count); if (pkt_dev->nr_labels) { - unsigned i; + unsigned int i; seq_printf(seq, " mpls: "); for (i = 0; i < pkt_dev->nr_labels; i++) seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]), @@ -812,7 +812,7 @@ done_str: static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev) { - unsigned n = 0; + unsigned int n = 0; char c; ssize_t i = 0; int len; @@ -1510,7 +1510,7 @@ static ssize_t pktgen_if_write(struct file *file, } if (!strcmp(name, "mpls")) { - unsigned n, cnt; + unsigned int n, cnt; len = get_labels(&user_buffer[i], pkt_dev); if (len < 0) @@ -2324,7 +2324,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev) } if (pkt_dev->flags & F_MPLS_RND) { - unsigned i; + unsigned int i; for (i = 0; i < pkt_dev->nr_labels; i++) if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM) pkt_dev->labels[i] = MPLS_STACK_BOTTOM | @@ -2550,7 +2550,7 @@ err: static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev) { - unsigned i; + unsigned int i; for (i = 0; i < pkt_dev->nr_labels; i++) *mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 4a0d8cfff2a0..2ff6fe4bada4 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -552,7 +552,7 @@ void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data } EXPORT_SYMBOL(__rta_fill); -int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group, int echo) +int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group, int echo) { struct sock *rtnl = net->rtnl; int err = 0; @@ -1953,7 +1953,7 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } -void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change) +void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change) { struct net *net = dev_net(dev); struct sk_buff *skb; @@ -2048,7 +2048,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) struct rtattr *attr = (void *)nlh + NLMSG_ALIGN(min_len); while (RTA_OK(attr, attrlen)) { - unsigned flavor = attr->rta_type; + unsigned int flavor = attr->rta_type; if (flavor) { if (flavor > rta_max[sz_idx]) return -EINVAL; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 35b3a685e342..8f0d68d14360 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3166,7 +3166,7 @@ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb) int len = skb->len; if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= - (unsigned)sk->sk_rcvbuf) + (unsigned int)sk->sk_rcvbuf) return -ENOMEM; skb_orphan(skb); diff --git a/net/core/sock.c b/net/core/sock.c index b2e14c07d920..c7e60eac639b 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1534,7 +1534,7 @@ struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, */ void *sock_kmalloc(struct sock *sk, int size, gfp_t priority) { - if ((unsigned)size <= sysctl_optmem_max && + if ((unsigned int)size <= sysctl_optmem_max && atomic_read(&sk->sk_omem_alloc) + size < sysctl_optmem_max) { void *mem; /* First do the add, to avoid the race if kmalloc diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c index 70bfaf2d1965..8c67bedf85b0 100644 --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -100,7 +100,7 @@ static void ccid3_update_send_interval(struct ccid3_hc_tx_sock *hc) DCCP_BUG_ON(hc->tx_t_ipi == 0); ccid3_pr_debug("t_ipi=%u, s=%u, X=%u\n", hc->tx_t_ipi, - hc->tx_s, (unsigned)(hc->tx_x >> 6)); + hc->tx_s, (unsigned int)(hc->tx_x >> 6)); } static u32 ccid3_hc_tx_idle_rtt(struct ccid3_hc_tx_sock *hc, ktime_t now) @@ -153,9 +153,9 @@ static void ccid3_hc_tx_update_x(struct sock *sk, ktime_t *stamp) if (hc->tx_x != old_x) { ccid3_pr_debug("X_prev=%u, X_now=%u, X_calc=%u, " - "X_recv=%u\n", (unsigned)(old_x >> 6), - (unsigned)(hc->tx_x >> 6), hc->tx_x_calc, - (unsigned)(hc->tx_x_recv >> 6)); + "X_recv=%u\n", (unsigned int)(old_x >> 6), + (unsigned int)(hc->tx_x >> 6), hc->tx_x_calc, + (unsigned int)(hc->tx_x_recv >> 6)); ccid3_update_send_interval(hc); } @@ -425,8 +425,8 @@ done_computing_x: "p=%u, X_calc=%u, X_recv=%u, X=%u\n", dccp_role(sk), sk, hc->tx_rtt, r_sample, hc->tx_s, hc->tx_p, hc->tx_x_calc, - (unsigned)(hc->tx_x_recv >> 6), - (unsigned)(hc->tx_x >> 6)); + (unsigned int)(hc->tx_x_recv >> 6), + (unsigned int)(hc->tx_x >> 6)); /* unschedule no feedback timer */ sk_stop_timer(sk, &hc->tx_no_feedback_timer); diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 29d6bb629a6c..9040be049d8c 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -75,7 +75,7 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo); * state, about 60 seconds */ /* RFC 1122, 4.2.3.1 initial RTO value */ -#define DCCP_TIMEOUT_INIT ((unsigned)(3 * HZ)) +#define DCCP_TIMEOUT_INIT ((unsigned int)(3 * HZ)) /* * The maximum back-off value for retransmissions. This is needed for @@ -84,7 +84,7 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo); * - feature-negotiation retransmission (sec. 6.6.3), * - Acks in client-PARTOPEN state (sec. 8.1.5). */ -#define DCCP_RTO_MAX ((unsigned)(64 * HZ)) +#define DCCP_RTO_MAX ((unsigned int)(64 * HZ)) /* * RTT sampling: sanity bounds and fallback RTT value from RFC 4340, section 3.4 @@ -287,9 +287,9 @@ extern struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, extern int dccp_child_process(struct sock *parent, struct sock *child, struct sk_buff *skb); extern int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - struct dccp_hdr *dh, unsigned len); + struct dccp_hdr *dh, unsigned int len); extern int dccp_rcv_established(struct sock *sk, struct sk_buff *skb, - const struct dccp_hdr *dh, const unsigned len); + const struct dccp_hdr *dh, const unsigned int len); extern int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized); extern void dccp_destroy_sock(struct sock *sk); diff --git a/net/dccp/input.c b/net/dccp/input.c index 51d5fe5fffba..bc93a333931e 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -285,7 +285,7 @@ static int dccp_check_seqno(struct sock *sk, struct sk_buff *skb) } static int __dccp_rcv_established(struct sock *sk, struct sk_buff *skb, - const struct dccp_hdr *dh, const unsigned len) + const struct dccp_hdr *dh, const unsigned int len) { struct dccp_sock *dp = dccp_sk(sk); @@ -366,7 +366,7 @@ discard: } int dccp_rcv_established(struct sock *sk, struct sk_buff *skb, - const struct dccp_hdr *dh, const unsigned len) + const struct dccp_hdr *dh, const unsigned int len) { if (dccp_check_seqno(sk, skb)) goto discard; @@ -388,7 +388,7 @@ EXPORT_SYMBOL_GPL(dccp_rcv_established); static int dccp_rcv_request_sent_state_process(struct sock *sk, struct sk_buff *skb, const struct dccp_hdr *dh, - const unsigned len) + const unsigned int len) { /* * Step 4: Prepare sequence numbers in REQUEST @@ -521,7 +521,7 @@ unable_to_proceed: static int dccp_rcv_respond_partopen_state_process(struct sock *sk, struct sk_buff *skb, const struct dccp_hdr *dh, - const unsigned len) + const unsigned int len) { struct dccp_sock *dp = dccp_sk(sk); u32 sample = dp->dccps_options_received.dccpor_timestamp_echo; @@ -572,7 +572,7 @@ static int dccp_rcv_respond_partopen_state_process(struct sock *sk, } int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - struct dccp_hdr *dh, unsigned len) + struct dccp_hdr *dh, unsigned int len) { struct dccp_sock *dp = dccp_sk(sk); struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 4136987d94da..2ba1a2814c24 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -250,7 +250,7 @@ static void dn_unhash_sock_bh(struct sock *sk) static struct hlist_head *listen_hash(struct sockaddr_dn *addr) { int i; - unsigned hash = addr->sdn_objnum; + unsigned int hash = addr->sdn_objnum; if (hash == 0) { hash = addr->sdn_objnamel; @@ -1844,9 +1844,9 @@ static inline int dn_queue_too_long(struct dn_scp *scp, struct sk_buff_head *que * inclusion (or not) of the two 16 bit acknowledgement fields so it doesn't * make much practical difference. */ -unsigned dn_mss_from_pmtu(struct net_device *dev, int mtu) +unsigned int dn_mss_from_pmtu(struct net_device *dev, int mtu) { - unsigned mss = 230 - DN_MAX_NSP_DATA_HEADER; + unsigned int mss = 230 - DN_MAX_NSP_DATA_HEADER; if (dev) { struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr); mtu -= LL_RESERVED_SPACE(dev); diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index 9e885f180b60..65a8cd7891fe 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -302,11 +302,12 @@ struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta struct rtattr *attr = RTA_DATA(rta->rta_mx); while(RTA_OK(attr, attrlen)) { - unsigned flavour = attr->rta_type; + unsigned int flavour = attr->rta_type; + if (flavour) { if (flavour > RTAX_MAX) goto err_inval; - fi->fib_metrics[flavour-1] = *(unsigned*)RTA_DATA(attr); + fi->fib_metrics[flavour-1] = *(unsigned int *)RTA_DATA(attr); } attr = RTA_NEXT(attr, attrlen); } diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c index f6544b2c91b0..58084f37151e 100644 --- a/net/decnet/dn_nsp_in.c +++ b/net/decnet/dn_nsp_in.c @@ -588,7 +588,7 @@ static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig number of warnings when compiling with -W --ANK */ if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= - (unsigned)sk->sk_rcvbuf) { + (unsigned int)sk->sk_rcvbuf) { err = -ENOMEM; goto out; } diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c index e446e85e64a6..b952f88d9c1f 100644 --- a/net/decnet/dn_nsp_out.c +++ b/net/decnet/dn_nsp_out.c @@ -209,7 +209,7 @@ static void dn_nsp_rtt(struct sock *sk, long rtt) * * Returns: The number of times the packet has been sent previously */ -static inline unsigned dn_nsp_clone_and_send(struct sk_buff *skb, +static inline unsigned int dn_nsp_clone_and_send(struct sk_buff *skb, gfp_t gfp) { struct dn_skb_cb *cb = DN_SKB_CB(skb); @@ -240,7 +240,7 @@ void dn_nsp_output(struct sock *sk) { struct dn_scp *scp = DN_SK(sk); struct sk_buff *skb; - unsigned reduce_win = 0; + unsigned int reduce_win = 0; /* * First we check for otherdata/linkservice messages diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 80a3de4906d3..7e1f8788da19 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -122,7 +122,7 @@ static int dn_route_input(struct sk_buff *); static void dn_run_flush(unsigned long dummy); static struct dn_rt_hash_bucket *dn_rt_hash_table; -static unsigned dn_rt_hash_mask; +static unsigned int dn_rt_hash_mask; static struct timer_list dn_route_timer; static DEFINE_TIMER(dn_rt_flush_timer, dn_run_flush, 0, 0); @@ -149,13 +149,13 @@ static void dn_dst_destroy(struct dst_entry *dst) dst_destroy_metrics_generic(dst); } -static __inline__ unsigned dn_hash(__le16 src, __le16 dst) +static __inline__ unsigned int dn_hash(__le16 src, __le16 dst) { __u16 tmp = (__u16 __force)(src ^ dst); tmp ^= (tmp >> 3); tmp ^= (tmp >> 5); tmp ^= (tmp >> 10); - return dn_rt_hash_mask & (unsigned)tmp; + return dn_rt_hash_mask & (unsigned int)tmp; } static inline void dnrt_free(struct dn_route *rt) @@ -297,7 +297,7 @@ static inline int compare_keys(struct flowidn *fl1, struct flowidn *fl2) (fl1->flowidn_iif ^ fl2->flowidn_iif)) == 0; } -static int dn_insert_route(struct dn_route *rt, unsigned hash, struct dn_route **rp) +static int dn_insert_route(struct dn_route *rt, unsigned int hash, struct dn_route **rp) { struct dn_route *rth; struct dn_route __rcu **rthp; @@ -934,8 +934,8 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowidn *o struct dn_route *rt = NULL; struct net_device *dev_out = NULL, *dev; struct neighbour *neigh = NULL; - unsigned hash; - unsigned flags = 0; + unsigned int hash; + unsigned int flags = 0; struct dn_fib_res res = { .fi = NULL, .type = RTN_UNICAST }; int err; int free_res = 0; @@ -1209,7 +1209,7 @@ e_neighbour: */ static int __dn_route_output_key(struct dst_entry **pprt, const struct flowidn *flp, int flags) { - unsigned hash = dn_hash(flp->saddr, flp->daddr); + unsigned int hash = dn_hash(flp->saddr, flp->daddr); struct dn_route *rt = NULL; if (!(flags & MSG_TRYHARD)) { @@ -1275,7 +1275,7 @@ static int dn_route_input_slow(struct sk_buff *skb) struct net_device *out_dev = NULL; struct dn_dev *dn_db; struct neighbour *neigh = NULL; - unsigned hash; + unsigned int hash; int flags = 0; __le16 gateway = 0; __le16 local_src = 0; @@ -1490,7 +1490,7 @@ static int dn_route_input(struct sk_buff *skb) { struct dn_route *rt; struct dn_skb_cb *cb = DN_SKB_CB(skb); - unsigned hash = dn_hash(cb->src, cb->dst); + unsigned int hash = dn_hash(cb->src, cb->dst); if (skb_dst(skb)) return 0; diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c index 7399e3d51922..e65f2c856e06 100644 --- a/net/decnet/dn_rules.c +++ b/net/decnet/dn_rules.c @@ -177,11 +177,11 @@ static int dn_fib_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, return 1; } -unsigned dnet_addr_type(__le16 addr) +unsigned int dnet_addr_type(__le16 addr) { struct flowidn fld = { .daddr = addr }; struct dn_fib_res res; - unsigned ret = RTN_UNICAST; + unsigned int ret = RTN_UNICAST; struct dn_fib_table *tb = dn_fib_get_table(RT_TABLE_LOCAL, 0); res.r = NULL; diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index c73bba326d70..6f70ea935b0b 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -38,7 +38,7 @@ MODULE_DESCRIPTION("DNS Resolver"); MODULE_AUTHOR("Wang Lei"); MODULE_LICENSE("GPL"); -unsigned dns_resolver_debug; +unsigned int dns_resolver_debug; module_param_named(debug, dns_resolver_debug, uint, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(debug, "DNS Resolver debugging mask"); diff --git a/net/dns_resolver/internal.h b/net/dns_resolver/internal.h index 189ca9e9b785..17c7886b5b3a 100644 --- a/net/dns_resolver/internal.h +++ b/net/dns_resolver/internal.h @@ -31,7 +31,7 @@ extern const struct cred *dns_resolver_cache; /* * debug tracing */ -extern unsigned dns_resolver_debug; +extern unsigned int dns_resolver_debug; #define kdebug(FMT, ...) \ do { \ diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index bf10a311cf1c..5889a6c38a10 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -77,7 +77,7 @@ __setup("ether=", netdev_boot_setup); */ int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, - const void *daddr, const void *saddr, unsigned len) + const void *daddr, const void *saddr, unsigned int len) { struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN); diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 368515885368..58c8895716ff 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -371,7 +371,7 @@ err: static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *_daddr, - const void *_saddr, unsigned len) + const void *_saddr, unsigned int len) { u8 tmp, iphc0, iphc1, *hc06_ptr; struct ipv6hdr *hdr; diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 1b09eaabaac1..6fbb2ad7bb6d 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -44,8 +44,8 @@ struct dgram_sock { struct ieee802154_addr src_addr; struct ieee802154_addr dst_addr; - unsigned bound:1; - unsigned want_ack:1; + unsigned int bound:1; + unsigned int want_ack:1; }; static inline struct dgram_sock *dgram_sk(const struct sock *sk) @@ -206,7 +206,7 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t size) { struct net_device *dev; - unsigned mtu; + unsigned int mtu; struct sk_buff *skb; struct dgram_sock *ro = dgram_sk(sk); int hlen, tlen; diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c index f96bae8fd330..50e823927d49 100644 --- a/net/ieee802154/raw.c +++ b/net/ieee802154/raw.c @@ -106,7 +106,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t size) { struct net_device *dev; - unsigned mtu; + unsigned int mtu; struct sk_buff *skb; int hlen, tlen; int err; diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 18d9b81ecb1a..373b56bf8f49 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1059,7 +1059,7 @@ static int arp_req_set(struct net *net, struct arpreq *r, neigh = __neigh_lookup_errno(&arp_tbl, &ip, dev); err = PTR_ERR(neigh); if (!IS_ERR(neigh)) { - unsigned state = NUD_STALE; + unsigned int state = NUD_STALE; if (r->arp_flags & ATF_PERM) state = NUD_PERMANENT; err = neigh_update(neigh, (r->arp_flags & ATF_COM) ? @@ -1071,7 +1071,7 @@ static int arp_req_set(struct net *net, struct arpreq *r, return err; } -static unsigned arp_state_to_flags(struct neighbour *neigh) +static unsigned int arp_state_to_flags(struct neighbour *neigh) { if (neigh->nud_state&NUD_PERMANENT) return ATF_PERM | ATF_COM; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 7ba2196e4377..8a01bfb7c873 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1125,7 +1125,7 @@ skip: } } -static inline bool inetdev_valid_mtu(unsigned mtu) +static inline bool inetdev_valid_mtu(unsigned int mtu) { return mtu >= 68; } diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index cbe3a68507cf..3854411fa37c 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -136,13 +136,13 @@ static void fib_flush(struct net *net) * Find address type as if only "dev" was present in the system. If * on_dev is NULL then all interfaces are taken into consideration. */ -static inline unsigned __inet_dev_addr_type(struct net *net, - const struct net_device *dev, - __be32 addr) +static inline unsigned int __inet_dev_addr_type(struct net *net, + const struct net_device *dev, + __be32 addr) { struct flowi4 fl4 = { .daddr = addr }; struct fib_result res; - unsigned ret = RTN_BROADCAST; + unsigned int ret = RTN_BROADCAST; struct fib_table *local_table; if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr)) @@ -740,7 +740,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim) #define BRD_OK 2 #define BRD0_OK 4 #define BRD1_OK 8 - unsigned ok = 0; + unsigned int ok = 0; int subnet = 0; /* Primary network */ int gone = 1; /* Address is missing */ int same_prefsrc = 0; /* Another primary with same IP */ diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 14409f111bc2..7d972f650a61 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -42,7 +42,8 @@ EXPORT_SYMBOL(sysctl_local_reserved_ports); void inet_get_local_port_range(int *low, int *high) { - unsigned seq; + unsigned int seq; + do { seq = read_seqbegin(&sysctl_local_ports.lock); diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index 89168c6351ff..543ef6225458 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c @@ -263,7 +263,7 @@ rescan: void inet_twdr_hangman(unsigned long data) { struct inet_timewait_death_row *twdr; - int unsigned need_timer; + unsigned int need_timer; twdr = (struct inet_timewait_death_row *)data; spin_lock(&twdr->death_lock); diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 1372c4586edc..95722ed0e5bb 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -413,7 +413,7 @@ int ip_options_compile(struct net *net, opt->is_changed = 1; } } else { - unsigned overflow = optptr[3]>>4; + unsigned int overflow = optptr[3]>>4; if (overflow == 15) { pp_ptr = optptr + 3; goto error; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 0a87e1fc0ce5..51c6c672c8aa 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -147,7 +147,7 @@ static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb) void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb) { struct inet_sock *inet = inet_sk(skb->sk); - unsigned flags = inet->cmsg_flags; + unsigned int flags = inet->cmsg_flags; /* Ordered by supposed usage frequency */ if (flags & 1) @@ -1094,7 +1094,7 @@ EXPORT_SYMBOL(compat_ip_setsockopt); */ static int do_ip_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen, unsigned flags) + char __user *optval, int __user *optlen, unsigned int flags) { struct inet_sock *inet = inet_sk(sk); int val; diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index 92ac7e7363a0..f267280d8709 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -1198,7 +1198,7 @@ static int __init ic_dynamic(void) d = ic_first_dev; retries = CONF_SEND_RETRIES; get_random_bytes(&timeout, sizeof(timeout)); - timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM); + timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned int) CONF_TIMEOUT_RANDOM); for (;;) { /* Track the device we are configuring */ ic_dev_xid = d->xid; diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 4f47e064e262..3cd8c586741a 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -12,7 +12,7 @@ #include /* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */ -int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type) +int ip_route_me_harder(struct sk_buff *skb, unsigned int addr_type) { struct net *net = dev_net(skb_dst(skb)->dev); const struct iphdr *iph = ip_hdr(skb); diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index fd7a3f68917f..a3935273869f 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -303,7 +303,7 @@ unsigned int arpt_do_table(struct sk_buff *skb, if (v < 0) { /* Pop from stack? */ if (v != XT_RETURN) { - verdict = (unsigned)(-v) - 1; + verdict = (unsigned int)(-v) - 1; break; } e = back; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 24e556e83a3b..585b80f3cc68 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -377,7 +377,7 @@ ipt_do_table(struct sk_buff *skb, if (v < 0) { /* Pop from stack? */ if (v != XT_RETURN) { - verdict = (unsigned)(-v) - 1; + verdict = (unsigned int)(-v) - 1; break; } if (*stackptr <= origptr) { diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c index 57932c43960e..ea4a23813d26 100644 --- a/net/ipv4/netfilter/nf_nat_sip.c +++ b/net/ipv4/netfilter/nf_nat_sip.c @@ -283,7 +283,7 @@ static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff, __be32 newip; u_int16_t port; char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; - unsigned buflen; + unsigned int buflen; /* Connection will come from reply */ if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 9f380ace22ee..6e930c7174dd 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -51,15 +51,16 @@ static struct ping_table ping_table; static u16 ping_port_rover; -static inline int ping_hashfn(struct net *net, unsigned num, unsigned mask) +static inline int ping_hashfn(struct net *net, unsigned int num, unsigned int mask) { int res = (num + net_hash_mix(net)) & mask; + pr_debug("hash(%d) = %d\n", num, res); return res; } static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table, - struct net *net, unsigned num) + struct net *net, unsigned int num) { return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)]; } @@ -188,7 +189,8 @@ static void inet_get_ping_group_range_net(struct net *net, gid_t *low, gid_t *high) { gid_t *data = net->ipv4.sysctl_ping_group_range; - unsigned seq; + unsigned int seq; + do { seq = read_seqbegin(&sysctl_local_ports.lock); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index a13ce2364ed2..ac4d6b3fa9c9 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -296,7 +296,7 @@ static inline void rt_hash_lock_init(void) #endif static struct rt_hash_bucket *rt_hash_table __read_mostly; -static unsigned rt_hash_mask __read_mostly; +static unsigned int rt_hash_mask __read_mostly; static unsigned int rt_hash_log __read_mostly; static DEFINE_PER_CPU(struct rt_cache_stat, rt_cache_stat); @@ -1143,7 +1143,7 @@ static int rt_bind_neighbour(struct rtable *rt) return 0; } -static struct rtable *rt_intern_hash(unsigned hash, struct rtable *rt, +static struct rtable *rt_intern_hash(unsigned int hash, struct rtable *rt, struct sk_buff *skb, int ifindex) { struct rtable *rth, *cand; @@ -1384,7 +1384,7 @@ void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more) } EXPORT_SYMBOL(__ip_select_ident); -static void rt_del(unsigned hash, struct rtable *rt) +static void rt_del(unsigned int hash, struct rtable *rt) { struct rtable __rcu **rthp; struct rtable *aux; @@ -1538,7 +1538,7 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) ip_rt_put(rt); ret = NULL; } else if (rt->rt_flags & RTCF_REDIRECTED) { - unsigned hash = rt_hash(rt->rt_key_dst, rt->rt_key_src, + unsigned int hash = rt_hash(rt->rt_key_dst, rt->rt_key_src, rt->rt_oif, rt_genid(dev_net(dst->dev))); rt_del(hash, rt); @@ -2217,7 +2217,7 @@ static int ip_mkroute_input(struct sk_buff *skb, { struct rtable *rth = NULL; int err; - unsigned hash; + unsigned int hash; #ifdef CONFIG_IP_ROUTE_MULTIPATH if (res->fi && res->fi->fib_nhs > 1) @@ -2255,10 +2255,10 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, struct fib_result res; struct in_device *in_dev = __in_dev_get_rcu(dev); struct flowi4 fl4; - unsigned flags = 0; + unsigned int flags = 0; u32 itag = 0; - struct rtable *rth; - unsigned hash; + struct rtable *rth; + unsigned int hash; __be32 spec_dst; int err = -EINVAL; struct net *net = dev_net(dev); @@ -2433,8 +2433,8 @@ martian_source_keep_err: int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr, u8 tos, struct net_device *dev, bool noref) { - struct rtable *rth; - unsigned hash; + struct rtable *rth; + unsigned int hash; int iif = dev->ifindex; struct net *net; int res; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 7a7724da9bff..34a628625d9c 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -78,7 +78,7 @@ static int ipv4_local_port_range(ctl_table *table, int write, static void inet_get_ping_group_range_table(struct ctl_table *table, gid_t *low, gid_t *high) { gid_t *data = table->data; - unsigned seq; + unsigned int seq; do { seq = read_seqbegin(&sysctl_local_ports.lock); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8bb6adeb62c0..c53e8a827f55 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2675,7 +2675,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, { struct sk_buff *segs = ERR_PTR(-EINVAL); struct tcphdr *th; - unsigned thlen; + unsigned int thlen; unsigned int seq; __be32 delta; unsigned int oldlen; @@ -3033,9 +3033,9 @@ int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp, struct scatterlist sg; const struct tcphdr *tp = tcp_hdr(skb); struct hash_desc *desc = &hp->md5_desc; - unsigned i; - const unsigned head_data_len = skb_headlen(skb) > header_len ? - skb_headlen(skb) - header_len : 0; + unsigned int i; + const unsigned int head_data_len = skb_headlen(skb) > header_len ? + skb_headlen(skb) - header_len : 0; const struct skb_shared_info *shi = skb_shinfo(skb); struct sk_buff *frag_iter; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 3dc94febc9ee..99448f06a42a 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -175,7 +175,7 @@ static void tcp_measure_rcv_mss(struct sock *sk, const struct sk_buff *skb) static void tcp_incr_quickack(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); - unsigned quickacks = tcp_sk(sk)->rcv_wnd / (2 * icsk->icsk_ack.rcv_mss); + unsigned int quickacks = tcp_sk(sk)->rcv_wnd / (2 * icsk->icsk_ack.rcv_mss); if (quickacks == 0) quickacks = 2; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 376b2cfbb685..de8790ced946 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -563,13 +563,13 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, /* Compute TCP options for SYN packets. This is not the final * network wire format yet. */ -static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb, +static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, struct tcp_out_options *opts, struct tcp_md5sig_key **md5) { struct tcp_sock *tp = tcp_sk(sk); struct tcp_cookie_values *cvp = tp->cookie_values; - unsigned remaining = MAX_TCP_OPTION_SPACE; + 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; @@ -663,15 +663,15 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb, } /* Set up TCP options for SYN-ACKs. */ -static unsigned tcp_synack_options(struct sock *sk, +static unsigned int tcp_synack_options(struct sock *sk, struct request_sock *req, - unsigned mss, struct sk_buff *skb, + unsigned int mss, struct sk_buff *skb, struct tcp_out_options *opts, struct tcp_md5sig_key **md5, struct tcp_extend_values *xvp) { struct inet_request_sock *ireq = inet_rsk(req); - unsigned remaining = MAX_TCP_OPTION_SPACE; + unsigned int remaining = MAX_TCP_OPTION_SPACE; u8 cookie_plus = (xvp != NULL && !xvp->cookie_out_never) ? xvp->cookie_plus : 0; @@ -742,13 +742,13 @@ static unsigned tcp_synack_options(struct sock *sk, /* Compute TCP options for ESTABLISHED sockets. This is not the * final wire format yet. */ -static unsigned tcp_established_options(struct sock *sk, struct sk_buff *skb, +static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb, struct tcp_out_options *opts, struct tcp_md5sig_key **md5) { struct tcp_skb_cb *tcb = skb ? TCP_SKB_CB(skb) : NULL; struct tcp_sock *tp = tcp_sk(sk); - unsigned size = 0; + unsigned int size = 0; unsigned int eff_sacks; #ifdef CONFIG_TCP_MD5SIG @@ -770,9 +770,9 @@ static unsigned tcp_established_options(struct sock *sk, struct sk_buff *skb, eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; if (unlikely(eff_sacks)) { - const unsigned remaining = MAX_TCP_OPTION_SPACE - size; + const unsigned int remaining = MAX_TCP_OPTION_SPACE - size; opts->num_sack_blocks = - min_t(unsigned, eff_sacks, + min_t(unsigned int, eff_sacks, (remaining - TCPOLEN_SACK_BASE_ALIGNED) / TCPOLEN_SACK_PERBLOCK); size += TCPOLEN_SACK_BASE_ALIGNED + @@ -801,7 +801,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, struct tcp_sock *tp; struct tcp_skb_cb *tcb; struct tcp_out_options opts; - unsigned tcp_options_size, tcp_header_size; + unsigned int tcp_options_size, tcp_header_size; struct tcp_md5sig_key *md5; struct tcphdr *th; int err; @@ -1258,7 +1258,7 @@ unsigned int tcp_current_mss(struct sock *sk) const struct tcp_sock *tp = tcp_sk(sk); const struct dst_entry *dst = __sk_dst_get(sk); u32 mss_now; - unsigned header_len; + unsigned int header_len; struct tcp_out_options opts; struct tcp_md5sig_key *md5; @@ -1389,7 +1389,7 @@ static inline int tcp_minshall_check(const struct tcp_sock *tp) */ static inline int tcp_nagle_check(const struct tcp_sock *tp, const struct sk_buff *skb, - unsigned mss_now, int nonagle) + unsigned int mss_now, int nonagle) { return skb->len < mss_now && ((nonagle & TCP_NAGLE_CORK) || diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c index a8df7052e0b6..4526fe68e60e 100644 --- a/net/ipv4/tcp_probe.c +++ b/net/ipv4/tcp_probe.c @@ -91,7 +91,7 @@ static inline int tcp_probe_avail(void) * Note: arguments must match tcp_rcv_established()! */ static int jtcp_rcv_established(struct sock *sk, struct sk_buff *skb, - struct tcphdr *th, unsigned len) + struct tcphdr *th, unsigned int len) { const struct tcp_sock *tp = tcp_sk(sk); const struct inet_sock *inet = inet_sk(sk); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 381ea5115142..3430e8fc18de 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -207,7 +207,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, if (!snum) { int low, high, remaining; - unsigned rand; + unsigned int rand; unsigned short first, last; DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN); diff --git a/net/ipv6/addrconf_core.c b/net/ipv6/addrconf_core.c index 7981bde57575..d051e5f4bf34 100644 --- a/net/ipv6/addrconf_core.c +++ b/net/ipv6/addrconf_core.c @@ -8,7 +8,7 @@ #define IPV6_ADDR_SCOPE_TYPE(scope) ((scope) << 16) -static inline unsigned ipv6_addr_scope2type(unsigned scope) +static inline unsigned int ipv6_addr_scope2type(unsigned int scope) { switch (scope) { case IPV6_ADDR_SCOPE_NODELOCAL: diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index f6210d6fd7d8..7fba35aea06c 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -515,7 +515,7 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) u8 nexthdr = ipv6_hdr(skb)->nexthdr; while (off <= opt->lastopt) { - unsigned len; + unsigned int len; u8 *ptr = nh + off; switch (nexthdr) { diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index b7867a1215b1..1dd632971bce 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -705,9 +705,9 @@ static int ip6fl_seq_show(struct seq_file *seq, void *v) struct ip6_flowlabel *fl = v; seq_printf(seq, "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n", - (unsigned)ntohl(fl->label), + (unsigned int)ntohl(fl->label), fl->share, - (unsigned)fl->owner, + (int)fl->owner, atomic_read(&fl->users), fl->linger/HZ, (long)(fl->expires - jiffies)/HZ, diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index aa21da6a09cd..e25b0fdd935c 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -198,7 +198,7 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct ip6_tnl_parm *p) { const struct in6_addr *remote = &p->raddr; const struct in6_addr *local = &p->laddr; - unsigned h = 0; + unsigned int h = 0; int prio = 0; if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index ca1af0760c4c..ba6d13d1f1e1 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -942,7 +942,7 @@ static int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt, } static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen, unsigned flags) + char __user *optval, int __user *optlen, unsigned int flags) { struct ipv6_pinfo *np = inet6_sk(sk); int len; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 9d4e15559319..d4e350f72bbb 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -396,7 +396,7 @@ ip6t_do_table(struct sk_buff *skb, if (v < 0) { /* Pop from stack? */ if (v != XT_RETURN) { - verdict = (unsigned)(-v) - 1; + verdict = (unsigned int)(-v) - 1; break; } if (*stackptr <= origptr) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 8c5df6f3a2de..f910cce69c9f 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -82,7 +82,7 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu); static struct rt6_info *rt6_add_route_info(struct net *net, const struct in6_addr *prefix, int prefixlen, const struct in6_addr *gwaddr, int ifindex, - unsigned pref); + unsigned int pref); static struct rt6_info *rt6_get_route_info(struct net *net, const struct in6_addr *prefix, int prefixlen, const struct in6_addr *gwaddr, int ifindex); @@ -1870,7 +1870,7 @@ out: static struct rt6_info *rt6_add_route_info(struct net *net, const struct in6_addr *prefix, int prefixlen, const struct in6_addr *gwaddr, int ifindex, - unsigned pref) + unsigned int pref) { struct fib6_config cfg = { .fc_table = RT6_TABLE_INFO, @@ -2200,10 +2200,9 @@ void rt6_ifdown(struct net *net, struct net_device *dev) icmp6_clean_all(fib6_ifdown, &adn); } -struct rt6_mtu_change_arg -{ +struct rt6_mtu_change_arg { struct net_device *dev; - unsigned mtu; + unsigned int mtu; }; static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) @@ -2245,7 +2244,7 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) return 0; } -void rt6_mtu_change(struct net_device *dev, unsigned mtu) +void rt6_mtu_change(struct net_device *dev, unsigned int mtu) { struct rt6_mtu_change_arg arg = { .dev = dev, diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index 4fe1db12d2a3..ee5a7065aacc 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c @@ -68,9 +68,9 @@ static DEFINE_SPINLOCK(xfrm6_tunnel_spi_lock); static struct kmem_cache *xfrm6_tunnel_spi_kmem __read_mostly; -static inline unsigned xfrm6_tunnel_spi_hash_byaddr(const xfrm_address_t *addr) +static inline unsigned int xfrm6_tunnel_spi_hash_byaddr(const xfrm_address_t *addr) { - unsigned h; + unsigned int h; h = (__force u32)(addr->a6[0] ^ addr->a6[1] ^ addr->a6[2] ^ addr->a6[3]); h ^= h >> 16; @@ -80,7 +80,7 @@ static inline unsigned xfrm6_tunnel_spi_hash_byaddr(const xfrm_address_t *addr) return h; } -static inline unsigned xfrm6_tunnel_spi_hash_byspi(u32 spi) +static inline unsigned int xfrm6_tunnel_spi_hash_byspi(u32 spi) { return spi % XFRM6_TUNNEL_SPI_BYSPI_HSIZE; } diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c index 77c5e6499f8f..d0667d68351d 100644 --- a/net/irda/ircomm/ircomm_tty_ioctl.c +++ b/net/irda/ircomm/ircomm_tty_ioctl.c @@ -54,7 +54,7 @@ */ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self) { - unsigned cflag, cval; + unsigned int cflag, cval; int baud; IRDA_DEBUG(2, "%s()\n", __func__ ); diff --git a/net/key/af_key.c b/net/key/af_key.c index 7e5d927b576f..34e418508a67 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1714,7 +1714,7 @@ static int key_notify_sa_flush(const struct km_event *c) static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_msg *hdr, void * const *ext_hdrs) { struct net *net = sock_net(sk); - unsigned proto; + unsigned int proto; struct km_event c; struct xfrm_audit audit_info; int err, err2; @@ -3547,7 +3547,7 @@ static int pfkey_sendmsg(struct kiocb *kiocb, goto out; err = -EMSGSIZE; - if ((unsigned)len > sk->sk_sndbuf - 32) + if ((unsigned int)len > sk->sk_sndbuf - 32) goto out; err = -ENOBUFS; diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index a16a48e79fab..09e4a38b4f43 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -54,15 +54,15 @@ struct l2tp_tunnel; */ struct l2tp_session_cfg { enum l2tp_pwtype pw_type; - unsigned data_seq:2; /* data sequencing level + unsigned int data_seq:2; /* data sequencing level * 0 => none, 1 => IP only, * 2 => all */ - unsigned recv_seq:1; /* expect receive packets with + unsigned int recv_seq:1; /* expect receive packets with * sequence numbers? */ - unsigned send_seq:1; /* send packets with sequence + unsigned int send_seq:1; /* send packets with sequence * numbers? */ - unsigned lns_mode:1; /* behave as LNS? LAC enables + unsigned int lns_mode:1; /* behave as LNS? LAC enables * sequence numbers under * control of LNS. */ int debug; /* bitmask of debug message @@ -107,15 +107,15 @@ struct l2tp_session { char name[32]; /* for logging */ char ifname[IFNAMSIZ]; - unsigned data_seq:2; /* data sequencing level + unsigned int data_seq:2; /* data sequencing level * 0 => none, 1 => IP only, * 2 => all */ - unsigned recv_seq:1; /* expect receive packets with + unsigned int recv_seq:1; /* expect receive packets with * sequence numbers? */ - unsigned send_seq:1; /* send packets with sequence + unsigned int send_seq:1; /* send packets with sequence * numbers? */ - unsigned lns_mode:1; /* behave as LNS? LAC enables + unsigned int lns_mode:1; /* behave as LNS? LAC enables * sequence numbers under * control of LNS. */ int debug; /* bitmask of debug message diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index b9bef2c75026..17bc85d5b7ba 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -518,7 +518,7 @@ static int llc_ui_listen(struct socket *sock, int backlog) if (sock_flag(sk, SOCK_ZAPPED)) goto out; rc = 0; - if (!(unsigned)backlog) /* BSDism */ + if (!(unsigned int)backlog) /* BSDism */ backlog = 1; sk->sk_max_ack_backlog = backlog; if (sk->sk_state != TCP_LISTEN) { diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index eb66b9790a6f..819c342f5b30 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1618,7 +1618,7 @@ static struct nfnetlink_subsystem ip_set_netlink_subsys __read_mostly = { static int ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) { - unsigned *op; + unsigned int *op; void *data; int copylen = *len, ret = 0; @@ -1626,7 +1626,7 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) return -EPERM; if (optval != SO_IP_SET) return -EBADF; - if (*len < sizeof(unsigned)) + if (*len < sizeof(unsigned int)) return -EINVAL; data = vmalloc(*len); @@ -1636,7 +1636,7 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) ret = -EFAULT; goto done; } - op = (unsigned *) data; + op = (unsigned int *) data; if (*op < IP_SET_OP_VERSION) { /* Check the version at the beginning of operations */ diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c index 52856178c9d7..64f9e8f13207 100644 --- a/net/netfilter/ipvs/ip_vs_app.c +++ b/net/netfilter/ipvs/ip_vs_app.c @@ -313,7 +313,7 @@ vs_fix_ack_seq(const struct ip_vs_seq *vseq, struct tcphdr *th) * Assumes already checked proto==IPPROTO_TCP and diff!=0. */ static inline void vs_seq_update(struct ip_vs_conn *cp, struct ip_vs_seq *vseq, - unsigned flag, __u32 seq, int diff) + unsigned int flag, __u32 seq, int diff) { /* spinlock is to keep updating cp->flags atomic */ spin_lock(&cp->lock); diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 29fa5badde75..4a09b7873003 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -86,42 +86,42 @@ struct ip_vs_aligned_lock static struct ip_vs_aligned_lock __ip_vs_conntbl_lock_array[CT_LOCKARRAY_SIZE] __cacheline_aligned; -static inline void ct_read_lock(unsigned key) +static inline void ct_read_lock(unsigned int key) { read_lock(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l); } -static inline void ct_read_unlock(unsigned key) +static inline void ct_read_unlock(unsigned int key) { read_unlock(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l); } -static inline void ct_write_lock(unsigned key) +static inline void ct_write_lock(unsigned int key) { write_lock(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l); } -static inline void ct_write_unlock(unsigned key) +static inline void ct_write_unlock(unsigned int key) { write_unlock(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l); } -static inline void ct_read_lock_bh(unsigned key) +static inline void ct_read_lock_bh(unsigned int key) { read_lock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l); } -static inline void ct_read_unlock_bh(unsigned key) +static inline void ct_read_unlock_bh(unsigned int key) { read_unlock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l); } -static inline void ct_write_lock_bh(unsigned key) +static inline void ct_write_lock_bh(unsigned int key) { write_lock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l); } -static inline void ct_write_unlock_bh(unsigned key) +static inline void ct_write_unlock_bh(unsigned int key) { write_unlock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l); } @@ -130,7 +130,7 @@ static inline void ct_write_unlock_bh(unsigned key) /* * Returns hash value for IPVS connection entry */ -static unsigned int ip_vs_conn_hashkey(struct net *net, int af, unsigned proto, +static unsigned int ip_vs_conn_hashkey(struct net *net, int af, unsigned int proto, const union nf_inet_addr *addr, __be16 port) { @@ -188,7 +188,7 @@ static unsigned int ip_vs_conn_hashkey_conn(const struct ip_vs_conn *cp) */ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) { - unsigned hash; + unsigned int hash; int ret; if (cp->flags & IP_VS_CONN_F_ONE_PACKET) @@ -224,7 +224,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) */ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp) { - unsigned hash; + unsigned int hash; int ret; /* unhash it and decrease its reference counter */ @@ -257,7 +257,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp) static inline struct ip_vs_conn * __ip_vs_conn_in_get(const struct ip_vs_conn_param *p) { - unsigned hash; + unsigned int hash; struct ip_vs_conn *cp; struct hlist_node *n; @@ -344,7 +344,7 @@ EXPORT_SYMBOL_GPL(ip_vs_conn_in_get_proto); /* Get reference to connection template */ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p) { - unsigned hash; + unsigned int hash; struct ip_vs_conn *cp; struct hlist_node *n; @@ -394,7 +394,7 @@ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p) * p->vaddr, p->vport: pkt dest address (foreign host) */ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p) { - unsigned hash; + unsigned int hash; struct ip_vs_conn *cp, *ret=NULL; struct hlist_node *n; @@ -824,7 +824,7 @@ void ip_vs_conn_expire_now(struct ip_vs_conn *cp) */ struct ip_vs_conn * ip_vs_conn_new(const struct ip_vs_conn_param *p, - const union nf_inet_addr *daddr, __be16 dport, unsigned flags, + const union nf_inet_addr *daddr, __be16 dport, unsigned int flags, struct ip_vs_dest *dest, __u32 fwmark) { struct ip_vs_conn *cp; @@ -1057,7 +1057,7 @@ static const struct file_operations ip_vs_conn_fops = { .release = seq_release_net, }; -static const char *ip_vs_origin_name(unsigned flags) +static const char *ip_vs_origin_name(unsigned int flags) { if (flags & IP_VS_CONN_F_SYNC) return "SYNC"; @@ -1169,7 +1169,7 @@ void ip_vs_random_dropentry(struct net *net) * Randomly scan 1/32 of the whole table every second */ for (idx = 0; idx < (ip_vs_conn_tab_size>>5); idx++) { - unsigned hash = net_random() & ip_vs_conn_tab_mask; + unsigned int hash = net_random() & ip_vs_conn_tab_mask; struct hlist_node *n; /* diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 2555816e7788..b5a5c7363f83 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -80,7 +80,7 @@ static atomic_t ipvs_netns_cnt = ATOMIC_INIT(0); #define icmp_id(icmph) (((icmph)->un).echo.id) #define icmpv6_id(icmph) (icmph->icmp6_dataun.u_echo.identifier) -const char *ip_vs_proto_name(unsigned proto) +const char *ip_vs_proto_name(unsigned int proto) { static char buf[20]; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 964d426d237f..b8d0df701227 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -265,11 +265,11 @@ static struct list_head ip_vs_svc_fwm_table[IP_VS_SVC_TAB_SIZE]; /* * Returns hash value for virtual service */ -static inline unsigned -ip_vs_svc_hashkey(struct net *net, int af, unsigned proto, +static inline unsigned int +ip_vs_svc_hashkey(struct net *net, int af, unsigned int proto, const union nf_inet_addr *addr, __be16 port) { - register unsigned porth = ntohs(port); + register unsigned int porth = ntohs(port); __be32 addr_fold = addr->ip; #ifdef CONFIG_IP_VS_IPV6 @@ -286,7 +286,7 @@ ip_vs_svc_hashkey(struct net *net, int af, unsigned proto, /* * Returns hash value of fwmark for virtual service lookup */ -static inline unsigned ip_vs_svc_fwm_hashkey(struct net *net, __u32 fwmark) +static inline unsigned int ip_vs_svc_fwm_hashkey(struct net *net, __u32 fwmark) { return (((size_t)net>>8) ^ fwmark) & IP_VS_SVC_TAB_MASK; } @@ -298,7 +298,7 @@ static inline unsigned ip_vs_svc_fwm_hashkey(struct net *net, __u32 fwmark) */ static int ip_vs_svc_hash(struct ip_vs_service *svc) { - unsigned hash; + unsigned int hash; if (svc->flags & IP_VS_SVC_F_HASHED) { pr_err("%s(): request for already hashed, called from %pF\n", @@ -361,7 +361,7 @@ static inline struct ip_vs_service * __ip_vs_service_find(struct net *net, int af, __u16 protocol, const union nf_inet_addr *vaddr, __be16 vport) { - unsigned hash; + unsigned int hash; struct ip_vs_service *svc; /* Check for "full" addressed entries */ @@ -388,7 +388,7 @@ __ip_vs_service_find(struct net *net, int af, __u16 protocol, static inline struct ip_vs_service * __ip_vs_svc_fwm_find(struct net *net, int af, __u32 fwmark) { - unsigned hash; + unsigned int hash; struct ip_vs_service *svc; /* Check for fwmark addressed entries */ @@ -489,11 +489,11 @@ __ip_vs_unbind_svc(struct ip_vs_dest *dest) /* * Returns hash value for real service */ -static inline unsigned ip_vs_rs_hashkey(int af, +static inline unsigned int ip_vs_rs_hashkey(int af, const union nf_inet_addr *addr, __be16 port) { - register unsigned porth = ntohs(port); + register unsigned int porth = ntohs(port); __be32 addr_fold = addr->ip; #ifdef CONFIG_IP_VS_IPV6 @@ -512,7 +512,7 @@ static inline unsigned ip_vs_rs_hashkey(int af, */ static int ip_vs_rs_hash(struct netns_ipvs *ipvs, struct ip_vs_dest *dest) { - unsigned hash; + unsigned int hash; if (!list_empty(&dest->d_list)) { return 0; @@ -555,7 +555,7 @@ ip_vs_lookup_real_service(struct net *net, int af, __u16 protocol, __be16 dport) { struct netns_ipvs *ipvs = net_ipvs(net); - unsigned hash; + unsigned int hash; struct ip_vs_dest *dest; /* @@ -842,7 +842,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, struct ip_vs_dest **dest_p) { struct ip_vs_dest *dest; - unsigned atype; + unsigned int atype; EnterFunction(2); @@ -1867,7 +1867,7 @@ struct ip_vs_iter { * Write the contents of the VS rule table to a PROCfs file. * (It is kept just for backward compatibility) */ -static inline const char *ip_vs_fwd_name(unsigned flags) +static inline const char *ip_vs_fwd_name(unsigned int flags) { switch (flags & IP_VS_CONN_F_FWD_MASK) { case IP_VS_CONN_F_LOCALNODE: diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c index 1c269e56200a..1a53a7a2fff0 100644 --- a/net/netfilter/ipvs/ip_vs_dh.c +++ b/net/netfilter/ipvs/ip_vs_dh.c @@ -68,7 +68,7 @@ struct ip_vs_dh_bucket { /* * Returns hash value for IPVS DH entry */ -static inline unsigned ip_vs_dh_hashkey(int af, const union nf_inet_addr *addr) +static inline unsigned int ip_vs_dh_hashkey(int af, const union nf_inet_addr *addr) { __be32 addr_fold = addr->ip; diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 538d74ee4f68..debb8c71422c 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -177,7 +177,7 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, __be16 port; struct ip_vs_conn *n_cp; char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ - unsigned buf_len; + unsigned int buf_len; int ret = 0; enum ip_conntrack_info ctinfo; struct nf_conn *ct; diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 0f16283fd058..27c24f156c28 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -142,7 +142,7 @@ static inline void ip_vs_lblc_free(struct ip_vs_lblc_entry *en) /* * Returns hash value for IPVS LBLC entry */ -static inline unsigned +static inline unsigned int ip_vs_lblc_hashkey(int af, const union nf_inet_addr *addr) { __be32 addr_fold = addr->ip; @@ -163,7 +163,7 @@ ip_vs_lblc_hashkey(int af, const union nf_inet_addr *addr) static void ip_vs_lblc_hash(struct ip_vs_lblc_table *tbl, struct ip_vs_lblc_entry *en) { - unsigned hash = ip_vs_lblc_hashkey(en->af, &en->addr); + unsigned int hash = ip_vs_lblc_hashkey(en->af, &en->addr); list_add(&en->list, &tbl->bucket[hash]); atomic_inc(&tbl->entries); @@ -178,7 +178,7 @@ static inline struct ip_vs_lblc_entry * ip_vs_lblc_get(int af, struct ip_vs_lblc_table *tbl, const union nf_inet_addr *addr) { - unsigned hash = ip_vs_lblc_hashkey(af, addr); + unsigned int hash = ip_vs_lblc_hashkey(af, addr); struct ip_vs_lblc_entry *en; list_for_each_entry(en, &tbl->bucket[hash], list) diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index eec797f8cce7..749875611ed6 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -311,7 +311,7 @@ static inline void ip_vs_lblcr_free(struct ip_vs_lblcr_entry *en) /* * Returns hash value for IPVS LBLCR entry */ -static inline unsigned +static inline unsigned int ip_vs_lblcr_hashkey(int af, const union nf_inet_addr *addr) { __be32 addr_fold = addr->ip; @@ -332,7 +332,7 @@ ip_vs_lblcr_hashkey(int af, const union nf_inet_addr *addr) static void ip_vs_lblcr_hash(struct ip_vs_lblcr_table *tbl, struct ip_vs_lblcr_entry *en) { - unsigned hash = ip_vs_lblcr_hashkey(en->af, &en->addr); + unsigned int hash = ip_vs_lblcr_hashkey(en->af, &en->addr); list_add(&en->list, &tbl->bucket[hash]); atomic_inc(&tbl->entries); @@ -347,7 +347,7 @@ static inline struct ip_vs_lblcr_entry * ip_vs_lblcr_get(int af, struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *addr) { - unsigned hash = ip_vs_lblcr_hashkey(af, addr); + unsigned int hash = ip_vs_lblcr_hashkey(af, addr); struct ip_vs_lblcr_entry *en; list_for_each_entry(en, &tbl->bucket[hash], list) diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index f843a8833250..6eda11de8c05 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -48,7 +48,7 @@ static struct ip_vs_protocol *ip_vs_proto_table[IP_VS_PROTO_TAB_SIZE]; */ static int __used __init register_ip_vs_protocol(struct ip_vs_protocol *pp) { - unsigned hash = IP_VS_PROTO_HASH(pp->protocol); + unsigned int hash = IP_VS_PROTO_HASH(pp->protocol); pp->next = ip_vs_proto_table[hash]; ip_vs_proto_table[hash] = pp; @@ -69,7 +69,7 @@ static int register_ip_vs_proto_netns(struct net *net, struct ip_vs_protocol *pp) { struct netns_ipvs *ipvs = net_ipvs(net); - unsigned hash = IP_VS_PROTO_HASH(pp->protocol); + unsigned int hash = IP_VS_PROTO_HASH(pp->protocol); struct ip_vs_proto_data *pd = kzalloc(sizeof(struct ip_vs_proto_data), GFP_ATOMIC); @@ -94,7 +94,7 @@ register_ip_vs_proto_netns(struct net *net, struct ip_vs_protocol *pp) static int unregister_ip_vs_protocol(struct ip_vs_protocol *pp) { struct ip_vs_protocol **pp_p; - unsigned hash = IP_VS_PROTO_HASH(pp->protocol); + unsigned int hash = IP_VS_PROTO_HASH(pp->protocol); pp_p = &ip_vs_proto_table[hash]; for (; *pp_p; pp_p = &(*pp_p)->next) { @@ -117,7 +117,7 @@ unregister_ip_vs_proto_netns(struct net *net, struct ip_vs_proto_data *pd) { struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_proto_data **pd_p; - unsigned hash = IP_VS_PROTO_HASH(pd->pp->protocol); + unsigned int hash = IP_VS_PROTO_HASH(pd->pp->protocol); pd_p = &ipvs->proto_data_table[hash]; for (; *pd_p; pd_p = &(*pd_p)->next) { @@ -139,7 +139,7 @@ unregister_ip_vs_proto_netns(struct net *net, struct ip_vs_proto_data *pd) struct ip_vs_protocol * ip_vs_proto_get(unsigned short proto) { struct ip_vs_protocol *pp; - unsigned hash = IP_VS_PROTO_HASH(proto); + unsigned int hash = IP_VS_PROTO_HASH(proto); for (pp = ip_vs_proto_table[hash]; pp; pp = pp->next) { if (pp->protocol == proto) @@ -157,7 +157,7 @@ struct ip_vs_proto_data * __ipvs_proto_data_get(struct netns_ipvs *ipvs, unsigned short proto) { struct ip_vs_proto_data *pd; - unsigned hash = IP_VS_PROTO_HASH(proto); + unsigned int hash = IP_VS_PROTO_HASH(proto); for (pd = ipvs->proto_data_table[hash]; pd; pd = pd->next) { if (pd->pp->protocol == proto) diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index 069e8d4d5c01..91e97ee049be 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -70,7 +70,7 @@ struct ip_vs_sh_bucket { /* * Returns hash value for IPVS SH entry */ -static inline unsigned ip_vs_sh_hashkey(int af, const union nf_inet_addr *addr) +static inline unsigned int ip_vs_sh_hashkey(int af, const union nf_inet_addr *addr) { __be32 addr_fold = addr->ip; diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 8a0d6d6889f0..f4e0b6cf8246 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -839,7 +839,7 @@ static void ip_vs_process_message_v0(struct net *net, const char *buffer, p = (char *)buffer + sizeof(struct ip_vs_sync_mesg_v0); for (i=0; inr_conns; i++) { - unsigned flags, state; + unsigned int flags, state; if (p + SIMPLE_CONN_SIZE > buffer+buflen) { IP_VS_ERR_RL("BACKUP v0, bogus conn\n"); @@ -1109,7 +1109,7 @@ static void ip_vs_process_message(struct net *net, __u8 *buffer, for (i=0; itimeout)) return 0; diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index e6ddde165612..3e797d1fcb94 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -103,7 +103,7 @@ int nfnetlink_has_listeners(struct net *net, unsigned int group) EXPORT_SYMBOL_GPL(nfnetlink_has_listeners); int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, - unsigned group, int echo, gfp_t flags) + unsigned int group, int echo, gfp_t flags) { return nlmsg_notify(net->nfnl, skb, pid, group, echo, flags); } diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index d2ff15a2412b..fc0d6dbe5d17 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -314,7 +314,7 @@ static int recent_mt_check(const struct xt_mtchk_param *par) #ifdef CONFIG_PROC_FS struct proc_dir_entry *pde; #endif - unsigned i; + unsigned int i; int ret = -EINVAL; if (unlikely(!hash_rnd_inited)) { diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 1c51d7a58f0b..743262becd6e 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -97,7 +97,7 @@ static int nr_rebuild_header(struct sk_buff *skb) static int nr_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, - const void *daddr, const void *saddr, unsigned len) + const void *daddr, const void *saddr, unsigned int len) { unsigned char *buff = skb_push(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); diff --git a/net/nfc/core.c b/net/nfc/core.c index 295d129864d2..32a7b615e65f 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -475,12 +475,12 @@ EXPORT_SYMBOL(nfc_class); static int match_idx(struct device *d, void *data) { struct nfc_dev *dev = to_nfc_dev(d); - unsigned *idx = data; + unsigned int *idx = data; return dev->idx == *idx; } -struct nfc_dev *nfc_get_device(unsigned idx) +struct nfc_dev *nfc_get_device(unsigned int idx) { struct device *d; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index ec8794c1099c..061a830b0a88 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -127,7 +127,7 @@ int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx, u8 comm_mode, u8 rf_mode); int nfc_genl_dep_link_down_event(struct nfc_dev *dev); -struct nfc_dev *nfc_get_device(unsigned idx); +struct nfc_dev *nfc_get_device(unsigned int idx); static inline void nfc_put_device(struct nfc_dev *dev) { diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index c1068aed03d1..5920bda4ab6b 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -140,9 +140,9 @@ int ovs_netdev_get_ifindex(const struct vport *vport) return netdev_vport->dev->ifindex; } -static unsigned packet_length(const struct sk_buff *skb) +static unsigned int packet_length(const struct sk_buff *skb) { - unsigned length = skb->len - ETH_HLEN; + unsigned int length = skb->len - ETH_HLEN; if (skb->protocol == htons(ETH_P_8021Q)) length -= VLAN_HLEN; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 4f2c0df79563..d2b5f6591f1a 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1764,7 +1764,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, macoff = netoff = TPACKET_ALIGN(po->tp_hdrlen) + 16 + po->tp_reserve; } else { - unsigned maclen = skb_network_offset(skb); + unsigned int maclen = skb_network_offset(skb); netoff = TPACKET_ALIGN(po->tp_hdrlen + (maclen < 16 ? 16 : maclen)) + po->tp_reserve; diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index d65f699fbf34..779ce4ff92ec 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -129,7 +129,7 @@ static const struct net_proto_family phonet_proto_family = { /* Phonet device header operations */ static int pn_header_create(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, - const void *saddr, unsigned len) + const void *saddr, unsigned int len) { u8 *media = skb_push(skb, 1); diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 9726fe684ab8..9dd4f926f7d1 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -273,7 +273,7 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb) hdr = pnp_hdr(skb); if (hdr->data[0] != PN_PEP_TYPE_COMMON) { LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u\n", - (unsigned)hdr->data[0]); + (unsigned int)hdr->data[0]); return -EOPNOTSUPP; } @@ -305,7 +305,7 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb) default: LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP indication: %u\n", - (unsigned)hdr->data[1]); + (unsigned int)hdr->data[1]); return -EOPNOTSUPP; } if (wake) @@ -478,9 +478,9 @@ static void pipe_destruct(struct sock *sk) skb_queue_purge(&pn->ctrlreq_queue); } -static u8 pipe_negotiate_fc(const u8 *fcs, unsigned n) +static u8 pipe_negotiate_fc(const u8 *fcs, unsigned int n) { - unsigned i; + unsigned int i; u8 final_fc = PN_NO_FLOW_CONTROL; for (i = 0; i < n; i++) { diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index da564efe4f5b..36f75a9e2c3d 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -268,7 +268,7 @@ static int phonet_device_autoconf(struct net_device *dev) static void phonet_route_autodel(struct net_device *dev) { struct phonet_net *pnn = phonet_pernet(dev_net(dev)); - unsigned i; + unsigned int i; DECLARE_BITMAP(deleted, 64); /* Remove left-over Phonet routes */ diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 4c7eff30dfa9..89cfa9ce4939 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -58,7 +58,7 @@ static struct { void __init pn_sock_init(void) { - unsigned i; + unsigned int i; for (i = 0; i < PN_HASHSIZE; i++) INIT_HLIST_HEAD(pnsocks.hlist + i); @@ -116,7 +116,7 @@ struct sock *pn_find_sock_by_sa(struct net *net, const struct sockaddr_pn *spn) void pn_deliver_sock_broadcast(struct net *net, struct sk_buff *skb) { struct hlist_head *hlist = pnsocks.hlist; - unsigned h; + unsigned int h; rcu_read_lock(); for (h = 0; h < PN_HASHSIZE; h++) { @@ -545,7 +545,7 @@ static struct sock *pn_sock_get_idx(struct seq_file *seq, loff_t pos) struct hlist_head *hlist = pnsocks.hlist; struct hlist_node *node; struct sock *sknode; - unsigned h; + unsigned int h; for (h = 0; h < PN_HASHSIZE; h++) { sk_for_each_rcu(sknode, node, hlist) { @@ -710,7 +710,7 @@ int pn_sock_unbind_res(struct sock *sk, u8 res) void pn_sock_unbind_all_res(struct sock *sk) { - unsigned res, match = 0; + unsigned int res, match = 0; mutex_lock(&resource_mutex); for (res = 0; res < 256; res++) { @@ -732,7 +732,7 @@ void pn_sock_unbind_all_res(struct sock *sk) static struct sock **pn_res_get_idx(struct seq_file *seq, loff_t pos) { struct net *net = seq_file_net(seq); - unsigned i; + unsigned int i; if (!net_eq(net, &init_net)) return NULL; @@ -750,7 +750,7 @@ static struct sock **pn_res_get_idx(struct seq_file *seq, loff_t pos) static struct sock **pn_res_get_next(struct seq_file *seq, struct sock **sk) { struct net *net = seq_file_net(seq); - unsigned i; + unsigned int i; BUG_ON(!net_eq(net, &init_net)); diff --git a/net/phonet/sysctl.c b/net/phonet/sysctl.c index 740bf209dd97..8bed7675b3f4 100644 --- a/net/phonet/sysctl.c +++ b/net/phonet/sysctl.c @@ -50,7 +50,8 @@ static void set_local_port_range(int range[2]) void phonet_get_local_port_range(int *min, int *max) { - unsigned seq; + unsigned int seq; + do { seq = read_seqbegin(&local_port_range_lock); if (min) diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index 906cc05bba63..28dbdb911b85 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -37,7 +37,7 @@ static int rose_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, - const void *daddr, const void *saddr, unsigned len) + const void *daddr, const void *saddr, unsigned int len) { unsigned char *buff = skb_push(skb, ROSE_MIN_LEN + 2); diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index 47f1fdb346b0..7ca57741b2fb 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c @@ -399,7 +399,7 @@ int rose_parse_facilities(unsigned char *p, unsigned packet_len, facilities_len = *p++; - if (facilities_len == 0 || (unsigned)facilities_len > packet_len) + if (facilities_len == 0 || (unsigned int)facilities_len > packet_len) return 0; while (facilities_len >= 3 && *p == 0x00) { diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 74c064c0dfdd..05996d0dd828 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -26,7 +26,7 @@ MODULE_AUTHOR("Red Hat, Inc."); MODULE_LICENSE("GPL"); MODULE_ALIAS_NETPROTO(PF_RXRPC); -unsigned rxrpc_debug; // = RXRPC_DEBUG_KPROTO; +unsigned int rxrpc_debug; // = RXRPC_DEBUG_KPROTO; module_param_named(debug, rxrpc_debug, uint, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(debug, "RxRPC debugging mask"); @@ -513,7 +513,7 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) { struct rxrpc_sock *rx = rxrpc_sk(sock->sk); - unsigned min_sec_level; + unsigned int min_sec_level; int ret; _enter(",%d,%d,,%d", level, optname, optlen); @@ -555,13 +555,13 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname, case RXRPC_MIN_SECURITY_LEVEL: ret = -EINVAL; - if (optlen != sizeof(unsigned)) + if (optlen != sizeof(unsigned int)) goto error; ret = -EISCONN; if (rx->sk.sk_state != RXRPC_UNCONNECTED) goto error; ret = get_user(min_sec_level, - (unsigned __user *) optval); + (unsigned int __user *) optval); if (ret < 0) goto error; ret = -EINVAL; diff --git a/net/rxrpc/ar-ack.c b/net/rxrpc/ar-ack.c index c3126e864f3c..e4d9cbcff402 100644 --- a/net/rxrpc/ar-ack.c +++ b/net/rxrpc/ar-ack.c @@ -19,7 +19,7 @@ #include #include "ar-internal.h" -static unsigned rxrpc_ack_defer = 1; +static unsigned int rxrpc_ack_defer = 1; static const char *const rxrpc_acks[] = { "---", "REQ", "DUP", "OOS", "WIN", "MEM", "PNG", "PNR", "DLY", "IDL", @@ -548,11 +548,11 @@ static void rxrpc_zap_tx_window(struct rxrpc_call *call) * process the extra information that may be appended to an ACK packet */ static void rxrpc_extract_ackinfo(struct rxrpc_call *call, struct sk_buff *skb, - unsigned latest, int nAcks) + unsigned int latest, int nAcks) { struct rxrpc_ackinfo ackinfo; struct rxrpc_peer *peer; - unsigned mtu; + unsigned int mtu; if (skb_copy_bits(skb, nAcks + 3, &ackinfo, sizeof(ackinfo)) < 0) { _leave(" [no ackinfo]"); diff --git a/net/rxrpc/ar-call.c b/net/rxrpc/ar-call.c index bf656c230ba9..a3bbb360a3f9 100644 --- a/net/rxrpc/ar-call.c +++ b/net/rxrpc/ar-call.c @@ -38,8 +38,8 @@ const char *const rxrpc_call_states[] = { struct kmem_cache *rxrpc_call_jar; LIST_HEAD(rxrpc_calls); DEFINE_RWLOCK(rxrpc_call_lock); -static unsigned rxrpc_call_max_lifetime = 60; -static unsigned rxrpc_dead_call_timeout = 2; +static unsigned int rxrpc_call_max_lifetime = 60; +static unsigned int rxrpc_dead_call_timeout = 2; static void rxrpc_destroy_call(struct work_struct *work); static void rxrpc_call_life_expired(unsigned long _call); diff --git a/net/rxrpc/ar-input.c b/net/rxrpc/ar-input.c index 1a2b0633fece..529572f18d1f 100644 --- a/net/rxrpc/ar-input.c +++ b/net/rxrpc/ar-input.c @@ -76,7 +76,7 @@ int rxrpc_queue_rcv_skb(struct rxrpc_call *call, struct sk_buff *skb, * --ANK */ // ret = -ENOBUFS; // if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= -// (unsigned) sk->sk_rcvbuf) +// (unsigned int) sk->sk_rcvbuf) // goto out; ret = sk_filter(sk, skb); diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 8e22bd345e71..a693aca2ae2e 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -83,7 +83,7 @@ struct rxrpc_skb_priv { struct rxrpc_call *call; /* call with which associated */ unsigned long resend_at; /* time in jiffies at which to resend */ union { - unsigned offset; /* offset into buffer of next read */ + unsigned int offset; /* offset into buffer of next read */ int remain; /* amount of space remaining for next write */ u32 error; /* network error code */ bool need_resend; /* T if needs resending */ @@ -176,9 +176,9 @@ struct rxrpc_peer { struct list_head error_targets; /* targets for net error distribution */ spinlock_t lock; /* access lock */ atomic_t usage; - unsigned if_mtu; /* interface MTU for this peer */ - unsigned mtu; /* network MTU for this peer */ - unsigned maxdata; /* data size (MTU - hdrsize) */ + unsigned int if_mtu; /* interface MTU for this peer */ + unsigned int mtu; /* network MTU for this peer */ + unsigned int maxdata; /* data size (MTU - hdrsize) */ unsigned short hdrsize; /* header size (IP + UDP + RxRPC) */ int debug_id; /* debug ID for printks */ int net_error; /* network error distributed */ @@ -187,8 +187,8 @@ struct rxrpc_peer { /* calculated RTT cache */ #define RXRPC_RTT_CACHE_SIZE 32 suseconds_t rtt; /* current RTT estimate (in uS) */ - unsigned rtt_point; /* next entry at which to insert */ - unsigned rtt_usage; /* amount of cache actually used */ + unsigned int rtt_point; /* next entry at which to insert */ + unsigned int rtt_usage; /* amount of cache actually used */ suseconds_t rtt_cache[RXRPC_RTT_CACHE_SIZE]; /* calculated RTT cache */ }; @@ -271,7 +271,7 @@ struct rxrpc_connection { } state; int error; /* error code for local abort */ int debug_id; /* debug ID for printks */ - unsigned call_counter; /* call ID counter */ + unsigned int call_counter; /* call ID counter */ atomic_t serial; /* packet serial number counter */ atomic_t hi_serial; /* highest serial number received */ u8 avail_calls; /* number of calls available */ @@ -592,7 +592,7 @@ extern struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *, /* * debug tracing */ -extern unsigned rxrpc_debug; +extern unsigned int rxrpc_debug; #define dbgprintk(FMT,...) \ printk("[%-6.6s] "FMT"\n", current->comm ,##__VA_ARGS__) diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index ae3a035f5390..8b1f9f49960f 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -82,7 +82,7 @@ static int rxrpc_vet_description_s(const char *desc) * - the caller guarantees we have at least 4 words */ static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr, - unsigned toklen) + unsigned int toklen) { struct rxrpc_key_token *token, **pptoken; size_t plen; @@ -210,10 +210,10 @@ static void rxrpc_rxk5_free(struct rxk5_key *rxk5) */ static int rxrpc_krb5_decode_principal(struct krb5_principal *princ, const __be32 **_xdr, - unsigned *_toklen) + unsigned int *_toklen) { const __be32 *xdr = *_xdr; - unsigned toklen = *_toklen, n_parts, loop, tmp; + unsigned int toklen = *_toklen, n_parts, loop, tmp; /* there must be at least one name, and at least #names+1 length * words */ @@ -286,10 +286,10 @@ static int rxrpc_krb5_decode_principal(struct krb5_principal *princ, static int rxrpc_krb5_decode_tagged_data(struct krb5_tagged_data *td, size_t max_data_size, const __be32 **_xdr, - unsigned *_toklen) + unsigned int *_toklen) { const __be32 *xdr = *_xdr; - unsigned toklen = *_toklen, len; + unsigned int toklen = *_toklen, len; /* there must be at least one tag and one length word */ if (toklen <= 8) @@ -330,11 +330,11 @@ static int rxrpc_krb5_decode_tagged_array(struct krb5_tagged_data **_td, u8 max_n_elem, size_t max_elem_size, const __be32 **_xdr, - unsigned *_toklen) + unsigned int *_toklen) { struct krb5_tagged_data *td; const __be32 *xdr = *_xdr; - unsigned toklen = *_toklen, n_elem, loop; + unsigned int toklen = *_toklen, n_elem, loop; int ret; /* there must be at least one count */ @@ -380,10 +380,10 @@ static int rxrpc_krb5_decode_tagged_array(struct krb5_tagged_data **_td, * extract a krb5 ticket */ static int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen, - const __be32 **_xdr, unsigned *_toklen) + const __be32 **_xdr, unsigned int *_toklen) { const __be32 *xdr = *_xdr; - unsigned toklen = *_toklen, len; + unsigned int toklen = *_toklen, len; /* there must be at least one length word */ if (toklen <= 4) @@ -419,7 +419,7 @@ static int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen, * - the caller guarantees we have at least 4 words */ static int rxrpc_instantiate_xdr_rxk5(struct key *key, const __be32 *xdr, - unsigned toklen) + unsigned int toklen) { struct rxrpc_key_token *token, **pptoken; struct rxk5_key *rxk5; @@ -549,7 +549,7 @@ static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datal { const __be32 *xdr = data, *token; const char *cp; - unsigned len, tmp, loop, ntoken, toklen, sec_ix; + unsigned int len, tmp, loop, ntoken, toklen, sec_ix; int ret; _enter(",{%x,%x,%x,%x},%zu", diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 7635107726ce..f226709ebd8f 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -31,7 +31,7 @@ #define REALM_SZ 40 /* size of principal's auth domain */ #define SNAME_SZ 40 /* size of service name */ -unsigned rxrpc_debug; +unsigned int rxrpc_debug; module_param_named(debug, rxrpc_debug, uint, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(debug, "rxkad debugging mask"); @@ -207,7 +207,7 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, struct rxrpc_crypt iv; struct scatterlist sg[16]; struct sk_buff *trailer; - unsigned len; + unsigned int len; u16 check; int nsg; @@ -826,7 +826,7 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, struct rxrpc_crypt iv, key; struct scatterlist sg[1]; struct in_addr addr; - unsigned life; + unsigned int life; time_t issue, now; bool little_endian; int ret; diff --git a/net/sctp/associola.c b/net/sctp/associola.c index acd2edbc073e..5bc9ab161b37 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1408,7 +1408,7 @@ static inline int sctp_peer_needs_update(struct sctp_association *asoc) } /* Increase asoc's rwnd by len and send any window update SACK if needed. */ -void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len) +void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned int len) { struct sctp_chunk *sack; struct timer_list *timer; @@ -1465,7 +1465,7 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len) } /* Decrease asoc's rwnd by len. */ -void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned len) +void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned int len) { int rx_count; int over = 0; diff --git a/net/sctp/output.c b/net/sctp/output.c index 817174eb5f41..69534c5f8afa 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -663,8 +663,8 @@ static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet, */ if (!sctp_sk(asoc->base.sk)->nodelay && sctp_packet_empty(packet) && inflight && sctp_state(asoc, ESTABLISHED)) { - unsigned max = transport->pathmtu - packet->overhead; - unsigned len = chunk->skb->len + q->out_qlen; + unsigned int max = transport->pathmtu - packet->overhead; + unsigned int len = chunk->skb->len + q->out_qlen; /* Check whether this chunk and all the rest of pending * data will fit or delay in hopes of bundling a full diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index cfeb1d4a1ee6..a0fa19f5650c 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -1147,7 +1147,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack) __u32 sack_ctsn, ctsn, tsn; __u32 highest_tsn, highest_new_tsn; __u32 sack_a_rwnd; - unsigned outstanding; + unsigned int outstanding; struct sctp_transport *primary = asoc->peer.primary_path; int count_of_newacks = 0; int gap_ack_blocks; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 1ff51c9d18d5..fbb374c65945 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -524,7 +524,7 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, /* Worker routine to handle INIT command failure. */ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, struct sctp_association *asoc, - unsigned error) + unsigned int error) { struct sctp_ulpevent *event; @@ -550,7 +550,7 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, sctp_event_t event_type, sctp_subtype_t subtype, struct sctp_chunk *chunk, - unsigned error) + unsigned int error) { struct sctp_ulpevent *event; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 891f5db8cc31..a147b4d307d2 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2410,7 +2410,7 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; - unsigned len; + unsigned int len; __be16 error = SCTP_ERROR_NO_ERROR; /* See if we have an error cause code in the chunk. */ @@ -2446,7 +2446,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; - unsigned len; + unsigned int len; __be16 error = SCTP_ERROR_NO_ERROR; if (!sctp_vtag_verify_either(chunk, asoc)) diff --git a/net/socket.c b/net/socket.c index 851edcd6b098..d6c1af9ae7b2 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1479,7 +1479,7 @@ SYSCALL_DEFINE2(listen, int, fd, int, backlog) sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn; - if ((unsigned)backlog > somaxconn) + if ((unsigned int)backlog > somaxconn) backlog = somaxconn; err = security_socket_listen(sock, backlog); @@ -1691,7 +1691,7 @@ SYSCALL_DEFINE3(getpeername, int, fd, struct sockaddr __user *, usockaddr, */ SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len, - unsigned, flags, struct sockaddr __user *, addr, + unsigned int, flags, struct sockaddr __user *, addr, int, addr_len) { struct socket *sock; @@ -1738,7 +1738,7 @@ out: */ SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len, - unsigned, flags) + unsigned int, flags) { return sys_sendto(fd, buff, len, flags, NULL, 0); } @@ -1750,7 +1750,7 @@ SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len, */ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size, - unsigned, flags, struct sockaddr __user *, addr, + unsigned int, flags, struct sockaddr __user *, addr, int __user *, addr_len) { struct socket *sock; @@ -1795,7 +1795,7 @@ out: */ asmlinkage long sys_recv(int fd, void __user *ubuf, size_t size, - unsigned flags) + unsigned int flags) { return sys_recvfrom(fd, ubuf, size, flags, NULL, NULL); } @@ -1897,7 +1897,7 @@ struct used_address { }; static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg, - struct msghdr *msg_sys, unsigned flags, + struct msghdr *msg_sys, unsigned int flags, struct used_address *used_address) { struct compat_msghdr __user *msg_compat = @@ -2014,7 +2014,7 @@ out: * BSD sendmsg interface */ -SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags) +SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned int, flags) { int fput_needed, err; struct msghdr msg_sys; @@ -2096,7 +2096,7 @@ SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg, } static int __sys_recvmsg(struct socket *sock, struct msghdr __user *msg, - struct msghdr *msg_sys, unsigned flags, int nosec) + struct msghdr *msg_sys, unsigned int flags, int nosec) { struct compat_msghdr __user *msg_compat = (struct compat_msghdr __user *)msg; @@ -3223,7 +3223,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, return -ENOIOCTLCMD; } -static long compat_sock_ioctl(struct file *file, unsigned cmd, +static long compat_sock_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct socket *sock = file->private_data; diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 8eff8c32d1b9..d3611f11a8df 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -624,7 +624,7 @@ gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx, ctx->seq_send = ctx->seq_send64; if (ctx->seq_send64 != ctx->seq_send) { dprintk("%s: seq_send64 %lx, seq_send %x overflow?\n", __func__, - (long unsigned)ctx->seq_send64, ctx->seq_send); + (unsigned long)ctx->seq_send64, ctx->seq_send); p = ERR_PTR(-EINVAL); goto out_err; } diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index de0b0f39d9d8..47ad2666fdf6 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1273,7 +1273,7 @@ static void *c_start(struct seq_file *m, loff_t *pos) __acquires(cd->hash_lock) { loff_t n = *pos; - unsigned hash, entry; + unsigned int hash, entry; struct cache_head *ch; struct cache_detail *cd = ((struct handle*)m->private)->cd; diff --git a/net/sunrpc/timer.c b/net/sunrpc/timer.c index dd824341c349..08881d0c9672 100644 --- a/net/sunrpc/timer.c +++ b/net/sunrpc/timer.c @@ -34,7 +34,7 @@ void rpc_init_rtt(struct rpc_rtt *rt, unsigned long timeo) { unsigned long init = 0; - unsigned i; + unsigned int i; rt->timeo = timeo; @@ -57,7 +57,7 @@ EXPORT_SYMBOL_GPL(rpc_init_rtt); * NB: When computing the smoothed RTT and standard deviation, * be careful not to produce negative intermediate results. */ -void rpc_update_rtt(struct rpc_rtt *rt, unsigned timer, long m) +void rpc_update_rtt(struct rpc_rtt *rt, unsigned int timer, long m) { long *srtt, *sdrtt; @@ -106,7 +106,7 @@ EXPORT_SYMBOL_GPL(rpc_update_rtt); * read, write, commit - A+4D * other - timeo */ -unsigned long rpc_calc_rto(struct rpc_rtt *rt, unsigned timer) +unsigned long rpc_calc_rto(struct rpc_rtt *rt, unsigned int timer) { unsigned long res; diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index b97a3dd9a60a..fddcccfcdf76 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -1204,7 +1204,7 @@ xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data) { int i, ret = 0; - unsigned page_len, thislen, page_offset; + unsigned int page_len, thislen, page_offset; struct scatterlist sg[1]; sg_init_table(sg, 1); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 0cbcd1ab49ab..6fe2dcead150 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -783,7 +783,7 @@ static void xprt_update_rtt(struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; struct rpc_rtt *rtt = task->tk_client->cl_rtt; - unsigned timer = task->tk_msg.rpc_proc->p_timer; + unsigned int timer = task->tk_msg.rpc_proc->p_timer; long m = usecs_to_jiffies(ktime_to_us(req->rq_rtt)); if (timer) { diff --git a/net/tipc/link.c b/net/tipc/link.c index b4b9b30167a3..33cecd6fc37d 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -612,7 +612,7 @@ static void link_activate(struct tipc_link *l_ptr) * @event: state machine event to process */ -static void link_state_event(struct tipc_link *l_ptr, unsigned event) +static void link_state_event(struct tipc_link *l_ptr, unsigned int event) { struct tipc_link *other; u32 cont_intv = l_ptr->continuity_interval; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 29e957f64458..bcb3314ef164 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -535,7 +535,7 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, (dest->family != AF_TIPC))) return -EINVAL; if ((total_len > TIPC_MAX_USER_MSG_SIZE) || - (m->msg_iovlen > (unsigned)INT_MAX)) + (m->msg_iovlen > (unsigned int)INT_MAX)) return -EMSGSIZE; if (iocb) @@ -647,7 +647,7 @@ static int send_packet(struct kiocb *iocb, struct socket *sock, return send_msg(iocb, sock, m, total_len); if ((total_len > TIPC_MAX_USER_MSG_SIZE) || - (m->msg_iovlen > (unsigned)INT_MAX)) + (m->msg_iovlen > (unsigned int)INT_MAX)) return -EMSGSIZE; if (iocb) @@ -734,8 +734,8 @@ static int send_stream(struct kiocb *iocb, struct socket *sock, goto exit; } - if ((total_len > (unsigned)INT_MAX) || - (m->msg_iovlen > (unsigned)INT_MAX)) { + if ((total_len > (unsigned int)INT_MAX) || + (m->msg_iovlen > (unsigned int)INT_MAX)) { res = -EMSGSIZE; goto exit; } diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index eadb9020cd64..641f2e47f165 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -149,9 +149,10 @@ static inline void unix_set_secdata(struct scm_cookie *scm, struct sk_buff *skb) * each socket state is protected by separate spin lock. */ -static inline unsigned unix_hash_fold(__wsum n) +static inline unsigned int unix_hash_fold(__wsum n) { - unsigned hash = (__force unsigned)n; + unsigned int hash = (__force unsigned int)n; + hash ^= hash>>16; hash ^= hash>>8; return hash&(UNIX_HASH_SIZE-1); @@ -200,7 +201,7 @@ static inline void unix_release_addr(struct unix_address *addr) * - if started by zero, it is abstract name. */ -static int unix_mkname(struct sockaddr_un *sunaddr, int len, unsigned *hashp) +static int unix_mkname(struct sockaddr_un *sunaddr, int len, unsigned int *hashp) { if (len <= sizeof(short) || len > sizeof(*sunaddr)) return -EINVAL; @@ -250,7 +251,7 @@ static inline void unix_insert_socket(struct hlist_head *list, struct sock *sk) static struct sock *__unix_find_socket_byname(struct net *net, struct sockaddr_un *sunname, - int len, int type, unsigned hash) + int len, int type, unsigned int hash) { struct sock *s; struct hlist_node *node; @@ -273,7 +274,7 @@ found: static inline struct sock *unix_find_socket_byname(struct net *net, struct sockaddr_un *sunname, int len, int type, - unsigned hash) + unsigned int hash) { struct sock *s; @@ -760,7 +761,7 @@ out: mutex_unlock(&u->readlock); static struct sock *unix_find_other(struct net *net, struct sockaddr_un *sunname, int len, - int type, unsigned hash, int *error) + int type, unsigned int hash, int *error) { struct sock *u; struct path path; @@ -824,7 +825,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct dentry *dentry = NULL; struct path path; int err; - unsigned hash; + unsigned int hash; struct unix_address *addr; struct hlist_head *list; @@ -964,7 +965,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, struct net *net = sock_net(sk); struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr; struct sock *other; - unsigned hash; + unsigned int hash; int err; if (addr->sa_family != AF_UNSPEC) { @@ -1062,7 +1063,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, struct sock *newsk = NULL; struct sock *other = NULL; struct sk_buff *skb = NULL; - unsigned hash; + unsigned int hash; int st; int err; long timeo; @@ -1437,7 +1438,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, struct sock *other = NULL; int namelen = 0; /* fake GCC */ int err; - unsigned hash; + unsigned int hash; struct sk_buff *skb; long timeo; struct scm_cookie tmp_scm; diff --git a/net/wimax/stack.c b/net/wimax/stack.c index 3c65eae701c4..a6470ac39498 100644 --- a/net/wimax/stack.c +++ b/net/wimax/stack.c @@ -187,7 +187,7 @@ out: static void __check_new_state(enum wimax_st old_state, enum wimax_st new_state, - unsigned allowed_states_bm) + unsigned int allowed_states_bm) { if (WARN_ON(((1 << new_state) & allowed_states_bm) == 0)) { printk(KERN_ERR "SW BUG! Forbidden state change %u -> %u\n", @@ -425,7 +425,8 @@ static size_t wimax_addr_scnprint(char *addr_str, size_t addr_str_size, unsigned char *addr, size_t addr_len) { - unsigned cnt, total; + unsigned int cnt, total; + for (total = cnt = 0; cnt < addr_len; cnt++) total += scnprintf(addr_str + total, addr_str_size - total, "%02x%c", addr[cnt], diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index 22adfebaad27..b0eb7aa49b60 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -256,7 +256,7 @@ static const struct iw_ioctl_description standard_ioctl[] = { .max_tokens = sizeof(struct iw_pmksa), }, }; -static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl); +static const unsigned int standard_ioctl_num = ARRAY_SIZE(standard_ioctl); /* * Meta-data about all the additional standard Wireless Extension events @@ -306,7 +306,7 @@ static const struct iw_ioctl_description standard_event[] = { .max_tokens = sizeof(struct iw_pmkid_cand), }, }; -static const unsigned standard_event_num = ARRAY_SIZE(standard_event); +static const unsigned int standard_event_num = ARRAY_SIZE(standard_event); /* Size (in bytes) of various events */ static const int event_type_size[] = { @@ -429,7 +429,7 @@ void wireless_send_event(struct net_device * dev, int hdr_len; /* Size of the event header */ int wrqu_off = 0; /* Offset in wrqu */ /* Don't "optimise" the following variable, it will crash */ - unsigned cmd_index; /* *MUST* be unsigned */ + unsigned int cmd_index; /* *MUST* be unsigned */ struct sk_buff *skb; struct nlmsghdr *nlh; struct nlattr *nla; diff --git a/net/x25/x25_facilities.c b/net/x25/x25_facilities.c index 36384a1fa9f2..66c638730c7a 100644 --- a/net/x25/x25_facilities.c +++ b/net/x25/x25_facilities.c @@ -231,7 +231,7 @@ int x25_create_facilities(unsigned char *buffer, } if (dte_facs->calling_len && (facil_mask & X25_MASK_CALLING_AE)) { - unsigned bytecount = (dte_facs->calling_len + 1) >> 1; + unsigned int bytecount = (dte_facs->calling_len + 1) >> 1; *p++ = X25_FAC_CALLING_AE; *p++ = 1 + bytecount; *p++ = dte_facs->calling_len; @@ -240,7 +240,7 @@ int x25_create_facilities(unsigned char *buffer, } if (dte_facs->called_len && (facil_mask & X25_MASK_CALLED_AE)) { - unsigned bytecount = (dte_facs->called_len % 2) ? + unsigned int bytecount = (dte_facs->called_len % 2) ? dte_facs->called_len / 2 + 1 : dte_facs->called_len / 2; *p++ = X25_FAC_CALLED_AE; diff --git a/net/xfrm/xfrm_hash.h b/net/xfrm/xfrm_hash.h index 7199d78b2aa1..716502ada53b 100644 --- a/net/xfrm/xfrm_hash.h +++ b/net/xfrm/xfrm_hash.h @@ -45,10 +45,10 @@ static inline unsigned int __xfrm_dst_hash(const xfrm_address_t *daddr, return (h ^ (h >> 16)) & hmask; } -static inline unsigned __xfrm_src_hash(const xfrm_address_t *daddr, - const xfrm_address_t *saddr, - unsigned short family, - unsigned int hmask) +static inline unsigned int __xfrm_src_hash(const xfrm_address_t *daddr, + const xfrm_address_t *saddr, + unsigned short family, + unsigned int hmask) { unsigned int h = family; switch (family) { -- cgit v1.2.3 From 77162022ab26a1f99d3af30c03760a76f86e193d Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Sun, 15 Apr 2012 06:43:56 +0000 Subject: net: add generic PF_BRIDGE:RTM_ FDB hooks This adds two new flags NTF_MASTER and NTF_SELF that can now be used to specify where PF_BRIDGE netlink commands should be sent. NTF_MASTER sends the commands to the 'dev->master' device for parsing. Typically this will be the linux net/bridge, or open-vswitch devices. Also without any flags set the command will be handled by the master device as well so that current user space tools continue to work as expected. The NTF_SELF flag will push the PF_BRIDGE commands to the device. In the basic example below the commands are then parsed and programmed in the embedded bridge. Note if both NTF_SELF and NTF_MASTER bits are set then the command will be sent to both 'dev->master' and 'dev' this allows user space to easily keep the embedded bridge and software bridge in sync. There is a slight complication in the case with both flags set when an error occurs. To resolve this the rtnl handler clears the NTF_ flag in the netlink ack to indicate which sets completed successfully. The add/del handlers will abort as soon as any error occurs. To support this new net device ops were added to call into the device and the existing bridging code was refactored to use these. There should be no required changes in user space to support the current bridge behavior. A basic setup with a SR-IOV enabled NIC looks like this, veth0 veth2 | | ------------ | bridge0 | <---- software bridging ------------ / / ethx.y ethx VF PF \ \ <---- propagate FDB entries to HW \ \ -------------------- | Embedded Bridge | <---- hardware offloaded switching -------------------- In this case the embedded bridge must be managed to allow 'veth0' to communicate with 'ethx.y' correctly. At present drivers managing the embedded bridge either send frames onto the network which then get dropped by the switch OR the embedded bridge will flood these frames. With this patch we have a mechanism to manage the embedded bridge correctly from user space. This example is specific to SR-IOV but replacing the VF with another PF or dropping this into the DSA framework generates similar management issues. Examples session using the 'br'[1] tool to add, dump and then delete a mac address with a new "embedded" option and enabled ixgbe driver: # br fdb add 22:35:19:ac:60:59 dev eth3 # br fdb port mac addr flags veth0 22:35:19:ac:60:58 static veth0 9a:5f:81:f7:f6:ec local eth3 00:1b:21:55:23:59 local eth3 22:35:19:ac:60:59 static veth0 22:35:19:ac:60:57 static #br fdb add 22:35:19:ac:60:59 embedded dev eth3 #br fdb port mac addr flags veth0 22:35:19:ac:60:58 static veth0 9a:5f:81:f7:f6:ec local eth3 00:1b:21:55:23:59 local eth3 22:35:19:ac:60:59 static veth0 22:35:19:ac:60:57 static eth3 22:35:19:ac:60:59 local embedded #br fdb del 22:35:19:ac:60:59 embedded dev eth3 I added a couple lines to 'br' to set the flags correctly is all. It is my opinion that the merit of this patch is now embedded and SW bridges can both be modeled correctly in user space using very nearly the same message passing. [1] 'br' tool was published as an RFC here and will be renamed 'bridge' http://patchwork.ozlabs.org/patch/117664/ Thanks to Jamal Hadi Salim, Stephen Hemminger and Ben Hutchings for valuable feedback, suggestions, and review. v2: fixed api descriptions and error case with both NTF_SELF and NTF_MASTER set plus updated patch description. Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- include/linux/neighbour.h | 3 + include/linux/netdevice.h | 23 +++++++ include/linux/rtnetlink.h | 4 ++ net/bridge/br_device.c | 3 + net/bridge/br_fdb.c | 128 ++++++++++---------------------------- net/bridge/br_netlink.c | 12 ---- net/bridge/br_private.h | 15 ++++- net/core/rtnetlink.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 228 insertions(+), 112 deletions(-) (limited to 'include/linux') diff --git a/include/linux/neighbour.h b/include/linux/neighbour.h index b188f68a08c9..275e5d65dcb2 100644 --- a/include/linux/neighbour.h +++ b/include/linux/neighbour.h @@ -33,6 +33,9 @@ enum { #define NTF_PROXY 0x08 /* == ATF_PUBL */ #define NTF_ROUTER 0x80 +#define NTF_SELF 0x02 +#define NTF_MASTER 0x04 + /* * Neighbor Cache Entry States. */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d3122321d8b1..100c48cca4fd 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -54,6 +54,7 @@ #include #include +#include struct netpoll_info; struct device; @@ -905,6 +906,16 @@ struct netdev_fcoe_hbainfo { * feature set might be less than what was returned by ndo_fix_features()). * Must return >0 or -errno if it changed dev->features itself. * + * int (*ndo_fdb_add)(struct ndmsg *ndm, struct net_device *dev, + * unsigned char *addr, u16 flags) + * Adds an FDB entry to dev for addr. + * int (*ndo_fdb_del)(struct ndmsg *ndm, struct net_device *dev, + * unsigned char *addr) + * Deletes the FDB entry from dev coresponding to addr. + * int (*ndo_fdb_dump)(struct sk_buff *skb, struct netlink_callback *cb, + * struct net_device *dev, int idx) + * Used to add FDB entries to dump requests. Implementers should add + * entries to skb and update idx with the number of entries. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1002,6 +1013,18 @@ struct net_device_ops { netdev_features_t features); int (*ndo_neigh_construct)(struct neighbour *n); void (*ndo_neigh_destroy)(struct neighbour *n); + + int (*ndo_fdb_add)(struct ndmsg *ndm, + struct net_device *dev, + unsigned char *addr, + u16 flags); + int (*ndo_fdb_del)(struct ndmsg *ndm, + struct net_device *dev, + unsigned char *addr); + int (*ndo_fdb_dump)(struct sk_buff *skb, + struct netlink_callback *cb, + struct net_device *dev, + int idx); }; /* diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 577592ea0ea0..2c1de8982c85 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -801,6 +801,10 @@ rtattr_failure: return table; } +extern int ndo_dflt_fdb_dump(struct sk_buff *skb, + struct netlink_callback *cb, + struct net_device *dev, + int idx); #endif /* __KERNEL__ */ diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index ba829de84423..d6e5929458b1 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -317,6 +317,9 @@ static const struct net_device_ops br_netdev_ops = { .ndo_add_slave = br_add_slave, .ndo_del_slave = br_del_slave, .ndo_fix_features = br_fix_features, + .ndo_fdb_add = br_fdb_add, + .ndo_fdb_del = br_fdb_delete, + .ndo_fdb_dump = br_fdb_dump, }; static void br_dev_free(struct net_device *dev) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 80dbce4974ce..5945c54bc2de 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -535,44 +535,38 @@ errout: } /* Dump information about entries, in response to GETNEIGH */ -int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) +int br_fdb_dump(struct sk_buff *skb, + struct netlink_callback *cb, + struct net_device *dev, + int idx) { - struct net *net = sock_net(skb->sk); - struct net_device *dev; - int idx = 0; - - rcu_read_lock(); - for_each_netdev_rcu(net, dev) { - struct net_bridge *br = netdev_priv(dev); - int i; - - if (!(dev->priv_flags & IFF_EBRIDGE)) - continue; + struct net_bridge *br = netdev_priv(dev); + int i; - for (i = 0; i < BR_HASH_SIZE; i++) { - struct hlist_node *h; - struct net_bridge_fdb_entry *f; + if (!(dev->priv_flags & IFF_EBRIDGE)) + goto out; - hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) { - if (idx < cb->args[0]) - goto skip; + for (i = 0; i < BR_HASH_SIZE; i++) { + struct hlist_node *h; + struct net_bridge_fdb_entry *f; - if (fdb_fill_info(skb, br, f, - NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, - RTM_NEWNEIGH, - NLM_F_MULTI) < 0) - break; + hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) { + if (idx < cb->args[0]) + goto skip; + + if (fdb_fill_info(skb, br, f, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, + RTM_NEWNEIGH, + NLM_F_MULTI) < 0) + break; skip: - ++idx; - } + ++idx; } } - rcu_read_unlock(); - - cb->args[0] = idx; - return skb->len; +out: + return idx; } /* Update (create or replace) forwarding database entry */ @@ -614,43 +608,11 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, } /* Add new permanent fdb entry with RTM_NEWNEIGH */ -int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +int br_fdb_add(struct ndmsg *ndm, struct net_device *dev, + unsigned char *addr, u16 nlh_flags) { - struct net *net = sock_net(skb->sk); - struct ndmsg *ndm; - struct nlattr *tb[NDA_MAX+1]; - struct net_device *dev; struct net_bridge_port *p; - const __u8 *addr; - int err; - - ASSERT_RTNL(); - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); - if (err < 0) - return err; - - ndm = nlmsg_data(nlh); - if (ndm->ndm_ifindex == 0) { - pr_info("bridge: RTM_NEWNEIGH with invalid ifindex\n"); - return -EINVAL; - } - - dev = __dev_get_by_index(net, ndm->ndm_ifindex); - if (dev == NULL) { - pr_info("bridge: RTM_NEWNEIGH with unknown ifindex\n"); - return -ENODEV; - } - - if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) { - pr_info("bridge: RTM_NEWNEIGH with invalid address\n"); - return -EINVAL; - } - - addr = nla_data(tb[NDA_LLADDR]); - if (!is_valid_ether_addr(addr)) { - pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n"); - return -EINVAL; - } + int err = 0; if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state); @@ -670,14 +632,14 @@ int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) rcu_read_unlock(); } else { spin_lock_bh(&p->br->hash_lock); - err = fdb_add_entry(p, addr, ndm->ndm_state, nlh->nlmsg_flags); + err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags); spin_unlock_bh(&p->br->hash_lock); } return err; } -static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) +static int fdb_delete_by_addr(struct net_bridge_port *p, u8 *addr) { struct net_bridge *br = p->br; struct hlist_head *head = &br->hash[br_mac_hash(addr)]; @@ -692,40 +654,12 @@ static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) } /* Remove neighbor entry with RTM_DELNEIGH */ -int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev, + unsigned char *addr) { - struct net *net = sock_net(skb->sk); - struct ndmsg *ndm; struct net_bridge_port *p; - struct nlattr *llattr; - const __u8 *addr; - struct net_device *dev; int err; - ASSERT_RTNL(); - if (nlmsg_len(nlh) < sizeof(*ndm)) - return -EINVAL; - - ndm = nlmsg_data(nlh); - if (ndm->ndm_ifindex == 0) { - pr_info("bridge: RTM_DELNEIGH with invalid ifindex\n"); - return -EINVAL; - } - - dev = __dev_get_by_index(net, ndm->ndm_ifindex); - if (dev == NULL) { - pr_info("bridge: RTM_DELNEIGH with unknown ifindex\n"); - return -ENODEV; - } - - llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR); - if (llattr == NULL || nla_len(llattr) != ETH_ALEN) { - pr_info("bridge: RTM_DELNEIGH with invalid address\n"); - return -EINVAL; - } - - addr = nla_data(llattr); - p = br_port_get_rtnl(dev); if (p == NULL) { pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index df38108f6973..2080485515f1 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -232,18 +232,6 @@ int __init br_netlink_init(void) br_rtm_setlink, NULL, NULL); if (err) goto err3; - err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, - br_fdb_add, NULL, NULL); - if (err) - goto err3; - err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, - br_fdb_delete, NULL, NULL); - if (err) - goto err3; - err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, - NULL, br_fdb_dump, NULL); - if (err) - goto err3; return 0; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index f8ffd8c49054..1a8ad4fb9a6b 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -360,9 +360,18 @@ extern int br_fdb_insert(struct net_bridge *br, extern void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr); -extern int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb); -extern int br_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); -extern int br_fdb_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); + +extern int br_fdb_delete(struct ndmsg *ndm, + struct net_device *dev, + unsigned char *addr); +extern int br_fdb_add(struct ndmsg *nlh, + struct net_device *dev, + unsigned char *addr, + u16 nlh_flags); +extern int br_fdb_dump(struct sk_buff *skb, + struct netlink_callback *cb, + struct net_device *dev, + int idx); /* br_forward.c */ extern void br_deliver(const struct net_bridge_port *to, diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 2ff6fe4bada4..b348b7fbf53a 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -35,7 +35,9 @@ #include #include #include +#include #include +#include #include @@ -1978,6 +1980,152 @@ errout: rtnl_set_sk_err(net, RTNLGRP_LINK, err); } +static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +{ + struct net *net = sock_net(skb->sk); + struct net_device *master = NULL; + struct ndmsg *ndm; + struct nlattr *tb[NDA_MAX+1]; + struct net_device *dev; + u8 *addr; + int err; + + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); + if (err < 0) + return err; + + ndm = nlmsg_data(nlh); + if (ndm->ndm_ifindex == 0) { + pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ifindex\n"); + return -EINVAL; + } + + dev = __dev_get_by_index(net, ndm->ndm_ifindex); + if (dev == NULL) { + pr_info("PF_BRIDGE: RTM_NEWNEIGH with unknown ifindex\n"); + return -ENODEV; + } + + if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) { + pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid address\n"); + return -EINVAL; + } + + addr = nla_data(tb[NDA_LLADDR]); + if (!is_valid_ether_addr(addr)) { + pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid ether address\n"); + return -EINVAL; + } + + err = -EOPNOTSUPP; + + /* Support fdb on master device the net/bridge default case */ + if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) && + (dev->priv_flags & IFF_BRIDGE_PORT)) { + master = dev->master; + err = master->netdev_ops->ndo_fdb_add(ndm, dev, addr, + nlh->nlmsg_flags); + if (err) + goto out; + else + ndm->ndm_flags &= ~NTF_MASTER; + } + + /* 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, dev, addr, + nlh->nlmsg_flags); + + if (!err) + ndm->ndm_flags &= ~NTF_SELF; + } +out: + return err; +} + +static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +{ + struct net *net = sock_net(skb->sk); + struct ndmsg *ndm; + struct nlattr *llattr; + struct net_device *dev; + int err = -EINVAL; + __u8 *addr; + + if (nlmsg_len(nlh) < sizeof(*ndm)) + return -EINVAL; + + ndm = nlmsg_data(nlh); + if (ndm->ndm_ifindex == 0) { + pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid ifindex\n"); + return -EINVAL; + } + + dev = __dev_get_by_index(net, ndm->ndm_ifindex); + if (dev == NULL) { + pr_info("PF_BRIDGE: RTM_DELNEIGH with unknown ifindex\n"); + return -ENODEV; + } + + llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR); + if (llattr == NULL || nla_len(llattr) != ETH_ALEN) { + pr_info("PF_BRIGDE: RTM_DELNEIGH with invalid address\n"); + return -EINVAL; + } + + addr = nla_data(llattr); + err = -EOPNOTSUPP; + + /* Support fdb on master device the net/bridge default case */ + if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) && + (dev->priv_flags & IFF_BRIDGE_PORT)) { + struct net_device *master = dev->master; + + if (master->netdev_ops->ndo_fdb_del) + err = master->netdev_ops->ndo_fdb_del(ndm, dev, addr); + + if (err) + goto out; + else + ndm->ndm_flags &= ~NTF_MASTER; + } + + /* 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, dev, addr); + + if (!err) + ndm->ndm_flags &= ~NTF_SELF; + } +out: + return err; +} + +static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int idx = 0; + struct net *net = sock_net(skb->sk); + struct net_device *dev; + + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + if (dev->priv_flags & IFF_BRIDGE_PORT) { + struct net_device *master = dev->master; + const struct net_device_ops *ops = master->netdev_ops; + + if (ops->ndo_fdb_dump) + idx = ops->ndo_fdb_dump(skb, cb, dev, idx); + } + + if (dev->netdev_ops->ndo_fdb_dump) + idx = dev->netdev_ops->ndo_fdb_dump(skb, cb, dev, idx); + } + rcu_read_unlock(); + + cb->args[0] = idx; + return skb->len; +} + /* Protected by RTNL sempahore. */ static struct rtattr **rta_buf; static int rtattr_max; @@ -2150,5 +2298,9 @@ void __init rtnetlink_init(void) rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, NULL); rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, NULL); + + rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL); + rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL); + rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); } -- cgit v1.2.3 From 12a94634453c61fd9a11c4702002e3db6d4feb70 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Sun, 15 Apr 2012 06:44:02 +0000 Subject: net: addr_list: add exclusive dev_uc_add and dev_mc_add This adds a dev_uc_add_excl() and dev_mc_add_excl() calls similar to the original dev_{uc|mc}_add() except it sets the global bit and returns -EEXIST for duplicat entires. This is useful for drivers that support SR-IOV, macvlan devices and any other devices that need to manage the unicast and multicast lists. v2: fix typo UNICAST should be MULTICAST in dev_mc_add_excl() CC: Ben Hutchings Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 + net/core/dev_addr_lists.c | 97 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 83 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 100c48cca4fd..e0b70e961e61 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2569,6 +2569,7 @@ extern int dev_addr_init(struct net_device *dev); /* Functions used for unicast addresses handling */ extern int dev_uc_add(struct net_device *dev, unsigned char *addr); +extern int dev_uc_add_excl(struct net_device *dev, unsigned char *addr); extern int dev_uc_del(struct net_device *dev, unsigned char *addr); extern int dev_uc_sync(struct net_device *to, struct net_device *from); extern void dev_uc_unsync(struct net_device *to, struct net_device *from); @@ -2578,6 +2579,7 @@ extern void dev_uc_init(struct net_device *dev); /* Functions used for multicast addresses handling */ extern int dev_mc_add(struct net_device *dev, unsigned char *addr); extern int dev_mc_add_global(struct net_device *dev, unsigned char *addr); +extern int dev_mc_add_excl(struct net_device *dev, unsigned char *addr); extern int dev_mc_del(struct net_device *dev, unsigned char *addr); extern int dev_mc_del_global(struct net_device *dev, unsigned char *addr); extern int dev_mc_sync(struct net_device *to, struct net_device *from); diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 626698f0db8b..c4cc2bc49f06 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -21,12 +21,35 @@ * General list handling functions */ +static int __hw_addr_create_ex(struct netdev_hw_addr_list *list, + unsigned char *addr, int addr_len, + unsigned char addr_type, bool global) +{ + struct netdev_hw_addr *ha; + int alloc_size; + + alloc_size = sizeof(*ha); + if (alloc_size < L1_CACHE_BYTES) + alloc_size = L1_CACHE_BYTES; + ha = kmalloc(alloc_size, GFP_ATOMIC); + if (!ha) + return -ENOMEM; + memcpy(ha->addr, addr, addr_len); + ha->type = addr_type; + ha->refcount = 1; + ha->global_use = global; + ha->synced = false; + list_add_tail_rcu(&ha->list, &list->list); + list->count++; + + return 0; +} + static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, unsigned char *addr, int addr_len, unsigned char addr_type, bool global) { struct netdev_hw_addr *ha; - int alloc_size; if (addr_len > MAX_ADDR_LEN) return -EINVAL; @@ -46,21 +69,7 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, } } - - alloc_size = sizeof(*ha); - if (alloc_size < L1_CACHE_BYTES) - alloc_size = L1_CACHE_BYTES; - ha = kmalloc(alloc_size, GFP_ATOMIC); - if (!ha) - return -ENOMEM; - memcpy(ha->addr, addr, addr_len); - ha->type = addr_type; - ha->refcount = 1; - ha->global_use = global; - ha->synced = false; - list_add_tail_rcu(&ha->list, &list->list); - list->count++; - return 0; + return __hw_addr_create_ex(list, addr, addr_len, addr_type, global); } static int __hw_addr_add(struct netdev_hw_addr_list *list, unsigned char *addr, @@ -376,6 +385,34 @@ EXPORT_SYMBOL(dev_addr_del_multiple); * Unicast list handling functions */ +/** + * dev_uc_add_excl - Add a global secondary unicast address + * @dev: device + * @addr: address to add + */ +int dev_uc_add_excl(struct net_device *dev, unsigned char *addr) +{ + struct netdev_hw_addr *ha; + int err; + + netif_addr_lock_bh(dev); + list_for_each_entry(ha, &dev->uc.list, list) { + if (!memcmp(ha->addr, addr, dev->addr_len) && + ha->type == NETDEV_HW_ADDR_T_UNICAST) { + err = -EEXIST; + goto out; + } + } + err = __hw_addr_create_ex(&dev->uc, addr, dev->addr_len, + NETDEV_HW_ADDR_T_UNICAST, true); + if (!err) + __dev_set_rx_mode(dev); +out: + netif_addr_unlock_bh(dev); + return err; +} +EXPORT_SYMBOL(dev_uc_add_excl); + /** * dev_uc_add - Add a secondary unicast address * @dev: device @@ -501,6 +538,34 @@ EXPORT_SYMBOL(dev_uc_init); * Multicast list handling functions */ +/** + * dev_mc_add_excl - Add a global secondary multicast address + * @dev: device + * @addr: address to add + */ +int dev_mc_add_excl(struct net_device *dev, unsigned char *addr) +{ + struct netdev_hw_addr *ha; + int err; + + netif_addr_lock_bh(dev); + list_for_each_entry(ha, &dev->mc.list, list) { + if (!memcmp(ha->addr, addr, dev->addr_len) && + ha->type == NETDEV_HW_ADDR_T_MULTICAST) { + err = -EEXIST; + goto out; + } + } + err = __hw_addr_create_ex(&dev->mc, addr, dev->addr_len, + NETDEV_HW_ADDR_T_MULTICAST, true); + if (!err) + __dev_set_rx_mode(dev); +out: + netif_addr_unlock_bh(dev); + return err; +} +EXPORT_SYMBOL(dev_mc_add_excl); + static int __dev_mc_add(struct net_device *dev, unsigned char *addr, bool global) { -- cgit v1.2.3 From df8ef8f3aaa6692970a436204c4429210addb23a Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Sun, 15 Apr 2012 06:44:37 +0000 Subject: macvlan: add FDB bridge ops and macvlan flags This adds FDB bridge ops to the macvlan device passthru mode. Additionally a flags field was added and a NOPROMISC bit to allow users to use passthru mode without the driver calling dev_set_promiscuity(). The flags field is a u16 placed in a 4 byte hole (consuming 2 bytes) of the macvlan_dev struct. We want to do this so that the macvlan driver or stack above the macvlan driver does not have to process every packet. For the use case where we know all the MAC addresses of the endstations above us this works well. This patch is a result of Roopa Prabhu's work. Follow up patches are needed for VEPA and VEB macvlan modes. v2: Change from distinct nopromisc mode to a flags field to configure this. This avoids the tendency to add a new mode every time we need some slightly different behavior. v3: fix error in dev_set_promiscuity and add change and get link attributes for flags. CC: Roopa Prabhu CC: Michael S. Tsirkin Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 73 ++++++++++++++++++++++++++++++++++++++++++---- include/linux/if_link.h | 3 ++ include/linux/if_macvlan.h | 1 + 3 files changed, 71 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index b17fc9007099..9653ed6998fe 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -312,7 +312,8 @@ static int macvlan_open(struct net_device *dev) int err; if (vlan->port->passthru) { - dev_set_promiscuity(lowerdev, 1); + if (!(vlan->flags & MACVLAN_FLAG_NOPROMISC)) + dev_set_promiscuity(lowerdev, 1); goto hash_add; } @@ -344,12 +345,15 @@ static int macvlan_stop(struct net_device *dev) struct macvlan_dev *vlan = netdev_priv(dev); struct net_device *lowerdev = vlan->lowerdev; + dev_uc_unsync(lowerdev, dev); + dev_mc_unsync(lowerdev, dev); + if (vlan->port->passthru) { - dev_set_promiscuity(lowerdev, -1); + if (!(vlan->flags & MACVLAN_FLAG_NOPROMISC)) + dev_set_promiscuity(lowerdev, -1); goto hash_del; } - dev_mc_unsync(lowerdev, dev); if (dev->flags & IFF_ALLMULTI) dev_set_allmulti(lowerdev, -1); @@ -399,10 +403,11 @@ static void macvlan_change_rx_flags(struct net_device *dev, int change) dev_set_allmulti(lowerdev, dev->flags & IFF_ALLMULTI ? 1 : -1); } -static void macvlan_set_multicast_list(struct net_device *dev) +static void macvlan_set_mac_lists(struct net_device *dev) { struct macvlan_dev *vlan = netdev_priv(dev); + dev_uc_sync(vlan->lowerdev, dev); dev_mc_sync(vlan->lowerdev, dev); } @@ -542,6 +547,43 @@ static int macvlan_vlan_rx_kill_vid(struct net_device *dev, return 0; } +static int macvlan_fdb_add(struct ndmsg *ndm, + struct net_device *dev, + unsigned char *addr, + u16 flags) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + int err = -EINVAL; + + if (!vlan->port->passthru) + return -EOPNOTSUPP; + + if (is_unicast_ether_addr(addr)) + err = dev_uc_add_excl(dev, addr); + else if (is_multicast_ether_addr(addr)) + err = dev_mc_add_excl(dev, addr); + + return err; +} + +static int macvlan_fdb_del(struct ndmsg *ndm, + struct net_device *dev, + unsigned char *addr) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + int err = -EINVAL; + + if (!vlan->port->passthru) + return -EOPNOTSUPP; + + if (is_unicast_ether_addr(addr)) + err = dev_uc_del(dev, addr); + else if (is_multicast_ether_addr(addr)) + err = dev_mc_del(dev, addr); + + return err; +} + static void macvlan_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { @@ -572,11 +614,14 @@ static const struct net_device_ops macvlan_netdev_ops = { .ndo_change_mtu = macvlan_change_mtu, .ndo_change_rx_flags = macvlan_change_rx_flags, .ndo_set_mac_address = macvlan_set_mac_address, - .ndo_set_rx_mode = macvlan_set_multicast_list, + .ndo_set_rx_mode = macvlan_set_mac_lists, .ndo_get_stats64 = macvlan_dev_get_stats64, .ndo_validate_addr = eth_validate_addr, .ndo_vlan_rx_add_vid = macvlan_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = macvlan_vlan_rx_kill_vid, + .ndo_fdb_add = macvlan_fdb_add, + .ndo_fdb_del = macvlan_fdb_del, + .ndo_fdb_dump = ndo_dflt_fdb_dump, }; void macvlan_common_setup(struct net_device *dev) @@ -711,6 +756,9 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, if (data && data[IFLA_MACVLAN_MODE]) vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); + if (data && data[IFLA_MACVLAN_FLAGS]) + vlan->flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]); + if (vlan->mode == MACVLAN_MODE_PASSTHRU) { if (port->count) return -EINVAL; @@ -760,6 +808,16 @@ static int macvlan_changelink(struct net_device *dev, struct macvlan_dev *vlan = netdev_priv(dev); if (data && data[IFLA_MACVLAN_MODE]) vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); + if (data && data[IFLA_MACVLAN_FLAGS]) { + __u16 flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]); + bool promisc = (flags ^ vlan->flags) & MACVLAN_FLAG_NOPROMISC; + + if (promisc && (flags & MACVLAN_FLAG_NOPROMISC)) + dev_set_promiscuity(vlan->lowerdev, -1); + else if (promisc && !(flags & MACVLAN_FLAG_NOPROMISC)) + dev_set_promiscuity(vlan->lowerdev, 1); + vlan->flags = flags; + } return 0; } @@ -775,6 +833,8 @@ static int macvlan_fill_info(struct sk_buff *skb, if (nla_put_u32(skb, IFLA_MACVLAN_MODE, vlan->mode)) goto nla_put_failure; + if (nla_put_u16(skb, IFLA_MACVLAN_FLAGS, vlan->flags)) + goto nla_put_failure; return 0; nla_put_failure: @@ -782,7 +842,8 @@ nla_put_failure: } static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = { - [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, + [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, + [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, }; int macvlan_link_register(struct rtnl_link_ops *ops) diff --git a/include/linux/if_link.h b/include/linux/if_link.h index 2f4fa93454c7..f715750d0b87 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h @@ -255,6 +255,7 @@ struct ifla_vlan_qos_mapping { enum { IFLA_MACVLAN_UNSPEC, IFLA_MACVLAN_MODE, + IFLA_MACVLAN_FLAGS, __IFLA_MACVLAN_MAX, }; @@ -267,6 +268,8 @@ enum macvlan_mode { MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */ }; +#define MACVLAN_FLAG_NOPROMISC 1 + /* SR-IOV virtual function management section */ enum { diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index d103dca5c563..f65e8d250f7e 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -60,6 +60,7 @@ struct macvlan_dev { struct net_device *lowerdev; struct macvlan_pcpu_stats __percpu *pcpu_stats; enum macvlan_mode mode; + u16 flags; int (*receive)(struct sk_buff *skb); int (*forward)(struct net_device *dev, struct sk_buff *skb); struct macvtap_queue *taps[MAX_MACVTAP_QUEUES]; -- cgit v1.2.3 From dcca1cfc0978fa084245937ec1472b870edde97d Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Thu, 12 Apr 2012 14:32:20 -0700 Subject: mac80211: Set the correct values for hwmp (1) and airtimeLinkMetric (1) Per sections 8.4.2.100.2 and 8.4.2.100.3 of Std 802.11-2012 Reported-by: Shinichi Hotori Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index db84e2f6b289..ce9af8918514 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1458,7 +1458,7 @@ enum { * be specified in a vendor specific information element */ enum { - IEEE80211_PATH_PROTOCOL_HWMP = 0, + IEEE80211_PATH_PROTOCOL_HWMP = 1, IEEE80211_PATH_PROTOCOL_VENDOR = 255, }; @@ -1470,7 +1470,7 @@ enum { * specified in a vendor specific information element */ enum { - IEEE80211_PATH_METRIC_AIRTIME = 0, + IEEE80211_PATH_METRIC_AIRTIME = 1, IEEE80211_PATH_METRIC_VENDOR = 255, }; -- cgit v1.2.3 From 98d8618af37728f6e18e84110ddb99987b47dd12 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 24 Mar 2012 15:19:49 +0530 Subject: mfd: add irq domain support for max8997 interrupts Add irq domain support for max8997 interrupts. The reverse mapping method used is linear mapping since the sub-drivers of max8997 such as regulator and charger drivers can use the max8997 irq_domain to get the linux irq number for max8997 interrupts. All uses of irq_base in platform data and max8997 driver private data are removed. Signed-off-by: Thomas Abraham Acked-by: MyungJoo Ham Acked-by: Grant Likely Acked-by: Samuel Ortiz Signed-off-by: Mark Brown --- arch/arm/mach-exynos/mach-nuri.c | 4 --- arch/arm/mach-exynos/mach-origen.c | 1 - drivers/mfd/max8997-irq.c | 61 ++++++++++++++++++++++--------------- drivers/mfd/max8997.c | 1 - include/linux/mfd/max8997-private.h | 4 ++- include/linux/mfd/max8997.h | 1 - 6 files changed, 39 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index b3982c867c9c..0b48d6a8cf03 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -1079,12 +1079,8 @@ static struct platform_device nuri_max8903_device = { static void __init nuri_power_init(void) { int gpio; - int irq_base = IRQ_GPIO_END + 1; int ta_en = 0; - nuri_max8997_pdata.irq_base = irq_base; - irq_base += MAX8997_IRQ_NR; - gpio = EXYNOS4_GPX0(7); gpio_request(gpio, "AP_PMIC_IRQ"); s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf)); diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index 878d4c99142d..600368fa6b69 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -424,7 +424,6 @@ static struct max8997_platform_data __initdata origen_max8997_pdata = { .buck1_gpiodvs = false, .buck2_gpiodvs = false, .buck5_gpiodvs = false, - .irq_base = IRQ_GPIO_END + 1, .ignore_gpiodvs_side_effect = true, .buck125_default_idx = 0x0, diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c index 09274cf7c33b..00390a117ae6 100644 --- a/drivers/mfd/max8997-irq.c +++ b/drivers/mfd/max8997-irq.c @@ -142,7 +142,8 @@ static void max8997_irq_sync_unlock(struct irq_data *data) static const inline struct max8997_irq_data * irq_to_max8997_irq(struct max8997_dev *max8997, int irq) { - return &max8997_irqs[irq - max8997->irq_base]; + struct irq_data *data = irq_get_irq_data(irq); + return &max8997_irqs[data->hwirq]; } static void max8997_irq_mask(struct irq_data *data) @@ -182,7 +183,7 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {}; u8 irq_src; int ret; - int i; + int i, cur_irq; ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src); if (ret < 0) { @@ -269,8 +270,11 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) /* Report */ for (i = 0; i < MAX8997_IRQ_NR; i++) { - if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) - handle_nested_irq(max8997->irq_base + i); + if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) { + cur_irq = irq_find_mapping(max8997->irq_domain, i); + if (cur_irq) + handle_nested_irq(cur_irq); + } } return IRQ_HANDLED; @@ -278,26 +282,40 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) int max8997_irq_resume(struct max8997_dev *max8997) { - if (max8997->irq && max8997->irq_base) - max8997_irq_thread(max8997->irq_base, max8997); + if (max8997->irq && max8997->irq_domain) + max8997_irq_thread(0, max8997); + return 0; +} + +static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct max8997_dev *max8997 = d->host_data; + + irq_set_chip_data(irq, max8997); + irq_set_chip_and_handler(irq, &max8997_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif return 0; } +static struct irq_domain_ops max8997_irq_domain_ops = { + .map = max8997_irq_domain_map, +}; + int max8997_irq_init(struct max8997_dev *max8997) { + struct irq_domain *domain; int i; - int cur_irq; int ret; u8 val; if (!max8997->irq) { dev_warn(max8997->dev, "No interrupt specified.\n"); - max8997->irq_base = 0; - return 0; - } - - if (!max8997->irq_base) { - dev_err(max8997->dev, "No interrupt base specified.\n"); return 0; } @@ -327,18 +345,11 @@ int max8997_irq_init(struct max8997_dev *max8997) true : false; } - /* Register with genirq */ - for (i = 0; i < MAX8997_IRQ_NR; i++) { - cur_irq = i + max8997->irq_base; - irq_set_chip_data(cur_irq, max8997); - irq_set_chip_and_handler(cur_irq, &max8997_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif + domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR, + &max8997_irq_domain_ops, &max8997); + if (!domain) { + dev_err(max8997->dev, "could not create irq domain\n"); + return -ENODEV; } ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread, diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index cb83a7ab53e7..20ecad3179d9 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -143,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c, if (!pdata) goto err; - max8997->irq_base = pdata->irq_base; max8997->ono = pdata->ono; mutex_init(&max8997->iolock); diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 3f4deb62d6b0..830152cfae33 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -23,6 +23,8 @@ #define __LINUX_MFD_MAX8997_PRIV_H #include +#include +#include #define MAX8997_REG_INVALID (0xff) @@ -325,7 +327,7 @@ struct max8997_dev { int irq; int ono; - int irq_base; + struct irq_domain *irq_domain; struct mutex irqlock; int irq_masks_cur[MAX8997_IRQ_GROUP_NR]; int irq_masks_cache[MAX8997_IRQ_GROUP_NR]; diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index 28726dd540f2..24b1a76540da 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -204,7 +204,6 @@ struct max8997_led_platform_data { struct max8997_platform_data { /* IRQ */ - int irq_base; int ono; int wakeup; -- cgit v1.2.3 From 65b19ce6c223287ac95bbc22b12ef5a2738472d1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 15 Apr 2012 11:16:05 +0100 Subject: regulator: core: Allow drivers to pass in a regmap Since many regulators use regmap for register I/O and since there's quite a few very common patterns in the code allow drivers to pass in a regmap to the core for use in generic code. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- drivers/regulator/core.c | 2 ++ include/linux/regulator/driver.h | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'include/linux') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 7943fd64988d..bca1e5989243 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -2877,6 +2878,7 @@ regulator_register(const struct regulator_desc *regulator_desc, rdev->reg_data = config->driver_data; rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; + rdev->regmap = config->regmap; INIT_LIST_HEAD(&rdev->consumer_list); INIT_LIST_HEAD(&rdev->list); BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 4f529ed48d4c..2e753731217b 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -19,6 +19,7 @@ #include #include +struct regmap; struct regulator_dev; struct regulator_init_data; @@ -187,12 +188,14 @@ struct regulator_desc { * @driver_data: private regulator data * @of_node: OpenFirmware node to parse for device tree bindings (may be * NULL). + * @regmap: regmap to use for core regmap helpers */ struct regulator_config { struct device *dev; const struct regulator_init_data *init_data; void *driver_data; struct device_node *of_node; + struct regmap *regmap; }; /* @@ -223,6 +226,7 @@ struct regulator_dev { struct device dev; struct regulation_constraints *constraints; struct regulator *supply; /* for tree */ + struct regmap *regmap; struct delayed_work disable_work; int deferred_disables; -- cgit v1.2.3 From 4ab5b3d92c863e55fa28cc41a7b005b7ae87afee Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 15 Apr 2012 11:23:30 +0100 Subject: regulator: core: Provide regmap based voltage_sel operations Since the voltage selector operations are intended to directly map a bitfield in the device register map into regulator API operations the code for implementing them is usually very standard we can save some code by providing standard implementations for devices using the regmap API. Drivers using regmap can pass their regmap in in the regmap_config struct, set vsel_reg and vsel_mask in their regulator_desc and then use regulator_{get,set}_voltage_sel_regmap in their ops. This saves a small amount of code from each driver. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- drivers/regulator/core.c | 44 ++++++++++++++++++++++++++++++++++++++++ include/linux/regulator/driver.h | 9 ++++++++ 2 files changed, 53 insertions(+) (limited to 'include/linux') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index bca1e5989243..02d2e15b2262 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1838,6 +1838,50 @@ int regulator_is_supported_voltage(struct regulator *regulator, } EXPORT_SYMBOL_GPL(regulator_is_supported_voltage); +/** + * regulator_get_voltage_sel_regmap - standard get_voltage_sel for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * vsel_reg and vsel_mask fields in their descriptor and then use this + * as their get_voltage_vsel operation, saving some code. + */ +int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val); + if (ret != 0) + return ret; + + val &= rdev->desc->vsel_mask; + val >>= ffs(rdev->desc->vsel_mask) - 1; + + return val; +} +EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_regmap); + +/** + * regulator_set_voltage_sel_regmap - standard set_voltage_sel for regmap users + * + * @rdev: regulator to operate on + * @sel: Selector to set + * + * Regulators that use regmap for their register I/O can set the + * vsel_reg and vsel_mask fields in their descriptor and then use this + * as their set_voltage_vsel operation, saving some code. + */ +int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel) +{ + sel <<= ffs(rdev->desc->vsel_mask) - 1; + + return regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg, + rdev->desc->vsel_mask, sel); +} +EXPORT_SYMBOL_GPL(regulator_set_voltage_sel_regmap); + static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 2e753731217b..d1c238970a6e 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -164,6 +164,9 @@ enum regulator_type { * @irq: Interrupt number for the regulator. * @type: Indicates if the regulator is a voltage or current regulator. * @owner: Module providing the regulator, used for refcounting. + + * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ + * @vsel_mask: Mask for register bitfield used for selector */ struct regulator_desc { const char *name; @@ -174,6 +177,9 @@ struct regulator_desc { int irq; enum regulator_type type; struct module *owner; + + unsigned int vsel_reg; + unsigned int vsel_mask; }; /** @@ -250,6 +256,9 @@ int rdev_get_id(struct regulator_dev *rdev); int regulator_mode_to_status(unsigned int); +int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev); +int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel); + void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); #endif -- cgit v1.2.3 From cd6dffb4c6c476f5787f4df3eda7ecb16e25780d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 15 Apr 2012 12:37:47 +0100 Subject: regulator: core: Provide standard enable operations for regmap users Since the enable(), disable() and is_enabled() operations for most regmap based regulators come down to reading and updating a single register bit we can factor out the code and allow these drivers to just define which bit to update using the enable_reg and enable_mask fields in their desc and then use operations provided by the core. As well as the code saving this opens the door to future optimisation of the bulk operations - if the core can realise that we are updating a single register for multiple regulators then it should be able to combine these updates into a single physical operation. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- drivers/regulator/core.c | 55 ++++++++++++++++++++++++++++++++++++++++ include/linux/regulator/driver.h | 7 +++++ 2 files changed, 62 insertions(+) (limited to 'include/linux') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 02d2e15b2262..9fafa00b8cd4 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1725,6 +1725,61 @@ int regulator_disable_deferred(struct regulator *regulator, int ms) } EXPORT_SYMBOL_GPL(regulator_disable_deferred); +/** + * regulator_is_enabled_regmap - standard is_enabled() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their is_enabled operation, saving some code. + */ +int regulator_is_enabled_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); + if (ret != 0) + return ret; + + return (val & rdev->desc->enable_mask) != 0; +} +EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap); + +/** + * regulator_enable_regmap - standard enable() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their enable() operation, saving some code. + */ +int regulator_enable_regmap(struct regulator_dev *rdev) +{ + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, + rdev->desc->enable_mask); +} +EXPORT_SYMBOL_GPL(regulator_enable_regmap); + +/** + * regulator_disable_regmap - standard disable() for regmap users + * + * @rdev: regulator to operate on + * + * Regulators that use regmap for their register I/O can set the + * enable_reg and enable_mask fields in their descriptor and then use + * this as their disable() operation, saving some code. + */ +int regulator_disable_regmap(struct regulator_dev *rdev) +{ + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, 0); +} +EXPORT_SYMBOL_GPL(regulator_disable_regmap); + static int _regulator_is_enabled(struct regulator_dev *rdev) { /* If we don't know then assume that the regulator is always on */ diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index d1c238970a6e..8160bc87be28 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -167,6 +167,8 @@ enum regulator_type { * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ * @vsel_mask: Mask for register bitfield used for selector + * @enable_reg: Register for control when using regmap enable/disable ops + * @enable_mask: Mask for control when using regmap enable/disable ops */ struct regulator_desc { const char *name; @@ -180,6 +182,8 @@ struct regulator_desc { unsigned int vsel_reg; unsigned int vsel_mask; + unsigned int enable_reg; + unsigned int enable_mask; }; /** @@ -258,6 +262,9 @@ int regulator_mode_to_status(unsigned int); int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev); int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel); +int regulator_is_enabled_regmap(struct regulator_dev *rdev); +int regulator_enable_regmap(struct regulator_dev *rdev); +int regulator_disable_regmap(struct regulator_dev *rdev); void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); -- cgit v1.2.3 From b1fa650c7e6e81ca788fef52b1659295eb82ffdd Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 17 Apr 2012 12:08:48 +1000 Subject: seccomp: use a static inline for a function stub Fixes this error message when CONFIG_SECCOMP is not set: arch/powerpc/kernel/ptrace.c: In function 'do_syscall_trace_enter': arch/powerpc/kernel/ptrace.c:1713:2: error: statement with no effect [-Werror=unused-value] Signed-off-by: Stephen Rothwell Signed-off-by: James Morris --- include/linux/seccomp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 5818e869651b..60f2b350ead7 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -90,7 +90,7 @@ static inline int seccomp_mode(struct seccomp *s) struct seccomp { }; struct seccomp_filter { }; -#define secure_computing(x) 0 +static inline int secure_computing(int this_syscall) { return 0; } static inline long prctl_get_seccomp(void) { -- cgit v1.2.3 From 9fe2a7015393dc0203ac39242ae9c89038994f3c Mon Sep 17 00:00:00 2001 From: Srivatsa Vaddagiri Date: Fri, 23 Mar 2012 13:36:28 +0530 Subject: debugfs: Add support to print u32 array in debugfs Move the code from Xen to debugfs to make the code common for other users as well. Accked-by: Greg Kroah-Hartman Signed-off-by: Srivatsa Vaddagiri Signed-off-by: Suzuki Poulose [v1: Fixed rebase issues] [v2: Fixed PPC compile issues] Signed-off-by: Raghavendra K T Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/debugfs.c | 104 --------------------------------------- arch/x86/xen/debugfs.h | 4 -- arch/x86/xen/spinlock.c | 12 ++--- fs/debugfs/file.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/debugfs.h | 11 +++++ 5 files changed, 145 insertions(+), 114 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/xen/debugfs.c b/arch/x86/xen/debugfs.c index ef1db1900d86..c8377fb26cdf 100644 --- a/arch/x86/xen/debugfs.c +++ b/arch/x86/xen/debugfs.c @@ -19,107 +19,3 @@ struct dentry * __init xen_init_debugfs(void) return d_xen_debug; } -struct array_data -{ - void *array; - unsigned elements; -}; - -static int u32_array_open(struct inode *inode, struct file *file) -{ - file->private_data = NULL; - return nonseekable_open(inode, file); -} - -static size_t format_array(char *buf, size_t bufsize, const char *fmt, - u32 *array, unsigned array_size) -{ - size_t ret = 0; - unsigned i; - - for(i = 0; i < array_size; i++) { - size_t len; - - len = snprintf(buf, bufsize, fmt, array[i]); - len++; /* ' ' or '\n' */ - ret += len; - - if (buf) { - buf += len; - bufsize -= len; - buf[-1] = (i == array_size-1) ? '\n' : ' '; - } - } - - ret++; /* \0 */ - if (buf) - *buf = '\0'; - - return ret; -} - -static char *format_array_alloc(const char *fmt, u32 *array, unsigned array_size) -{ - size_t len = format_array(NULL, 0, fmt, array, array_size); - char *ret; - - ret = kmalloc(len, GFP_KERNEL); - if (ret == NULL) - return NULL; - - format_array(ret, len, fmt, array, array_size); - return ret; -} - -static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len, - loff_t *ppos) -{ - struct inode *inode = file->f_path.dentry->d_inode; - struct array_data *data = inode->i_private; - size_t size; - - if (*ppos == 0) { - if (file->private_data) { - kfree(file->private_data); - file->private_data = NULL; - } - - file->private_data = format_array_alloc("%u", data->array, data->elements); - } - - size = 0; - if (file->private_data) - size = strlen(file->private_data); - - return simple_read_from_buffer(buf, len, ppos, file->private_data, size); -} - -static int xen_array_release(struct inode *inode, struct file *file) -{ - kfree(file->private_data); - - return 0; -} - -static const struct file_operations u32_array_fops = { - .owner = THIS_MODULE, - .open = u32_array_open, - .release= xen_array_release, - .read = u32_array_read, - .llseek = no_llseek, -}; - -struct dentry *xen_debugfs_create_u32_array(const char *name, umode_t mode, - struct dentry *parent, - u32 *array, unsigned elements) -{ - struct array_data *data = kmalloc(sizeof(*data), GFP_KERNEL); - - if (data == NULL) - return NULL; - - data->array = array; - data->elements = elements; - - return debugfs_create_file(name, mode, parent, data, &u32_array_fops); -} diff --git a/arch/x86/xen/debugfs.h b/arch/x86/xen/debugfs.h index 78d25499be5b..12ebf3325c7b 100644 --- a/arch/x86/xen/debugfs.h +++ b/arch/x86/xen/debugfs.h @@ -3,8 +3,4 @@ struct dentry * __init xen_init_debugfs(void); -struct dentry *xen_debugfs_create_u32_array(const char *name, umode_t mode, - struct dentry *parent, - u32 *array, unsigned elements); - #endif /* _XEN_DEBUGFS_H */ diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c index d69cc6c3f808..83e866d714ce 100644 --- a/arch/x86/xen/spinlock.c +++ b/arch/x86/xen/spinlock.c @@ -440,12 +440,12 @@ static int __init xen_spinlock_debugfs(void) debugfs_create_u64("time_total", 0444, d_spin_debug, &spinlock_stats.time_total); - xen_debugfs_create_u32_array("histo_total", 0444, d_spin_debug, - spinlock_stats.histo_spin_total, HISTO_BUCKETS + 1); - xen_debugfs_create_u32_array("histo_spinning", 0444, d_spin_debug, - spinlock_stats.histo_spin_spinning, HISTO_BUCKETS + 1); - xen_debugfs_create_u32_array("histo_blocked", 0444, d_spin_debug, - spinlock_stats.histo_spin_blocked, HISTO_BUCKETS + 1); + debugfs_create_u32_array("histo_total", 0444, d_spin_debug, + spinlock_stats.histo_spin_total, HISTO_BUCKETS + 1); + debugfs_create_u32_array("histo_spinning", 0444, d_spin_debug, + spinlock_stats.histo_spin_spinning, HISTO_BUCKETS + 1); + debugfs_create_u32_array("histo_blocked", 0444, d_spin_debug, + spinlock_stats.histo_spin_blocked, HISTO_BUCKETS + 1); return 0; } diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 5dfafdd1dbd3..2340f6978d6e 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -20,6 +20,7 @@ #include #include #include +#include static ssize_t default_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) @@ -520,6 +521,133 @@ struct dentry *debugfs_create_blob(const char *name, umode_t mode, } EXPORT_SYMBOL_GPL(debugfs_create_blob); +struct array_data { + void *array; + u32 elements; +}; + +static int u32_array_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return nonseekable_open(inode, file); +} + +static size_t format_array(char *buf, size_t bufsize, const char *fmt, + u32 *array, u32 array_size) +{ + size_t ret = 0; + u32 i; + + for (i = 0; i < array_size; i++) { + size_t len; + + len = snprintf(buf, bufsize, fmt, array[i]); + len++; /* ' ' or '\n' */ + ret += len; + + if (buf) { + buf += len; + bufsize -= len; + buf[-1] = (i == array_size-1) ? '\n' : ' '; + } + } + + ret++; /* \0 */ + if (buf) + *buf = '\0'; + + return ret; +} + +static char *format_array_alloc(const char *fmt, u32 *array, + u32 array_size) +{ + size_t len = format_array(NULL, 0, fmt, array, array_size); + char *ret; + + ret = kmalloc(len, GFP_KERNEL); + if (ret == NULL) + return NULL; + + format_array(ret, len, fmt, array, array_size); + return ret; +} + +static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct array_data *data = inode->i_private; + size_t size; + + if (*ppos == 0) { + if (file->private_data) { + kfree(file->private_data); + file->private_data = NULL; + } + + file->private_data = format_array_alloc("%u", data->array, + data->elements); + } + + size = 0; + if (file->private_data) + size = strlen(file->private_data); + + return simple_read_from_buffer(buf, len, ppos, + file->private_data, size); +} + +static int u32_array_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static const struct file_operations u32_array_fops = { + .owner = THIS_MODULE, + .open = u32_array_open, + .release = u32_array_release, + .read = u32_array_read, + .llseek = no_llseek, +}; + +/** + * debugfs_create_u32_array - create a debugfs file that is used to read u32 + * array. + * @name: a pointer to a string containing the name of the file to create. + * @mode: the permission that the file should have. + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this parameter is %NULL, then the + * file will be created in the root of the debugfs filesystem. + * @array: u32 array that provides data. + * @elements: total number of elements in the array. + * + * This function creates a file in debugfs with the given name that exports + * @array as data. If the @mode variable is so set it can be read from. + * Writing is not supported. Seek within the file is also not supported. + * Once array is created its size can not be changed. + * + * The function returns a pointer to dentry on success. If debugfs is not + * enabled in the kernel, the value -%ENODEV will be returned. + */ +struct dentry *debugfs_create_u32_array(const char *name, umode_t mode, + struct dentry *parent, + u32 *array, u32 elements) +{ + struct array_data *data = kmalloc(sizeof(*data), GFP_KERNEL); + + if (data == NULL) + return NULL; + + data->array = array; + data->elements = elements; + + return debugfs_create_file(name, mode, parent, data, &u32_array_fops); +} +EXPORT_SYMBOL_GPL(debugfs_create_u32_array); + #ifdef CONFIG_HAS_IOMEM /* diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index ae36b72c22f3..66c434f5dd1e 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -93,6 +93,10 @@ struct dentry *debugfs_create_regset32(const char *name, umode_t mode, int debugfs_print_regs32(struct seq_file *s, const struct debugfs_reg32 *regs, int nregs, void __iomem *base, char *prefix); +struct dentry *debugfs_create_u32_array(const char *name, umode_t mode, + struct dentry *parent, + u32 *array, u32 elements); + bool debugfs_initialized(void); #else @@ -219,6 +223,13 @@ static inline bool debugfs_initialized(void) return false; } +static inline struct dentry *debugfs_create_u32_array(const char *name, umode_t mode, + struct dentry *parent, + u32 *array, u32 elements) +{ + return ERR_PTR(-ENODEV); +} + #endif #endif -- cgit v1.2.3 From 82b719b11fd750188c125078ad6a6c0d23219dfb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 17 Apr 2012 14:51:47 +0100 Subject: mfd: Revert "mfd: add irq domain support for max8997 interrupts" This reverts commit 98d8618af37728f6e18e84110ddb99987b47dd12 as it breaks the build of the muic driver. Reported-by: Stephen Rothwell Signed-off-by: Mark Brown --- arch/arm/mach-exynos/mach-nuri.c | 4 +++ arch/arm/mach-exynos/mach-origen.c | 1 + drivers/mfd/max8997-irq.c | 61 +++++++++++++++---------------------- drivers/mfd/max8997.c | 1 + include/linux/mfd/max8997-private.h | 4 +-- include/linux/mfd/max8997.h | 1 + 6 files changed, 33 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index 0b48d6a8cf03..b3982c867c9c 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -1079,8 +1079,12 @@ static struct platform_device nuri_max8903_device = { static void __init nuri_power_init(void) { int gpio; + int irq_base = IRQ_GPIO_END + 1; int ta_en = 0; + nuri_max8997_pdata.irq_base = irq_base; + irq_base += MAX8997_IRQ_NR; + gpio = EXYNOS4_GPX0(7); gpio_request(gpio, "AP_PMIC_IRQ"); s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf)); diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index 600368fa6b69..878d4c99142d 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -424,6 +424,7 @@ static struct max8997_platform_data __initdata origen_max8997_pdata = { .buck1_gpiodvs = false, .buck2_gpiodvs = false, .buck5_gpiodvs = false, + .irq_base = IRQ_GPIO_END + 1, .ignore_gpiodvs_side_effect = true, .buck125_default_idx = 0x0, diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c index 00390a117ae6..09274cf7c33b 100644 --- a/drivers/mfd/max8997-irq.c +++ b/drivers/mfd/max8997-irq.c @@ -142,8 +142,7 @@ static void max8997_irq_sync_unlock(struct irq_data *data) static const inline struct max8997_irq_data * irq_to_max8997_irq(struct max8997_dev *max8997, int irq) { - struct irq_data *data = irq_get_irq_data(irq); - return &max8997_irqs[data->hwirq]; + return &max8997_irqs[irq - max8997->irq_base]; } static void max8997_irq_mask(struct irq_data *data) @@ -183,7 +182,7 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {}; u8 irq_src; int ret; - int i, cur_irq; + int i; ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src); if (ret < 0) { @@ -270,11 +269,8 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) /* Report */ for (i = 0; i < MAX8997_IRQ_NR; i++) { - if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) { - cur_irq = irq_find_mapping(max8997->irq_domain, i); - if (cur_irq) - handle_nested_irq(cur_irq); - } + if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) + handle_nested_irq(max8997->irq_base + i); } return IRQ_HANDLED; @@ -282,40 +278,26 @@ static irqreturn_t max8997_irq_thread(int irq, void *data) int max8997_irq_resume(struct max8997_dev *max8997) { - if (max8997->irq && max8997->irq_domain) - max8997_irq_thread(0, max8997); - return 0; -} - -static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hw) -{ - struct max8997_dev *max8997 = d->host_data; - - irq_set_chip_data(irq, max8997); - irq_set_chip_and_handler(irq, &max8997_irq_chip, handle_edge_irq); - irq_set_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - irq_set_noprobe(irq); -#endif + if (max8997->irq && max8997->irq_base) + max8997_irq_thread(max8997->irq_base, max8997); return 0; } -static struct irq_domain_ops max8997_irq_domain_ops = { - .map = max8997_irq_domain_map, -}; - int max8997_irq_init(struct max8997_dev *max8997) { - struct irq_domain *domain; int i; + int cur_irq; int ret; u8 val; if (!max8997->irq) { dev_warn(max8997->dev, "No interrupt specified.\n"); + max8997->irq_base = 0; + return 0; + } + + if (!max8997->irq_base) { + dev_err(max8997->dev, "No interrupt base specified.\n"); return 0; } @@ -345,11 +327,18 @@ int max8997_irq_init(struct max8997_dev *max8997) true : false; } - domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR, - &max8997_irq_domain_ops, &max8997); - if (!domain) { - dev_err(max8997->dev, "could not create irq domain\n"); - return -ENODEV; + /* Register with genirq */ + for (i = 0; i < MAX8997_IRQ_NR; i++) { + cur_irq = i + max8997->irq_base; + irq_set_chip_data(cur_irq, max8997); + irq_set_chip_and_handler(cur_irq, &max8997_irq_chip, + handle_edge_irq); + irq_set_nested_thread(cur_irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + irq_set_noprobe(cur_irq); +#endif } ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread, diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index 20ecad3179d9..cb83a7ab53e7 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -143,6 +143,7 @@ static int max8997_i2c_probe(struct i2c_client *i2c, if (!pdata) goto err; + max8997->irq_base = pdata->irq_base; max8997->ono = pdata->ono; mutex_init(&max8997->iolock); diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 830152cfae33..3f4deb62d6b0 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -23,8 +23,6 @@ #define __LINUX_MFD_MAX8997_PRIV_H #include -#include -#include #define MAX8997_REG_INVALID (0xff) @@ -327,7 +325,7 @@ struct max8997_dev { int irq; int ono; - struct irq_domain *irq_domain; + int irq_base; struct mutex irqlock; int irq_masks_cur[MAX8997_IRQ_GROUP_NR]; int irq_masks_cache[MAX8997_IRQ_GROUP_NR]; diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index 24b1a76540da..28726dd540f2 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -204,6 +204,7 @@ struct max8997_led_platform_data { struct max8997_platform_data { /* IRQ */ + int irq_base; int ono; int wakeup; -- cgit v1.2.3 From 0b6c4857f7684f6d3f59e0506f62953575346978 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 9 Apr 2012 20:51:18 +0200 Subject: firewire: core: fix DMA mapping direction Seen with recent libdc1394: If a client mmap()s the buffer of an isochronous reception buffer with PROT_READ|PROT_WRITE instead of just PROT_READ, firewire-core sets the wrong DMA mapping direction during buffer initialization. The fix is to split fw_iso_buffer_init() into allocation and DMA mapping and to perform the latter after both buffer and DMA context were allocated. Buffer allocation and context allocation may happen in any order, but we need the context type (reception or transmission) in order to set the DMA direction of the buffer. Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 51 ++++++++++++++++++++++------ drivers/firewire/core-iso.c | 80 ++++++++++++++++++++++++++++---------------- drivers/firewire/core.h | 7 +++- include/linux/firewire.h | 1 + 4 files changed, 100 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 2e6b24547e2a..2783f69dada6 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,7 @@ struct client { u64 iso_closure; struct fw_iso_buffer buffer; unsigned long vm_start; + bool buffer_is_mapped; struct list_head phy_receiver_link; u64 phy_receiver_closure; @@ -959,11 +961,20 @@ static void iso_mc_callback(struct fw_iso_context *context, sizeof(e->interrupt), NULL, 0); } +static enum dma_data_direction iso_dma_direction(struct fw_iso_context *context) +{ + if (context->type == FW_ISO_CONTEXT_TRANSMIT) + return DMA_TO_DEVICE; + else + return DMA_FROM_DEVICE; +} + static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) { struct fw_cdev_create_iso_context *a = &arg->create_iso_context; struct fw_iso_context *context; fw_iso_callback_t cb; + int ret; BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE || @@ -1004,8 +1015,21 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) if (client->iso_context != NULL) { spin_unlock_irq(&client->lock); fw_iso_context_destroy(context); + return -EBUSY; } + if (!client->buffer_is_mapped) { + ret = fw_iso_buffer_map_dma(&client->buffer, + client->device->card, + iso_dma_direction(context)); + if (ret < 0) { + spin_unlock_irq(&client->lock); + fw_iso_context_destroy(context); + + return ret; + } + client->buffer_is_mapped = true; + } client->iso_closure = a->closure; client->iso_context = context; spin_unlock_irq(&client->lock); @@ -1651,7 +1675,6 @@ static long fw_device_op_compat_ioctl(struct file *file, static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) { struct client *client = file->private_data; - enum dma_data_direction direction; unsigned long size; int page_count, ret; @@ -1674,20 +1697,28 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) if (size & ~PAGE_MASK) return -EINVAL; - if (vma->vm_flags & VM_WRITE) - direction = DMA_TO_DEVICE; - else - direction = DMA_FROM_DEVICE; - - ret = fw_iso_buffer_init(&client->buffer, client->device->card, - page_count, direction); + ret = fw_iso_buffer_alloc(&client->buffer, page_count); if (ret < 0) return ret; - ret = fw_iso_buffer_map(&client->buffer, vma); + spin_lock_irq(&client->lock); + if (client->iso_context) { + ret = fw_iso_buffer_map_dma(&client->buffer, + client->device->card, + iso_dma_direction(client->iso_context)); + client->buffer_is_mapped = (ret == 0); + } + spin_unlock_irq(&client->lock); if (ret < 0) - fw_iso_buffer_destroy(&client->buffer, client->device->card); + goto fail; + ret = fw_iso_buffer_map_vma(&client->buffer, vma); + if (ret < 0) + goto fail; + + return 0; + fail: + fw_iso_buffer_destroy(&client->buffer, client->device->card); return ret; } diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index d1565828ae2c..8382e27e9a27 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -39,52 +39,73 @@ * Isochronous DMA context management */ -int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, - int page_count, enum dma_data_direction direction) +int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count) { - int i, j; - dma_addr_t address; - - buffer->page_count = page_count; - buffer->direction = direction; + int i; + buffer->page_count = 0; + buffer->page_count_mapped = 0; buffer->pages = kmalloc(page_count * sizeof(buffer->pages[0]), GFP_KERNEL); if (buffer->pages == NULL) - goto out; + return -ENOMEM; - for (i = 0; i < buffer->page_count; i++) { + for (i = 0; i < page_count; i++) { buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); if (buffer->pages[i] == NULL) - goto out_pages; + break; + } + buffer->page_count = i; + if (i < page_count) { + fw_iso_buffer_destroy(buffer, NULL); + return -ENOMEM; + } + return 0; +} + +int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card, + enum dma_data_direction direction) +{ + dma_addr_t address; + int i; + + buffer->direction = direction; + + for (i = 0; i < buffer->page_count; i++) { address = dma_map_page(card->device, buffer->pages[i], 0, PAGE_SIZE, direction); - if (dma_mapping_error(card->device, address)) { - __free_page(buffer->pages[i]); - goto out_pages; - } + if (dma_mapping_error(card->device, address)) + break; + set_page_private(buffer->pages[i], address); } + buffer->page_count_mapped = i; + if (i < buffer->page_count) + return -ENOMEM; return 0; +} - out_pages: - for (j = 0; j < i; j++) { - address = page_private(buffer->pages[j]); - dma_unmap_page(card->device, address, - PAGE_SIZE, direction); - __free_page(buffer->pages[j]); - } - kfree(buffer->pages); - out: - buffer->pages = NULL; +int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, + int page_count, enum dma_data_direction direction) +{ + int ret; + + ret = fw_iso_buffer_alloc(buffer, page_count); + if (ret < 0) + return ret; + + ret = fw_iso_buffer_map_dma(buffer, card, direction); + if (ret < 0) + fw_iso_buffer_destroy(buffer, card); - return -ENOMEM; + return ret; } EXPORT_SYMBOL(fw_iso_buffer_init); -int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma) +int fw_iso_buffer_map_vma(struct fw_iso_buffer *buffer, + struct vm_area_struct *vma) { unsigned long uaddr; int i, err; @@ -107,15 +128,18 @@ void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, int i; dma_addr_t address; - for (i = 0; i < buffer->page_count; i++) { + for (i = 0; i < buffer->page_count_mapped; i++) { address = page_private(buffer->pages[i]); dma_unmap_page(card->device, address, PAGE_SIZE, buffer->direction); - __free_page(buffer->pages[i]); } + for (i = 0; i < buffer->page_count; i++) + __free_page(buffer->pages[i]); kfree(buffer->pages); buffer->pages = NULL; + buffer->page_count = 0; + buffer->page_count_mapped = 0; } EXPORT_SYMBOL(fw_iso_buffer_destroy); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 9047f5547d98..94257aecd054 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -169,7 +170,11 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event); /* -iso */ -int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma); +int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count); +int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card, + enum dma_data_direction direction); +int fw_iso_buffer_map_vma(struct fw_iso_buffer *buffer, + struct vm_area_struct *vma); /* -topology */ diff --git a/include/linux/firewire.h b/include/linux/firewire.h index cdc9b719e9c7..0a1905719f6f 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -391,6 +391,7 @@ struct fw_iso_buffer { enum dma_data_direction direction; struct page **pages; int page_count; + int page_count_mapped; }; int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, -- cgit v1.2.3 From 7bdbff6762a573b911e4ee5715779d8ee6a62631 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 11 Apr 2012 17:38:10 +0200 Subject: firewire: move rcode_string() to core There is nothing audio-specific about the rcode_string() helper, so move it from snd-firewire-lib into firewire-core to allow other code to use it. Signed-off-by: Clemens Ladisch Signed-off-by: Stefan Richter (fixed sound/firewire/cmp.c) --- drivers/firewire/core-transaction.c | 26 ++++++++++++++++++++++++++ include/linux/firewire.h | 1 + sound/firewire/cmp.c | 2 +- sound/firewire/lib.c | 28 +--------------------------- sound/firewire/lib.h | 1 - 5 files changed, 29 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index dea2dcc9310d..1c4980c32f90 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -994,6 +994,32 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p) } EXPORT_SYMBOL(fw_core_handle_response); +/** + * fw_rcode_string - convert a firewire result code to an error description + * @rcode: the result code + */ +const char *fw_rcode_string(int rcode) +{ + static const char *const names[] = { + [RCODE_COMPLETE] = "no error", + [RCODE_CONFLICT_ERROR] = "conflict error", + [RCODE_DATA_ERROR] = "data error", + [RCODE_TYPE_ERROR] = "type error", + [RCODE_ADDRESS_ERROR] = "address error", + [RCODE_SEND_ERROR] = "send error", + [RCODE_CANCELLED] = "timeout", + [RCODE_BUSY] = "busy", + [RCODE_GENERATION] = "bus reset", + [RCODE_NO_ACK] = "no ack", + }; + + if ((unsigned int)rcode < ARRAY_SIZE(names) && names[rcode]) + return names[rcode]; + else + return "unknown"; +} +EXPORT_SYMBOL(fw_rcode_string); + static const struct fw_address_region topology_map_region = { .start = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP, .end = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP_END, }; diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 0a1905719f6f..584826ba2eb7 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -334,6 +334,7 @@ int fw_cancel_transaction(struct fw_card *card, int fw_run_transaction(struct fw_card *card, int tcode, int destination_id, int generation, int speed, unsigned long long offset, void *payload, size_t length); +const char *fw_rcode_string(int rcode); static inline int fw_stream_packet_destination_id(int tag, int channel, int sy) { diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 76294f2ae47f..645cb0ba4293 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -84,7 +84,7 @@ static int pcr_modify(struct cmp_connection *c, return 0; io_error: - cmp_error(c, "transaction failed: %s\n", rcode_string(rcode)); + cmp_error(c, "transaction failed: %s\n", fw_rcode_string(rcode)); return -EIO; bus_reset: diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index 4750cea2210e..14eb41498372 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -13,32 +13,6 @@ #define ERROR_RETRY_DELAY_MS 5 -/** - * rcode_string - convert a firewire result code to a string - * @rcode: the result - */ -const char *rcode_string(unsigned int rcode) -{ - static const char *const names[] = { - [RCODE_COMPLETE] = "complete", - [RCODE_CONFLICT_ERROR] = "conflict error", - [RCODE_DATA_ERROR] = "data error", - [RCODE_TYPE_ERROR] = "type error", - [RCODE_ADDRESS_ERROR] = "address error", - [RCODE_SEND_ERROR] = "send error", - [RCODE_CANCELLED] = "cancelled", - [RCODE_BUSY] = "busy", - [RCODE_GENERATION] = "generation", - [RCODE_NO_ACK] = "no ack", - }; - - if (rcode < ARRAY_SIZE(names) && names[rcode]) - return names[rcode]; - else - return "unknown"; -} -EXPORT_SYMBOL(rcode_string); - /** * snd_fw_transaction - send a request and wait for its completion * @unit: the driver's unit on the target device @@ -71,7 +45,7 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode, if (rcode_is_permanent_error(rcode) || ++tries >= 3) { dev_err(&unit->device, "transaction failed: %s\n", - rcode_string(rcode)); + fw_rcode_string(rcode)); return -EIO; } diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h index 064f3fd9ab06..aef301476ea9 100644 --- a/sound/firewire/lib.h +++ b/sound/firewire/lib.h @@ -8,7 +8,6 @@ struct fw_unit; int snd_fw_transaction(struct fw_unit *unit, int tcode, u64 offset, void *buffer, size_t length); -const char *rcode_string(unsigned int rcode); /* returns true if retrying the transaction would not make sense */ static inline bool rcode_is_permanent_error(int rcode) -- cgit v1.2.3 From 766644d2df254934d656a0a0628b636212c24f9e Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sun, 25 Mar 2012 20:32:49 +0530 Subject: of/irq: add empty irq_of_parse_and_map() for non-dt builds Add a empty irq_of_parse_and_map() function that returns 0 for non-dt builds and avoid having #ifdef CONFIG_OF around all calls to irq_of_parse_and_map(). In addition to that, the irq_of_parse_and_map() function declaration is made available only if CONFIG_OF_IRQ is defined, which is the same config option that makes the irq_of_parse_and_map() function definition available. While at it, fix a typo as well. Changes since v1: - Moved irq_of_parse_and_map() function declaration under CONFIG_OF_IRQ. - Fix a minor typo in comments. Suggested-by: Grant Likely Signed-off-by: Thomas Abraham Acked-by: Rob Herring [grant.likely: fix bug causing SPARC to break] Signed-off-by: Grant Likely --- include/linux/of_irq.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index d229ad3edee0..1717cd935e1c 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -11,7 +11,7 @@ struct of_irq; #include /* - * irq_of_parse_and_map() is used ba all OF enabled platforms; but SPARC + * irq_of_parse_and_map() is used by all OF enabled platforms; but SPARC * implements it differently. However, the prototype is the same for all, * so declare it here regardless of the CONFIG_OF_IRQ setting. */ @@ -76,5 +76,13 @@ extern struct device_node *of_irq_find_parent(struct device_node *child); extern void of_irq_init(const struct of_device_id *matches); #endif /* CONFIG_OF_IRQ */ -#endif /* CONFIG_OF */ + +#else /* !CONFIG_OF */ +static inline unsigned int irq_of_parse_and_map(struct device_node *dev, + int index) +{ + return 0; +} +#endif /* !CONFIG_OF */ + #endif /* __OF_IRQ_H */ -- cgit v1.2.3 From e4da89d02f369450996cfd04f64b1cce4d8afaea Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Tue, 17 Apr 2012 14:48:57 -0500 Subject: seccomp: ignore secure_computing return values This change is inspired by https://lkml.org/lkml/2012/4/16/14 which fixes the build warnings for arches that don't support CONFIG_HAVE_ARCH_SECCOMP_FILTER. In particular, there is no requirement for the return value of secure_computing() to be checked unless the architecture supports seccomp filter. Instead of silencing the warnings with (void) a new static inline is added to encode the expected behavior in a compiler and human friendly way. v2: - cleans things up with a static inline - removes sfr's signed-off-by since it is a different approach v1: - matches sfr's original change Reported-by: Stephen Rothwell Signed-off-by: Will Drewry Acked-by: Kees Cook Signed-off-by: James Morris --- arch/microblaze/kernel/ptrace.c | 2 +- arch/mips/kernel/ptrace.c | 2 +- arch/powerpc/kernel/ptrace.c | 2 +- arch/s390/kernel/ptrace.c | 2 +- arch/sh/kernel/ptrace_32.c | 2 +- arch/sh/kernel/ptrace_64.c | 2 +- arch/sparc/kernel/ptrace_64.c | 2 +- include/linux/seccomp.h | 7 +++++++ 8 files changed, 14 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c index 6eb2aa927d89..ab1b9db661f3 100644 --- a/arch/microblaze/kernel/ptrace.c +++ b/arch/microblaze/kernel/ptrace.c @@ -136,7 +136,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) { long ret = 0; - secure_computing(regs->r12); + secure_computing_strict(regs->r12); if (test_thread_flag(TIF_SYSCALL_TRACE) && tracehook_report_syscall_entry(regs)) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 7c24c2973c6d..4812c6d916e4 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -535,7 +535,7 @@ static inline int audit_arch(void) asmlinkage void syscall_trace_enter(struct pt_regs *regs) { /* do the secure computing check first */ - secure_computing(regs->regs[2]); + secure_computing_strict(regs->regs[2]); if (!(current->ptrace & PT_PTRACED)) goto out; diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 8d8e028893be..dd5e214cdf21 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -1710,7 +1710,7 @@ long do_syscall_trace_enter(struct pt_regs *regs) { long ret = 0; - secure_computing(regs->gpr[0]); + secure_computing_strict(regs->gpr[0]); if (test_thread_flag(TIF_SYSCALL_TRACE) && tracehook_report_syscall_entry(regs)) diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 02f300fbf070..4993e689b2c2 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -719,7 +719,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) long ret = 0; /* Do the secure computing check first. */ - secure_computing(regs->gprs[2]); + secure_computing_strict(regs->gprs[2]); /* * The sysc_tracesys code in entry.S stored the system diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 9698671444e6..81f999a672f6 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -503,7 +503,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) { long ret = 0; - secure_computing(regs->regs[0]); + secure_computing_strict(regs->regs[0]); if (test_thread_flag(TIF_SYSCALL_TRACE) && tracehook_report_syscall_entry(regs)) diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c index bc81e07dc098..af90339dadcd 100644 --- a/arch/sh/kernel/ptrace_64.c +++ b/arch/sh/kernel/ptrace_64.c @@ -522,7 +522,7 @@ asmlinkage long long do_syscall_trace_enter(struct pt_regs *regs) { long long ret = 0; - secure_computing(regs->regs[9]); + secure_computing_strict(regs->regs[9]); if (test_thread_flag(TIF_SYSCALL_TRACE) && tracehook_report_syscall_entry(regs)) diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 6f97c0767995..484dabac7045 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -1062,7 +1062,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) int ret = 0; /* do the secure computing check first */ - secure_computing(regs->u_regs[UREG_G1]); + secure_computing_strict(regs->u_regs[UREG_G1]); if (test_thread_flag(TIF_SYSCALL_TRACE)) ret = tracehook_report_syscall_entry(regs); diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 60f2b350ead7..84f6320da50f 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -75,6 +75,12 @@ static inline int secure_computing(int this_syscall) return 0; } +/* A wrapper for architectures supporting only SECCOMP_MODE_STRICT. */ +static inline void secure_computing_strict(int this_syscall) +{ + BUG_ON(secure_computing(this_syscall) != 0); +} + extern long prctl_get_seccomp(void); extern long prctl_set_seccomp(unsigned long, char __user *); @@ -91,6 +97,7 @@ struct seccomp { }; struct seccomp_filter { }; static inline int secure_computing(int this_syscall) { return 0; } +static inline void secure_computing_strict(int this_syscall) { return; } static inline long prctl_get_seccomp(void) { -- cgit v1.2.3 From 6974f1f458b083a84778641c6a4665799d6156da Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 5 Apr 2012 17:07:23 +0800 Subject: pinctrl: fix compile error if not select PINMUX support The pinctrl_register_mappings is defined in core.c, so change the dependent macro from CONFIG_MUX to CONFIG_PINCTRL. The compile error message is: drivers/pinctrl/core.c:886: error: redefinition of 'pinctrl_register_mappings' include/linux/pinctrl/machine.h:160: note: previous definition of 'pinctrl_register_mappings' was here make[2]: *** [drivers/pinctrl/core.o] Error 1 make[1]: *** [drivers/pinctrl] Error 2 make: *** [drivers] Error 2 Acked-by: Stephen Warren Signed-off-by: Dong Aisheng Signed-off-by: Linus Walleij --- include/linux/pinctrl/machine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index fee4349364f7..d32eb8ccba84 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -148,7 +148,7 @@ struct pinctrl_map { #define PIN_MAP_CONFIGS_GROUP_HOG_DEFAULT(dev, grp, cfgs) \ PIN_MAP_CONFIGS_GROUP(dev, PINCTRL_STATE_DEFAULT, dev, grp, cfgs) -#ifdef CONFIG_PINMUX +#ifdef CONFIG_PINCTRL extern int pinctrl_register_mappings(struct pinctrl_map const *map, unsigned num_maps); -- cgit v1.2.3 From 22f099d0fcb33073a1f1f10402a16b28602e20f2 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 16 Mar 2012 14:54:23 -0600 Subject: pinctrl: include to prevent compile errors Macros in call ARRAY_SIZE(), the definition of which eventually calls BUILD_BUG_ON_ZERO(), which is defined in . Include that so that every .c file using the pinctrl macros doesn't have to do that itself. Signed-off-by: Stephen Warren Signed-off-by: Linus Walleij --- include/linux/pinctrl/machine.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h index d32eb8ccba84..e4d1de742502 100644 --- a/include/linux/pinctrl/machine.h +++ b/include/linux/pinctrl/machine.h @@ -12,6 +12,8 @@ #ifndef __LINUX_PINCTRL_MACHINE_H #define __LINUX_PINCTRL_MACHINE_H +#include + #include "pinctrl-state.h" enum pinctrl_map_type { -- cgit v1.2.3 From 57291ce295c0aca738dd284c4a9c591c09ebee71 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 23 Mar 2012 10:29:46 -0600 Subject: pinctrl: core device tree mapping table parsing support During pinctrl_get(), if the client device has a device tree node, look for the common pinctrl properties there. If found, parse the referenced device tree nodes, with the help of the pinctrl drivers, and generate mapping table entries from them. During pinctrl_put(), free any results of device tree parsing. Acked-by: Dong Aisheng Signed-off-by: Stephen Warren Signed-off-by: Linus Walleij --- drivers/pinctrl/Makefile | 1 + drivers/pinctrl/core.c | 72 +++++++++--- drivers/pinctrl/core.h | 11 +- drivers/pinctrl/devicetree.c | 249 ++++++++++++++++++++++++++++++++++++++++ drivers/pinctrl/devicetree.h | 35 ++++++ include/linux/pinctrl/pinctrl.h | 7 ++ 6 files changed, 357 insertions(+), 18 deletions(-) create mode 100644 drivers/pinctrl/devicetree.c create mode 100644 drivers/pinctrl/devicetree.h (limited to 'include/linux') diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 6d4150b4eced..049c9fb39dab 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -5,6 +5,7 @@ ccflags-$(CONFIG_DEBUG_PINCTRL) += -DDEBUG obj-$(CONFIG_PINCTRL) += core.o obj-$(CONFIG_PINMUX) += pinmux.o obj-$(CONFIG_PINCONF) += pinconf.o +obj-$(CONFIG_OF) += devicetree.o obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o obj-$(CONFIG_PINCTRL_PXA3xx) += pinctrl-pxa3xx.o obj-$(CONFIG_PINCTRL_MMP2) += pinctrl-mmp2.o diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index df6296c5f47b..832f71dcd8c4 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -26,6 +26,7 @@ #include #include #include "core.h" +#include "devicetree.h" #include "pinmux.h" #include "pinconf.h" @@ -45,7 +46,7 @@ struct pinctrl_maps { DEFINE_MUTEX(pinctrl_mutex); /* Global list of pin control devices (struct pinctrl_dev) */ -static LIST_HEAD(pinctrldev_list); +LIST_HEAD(pinctrldev_list); /* List of pin controller handles (struct pinctrl) */ static LIST_HEAD(pinctrl_list); @@ -579,6 +580,13 @@ static struct pinctrl *create_pinctrl(struct device *dev) } p->dev = dev; INIT_LIST_HEAD(&p->states); + INIT_LIST_HEAD(&p->dt_maps); + + ret = pinctrl_dt_to_map(p); + if (ret < 0) { + kfree(p); + return ERR_PTR(ret); + } devname = dev_name(dev); @@ -662,6 +670,8 @@ static void pinctrl_put_locked(struct pinctrl *p, bool inlist) kfree(state); } + pinctrl_dt_free_maps(p); + if (inlist) list_del(&p->node); kfree(p); @@ -787,15 +797,8 @@ int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) } EXPORT_SYMBOL_GPL(pinctrl_select_state); -/** - * pinctrl_register_mappings() - register a set of pin controller mappings - * @maps: the pincontrol mappings table to register. This should probably be - * marked with __initdata so it can be discarded after boot. This - * function will perform a shallow copy for the mapping entries. - * @num_maps: the number of maps in the mapping table - */ -int pinctrl_register_mappings(struct pinctrl_map const *maps, - unsigned num_maps) +int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps, + bool dup, bool locked) { int i, ret; struct pinctrl_maps *maps_node; @@ -851,20 +854,52 @@ int pinctrl_register_mappings(struct pinctrl_map const *maps, } maps_node->num_maps = num_maps; - maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps, GFP_KERNEL); - if (!maps_node->maps) { - pr_err("failed to duplicate mapping table\n"); - kfree(maps_node); - return -ENOMEM; + if (dup) { + maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps, + GFP_KERNEL); + if (!maps_node->maps) { + pr_err("failed to duplicate mapping table\n"); + kfree(maps_node); + return -ENOMEM; + } + } else { + maps_node->maps = maps; } - mutex_lock(&pinctrl_mutex); + if (!locked) + mutex_lock(&pinctrl_mutex); list_add_tail(&maps_node->node, &pinctrl_maps); - mutex_unlock(&pinctrl_mutex); + if (!locked) + mutex_unlock(&pinctrl_mutex); return 0; } +/** + * pinctrl_register_mappings() - register a set of pin controller mappings + * @maps: the pincontrol mappings table to register. This should probably be + * marked with __initdata so it can be discarded after boot. This + * function will perform a shallow copy for the mapping entries. + * @num_maps: the number of maps in the mapping table + */ +int pinctrl_register_mappings(struct pinctrl_map const *maps, + unsigned num_maps) +{ + return pinctrl_register_map(maps, num_maps, true, false); +} + +void pinctrl_unregister_map(struct pinctrl_map const *map) +{ + struct pinctrl_maps *maps_node; + + list_for_each_entry(maps_node, &pinctrl_maps, node) { + if (maps_node->maps == map) { + list_del(&maps_node->node); + return; + } + } +} + #ifdef CONFIG_DEBUG_FS static int pinctrl_pins_show(struct seq_file *s, void *what) @@ -1231,6 +1266,9 @@ static int pinctrl_check_ops(struct pinctrl_dev *pctldev) !ops->get_group_pins) return -EINVAL; + if (ops->dt_node_to_map && !ops->dt_free_map) + return -EINVAL; + return 0; } diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 17ecf651b123..98ae8085e735 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -52,12 +52,15 @@ struct pinctrl_dev { * @dev: the device using this pin control handle * @states: a list of states for this device * @state: the current state + * @dt_maps: the mapping table chunks dynamically parsed from device tree for + * this device, if any */ struct pinctrl { struct list_head node; struct device *dev; struct list_head states; struct pinctrl_state *state; + struct list_head dt_maps; }; /** @@ -100,7 +103,8 @@ struct pinctrl_setting_configs { * struct pinctrl_setting - an individual mux or config setting * @node: list node for struct pinctrl_settings's @settings field * @type: the type of setting - * @pctldev: pin control device handling to be programmed + * @pctldev: pin control device handling to be programmed. Not used for + * PIN_MAP_TYPE_DUMMY_STATE. * @data: Data specific to the setting type */ struct pinctrl_setting { @@ -153,4 +157,9 @@ static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev, return radix_tree_lookup(&pctldev->pin_desc_tree, pin); } +int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps, + bool dup, bool locked); +void pinctrl_unregister_map(struct pinctrl_map const *map); + extern struct mutex pinctrl_mutex; +extern struct list_head pinctrldev_list; diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c new file mode 100644 index 000000000000..5ef2feb44395 --- /dev/null +++ b/drivers/pinctrl/devicetree.c @@ -0,0 +1,249 @@ +/* + * Device tree integration for the pin control subsystem + * + * Copyright (C) 2012 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 "core.h" +#include "devicetree.h" + +/** + * struct pinctrl_dt_map - mapping table chunk parsed from device tree + * @node: list node for struct pinctrl's @dt_maps field + * @pctldev: the pin controller that allocated this struct, and will free it + * @maps: the mapping table entries + */ +struct pinctrl_dt_map { + struct list_head node; + struct pinctrl_dev *pctldev; + struct pinctrl_map *map; + unsigned num_maps; +}; + +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; + ops->dt_free_map(pctldev, map, num_maps); + } else { + /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */ + kfree(map); + } +} + +void pinctrl_dt_free_maps(struct pinctrl *p) +{ + struct pinctrl_dt_map *dt_map, *n1; + + list_for_each_entry_safe(dt_map, n1, &p->dt_maps, node) { + pinctrl_unregister_map(dt_map->map); + list_del(&dt_map->node); + dt_free_map(dt_map->pctldev, dt_map->map, + dt_map->num_maps); + kfree(dt_map); + } + + of_node_put(p->dev->of_node); +} + +static int dt_remember_or_free_map(struct pinctrl *p, const char *statename, + struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + int i; + struct pinctrl_dt_map *dt_map; + + /* Initialize common mapping table entry fields */ + for (i = 0; i < num_maps; i++) { + map[i].dev_name = dev_name(p->dev); + map[i].name = statename; + if (pctldev) + map[i].ctrl_dev_name = dev_name(pctldev->dev); + } + + /* Remember the converted mapping table entries */ + dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL); + if (!dt_map) { + dev_err(p->dev, "failed to alloc struct pinctrl_dt_map\n"); + dt_free_map(pctldev, map, num_maps); + return -ENOMEM; + } + + dt_map->pctldev = pctldev; + dt_map->map = map; + dt_map->num_maps = num_maps; + list_add_tail(&dt_map->node, &p->dt_maps); + + return pinctrl_register_map(map, num_maps, false, true); +} + +static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np) +{ + struct pinctrl_dev *pctldev; + + list_for_each_entry(pctldev, &pinctrldev_list, node) + if (pctldev->dev->of_node == np) + return pctldev; + + return NULL; +} + +static int dt_to_map_one_config(struct pinctrl *p, const char *statename, + struct device_node *np_config) +{ + struct device_node *np_pctldev; + struct pinctrl_dev *pctldev; + struct pinctrl_ops *ops; + int ret; + struct pinctrl_map *map; + unsigned num_maps; + + /* Find the pin controller containing np_config */ + np_pctldev = of_node_get(np_config); + for (;;) { + np_pctldev = of_get_next_parent(np_pctldev); + if (!np_pctldev || of_node_is_root(np_pctldev)) { + dev_err(p->dev, "could not find pctldev for node %s\n", + np_config->full_name); + of_node_put(np_pctldev); + /* FIXME: This should trigger deferrered probe */ + return -ENODEV; + } + pctldev = find_pinctrl_by_of_node(np_pctldev); + if (pctldev) + break; + } + of_node_put(np_pctldev); + + /* + * Call pinctrl driver to parse device tree node, and + * generate mapping table entries + */ + ops = pctldev->desc->pctlops; + if (!ops->dt_node_to_map) { + dev_err(p->dev, "pctldev %s doesn't support DT\n", + dev_name(pctldev->dev)); + return -ENODEV; + } + ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps); + if (ret < 0) + return ret; + + /* Stash the mapping table chunk away for later use */ + return dt_remember_or_free_map(p, statename, pctldev, map, num_maps); +} + +static int dt_remember_dummy_state(struct pinctrl *p, const char *statename) +{ + struct pinctrl_map *map; + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) { + dev_err(p->dev, "failed to alloc struct pinctrl_map\n"); + return -ENOMEM; + } + + /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */ + map->type = PIN_MAP_TYPE_DUMMY_STATE; + + return dt_remember_or_free_map(p, statename, NULL, map, 1); +} + +int pinctrl_dt_to_map(struct pinctrl *p) +{ + struct device_node *np = p->dev->of_node; + int state, ret; + char *propname; + struct property *prop; + const char *statename; + const __be32 *list; + int size, config; + phandle phandle; + struct device_node *np_config; + + /* CONFIG_OF enabled, p->dev not instantiated from DT */ + if (!np) { + dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n"); + return 0; + } + + /* We may store pointers to property names within the node */ + of_node_get(np); + + /* For each defined state ID */ + for (state = 0; ; state++) { + /* Retrieve the pinctrl-* property */ + propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state); + prop = of_find_property(np, propname, &size); + kfree(propname); + if (!prop) + break; + list = prop->value; + size /= sizeof(*list); + + /* Determine whether pinctrl-names property names the state */ + ret = of_property_read_string_index(np, "pinctrl-names", + state, &statename); + /* + * If not, statename is just the integer state ID. But rather + * than dynamically allocate it and have to free it later, + * just point part way into the property name for the string. + */ + if (ret < 0) { + /* strlen("pinctrl-") == 8 */ + statename = prop->name + 8; + } + + /* For every referenced pin configuration node in it */ + for (config = 0; config < size; config++) { + phandle = be32_to_cpup(list++); + + /* Look up the pin configuration node */ + np_config = of_find_node_by_phandle(phandle); + if (!np_config) { + dev_err(p->dev, + "prop %s index %i invalid phandle\n", + prop->name, config); + ret = -EINVAL; + goto err; + } + + /* Parse the node */ + ret = dt_to_map_one_config(p, statename, np_config); + of_node_put(np_config); + if (ret < 0) + goto err; + } + + /* No entries in DT? Generate a dummy state table entry */ + if (!size) { + ret = dt_remember_dummy_state(p, statename); + if (ret < 0) + goto err; + } + } + + return 0; + +err: + pinctrl_dt_free_maps(p); + return ret; +} diff --git a/drivers/pinctrl/devicetree.h b/drivers/pinctrl/devicetree.h new file mode 100644 index 000000000000..760bc4960f58 --- /dev/null +++ b/drivers/pinctrl/devicetree.h @@ -0,0 +1,35 @@ +/* + * Internal interface to pinctrl device tree integration + * + * Copyright (C) 2012 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 . + */ + +#ifdef CONFIG_OF + +void pinctrl_dt_free_maps(struct pinctrl *p); +int pinctrl_dt_to_map(struct pinctrl *p); + +#else + +static inline int pinctrl_dt_to_map(struct pinctrl *p) +{ + return 0; +} + +static inline void pinctrl_dt_free_maps(struct pinctrl *p) +{ +} + +#endif diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 4e9f0788c221..aa92cdeb99fd 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -21,9 +21,11 @@ struct device; struct pinctrl_dev; +struct pinctrl_map; struct pinmux_ops; struct pinconf_ops; struct gpio_chip; +struct device_node; /** * struct pinctrl_pin_desc - boards/machines provide information on their @@ -83,6 +85,11 @@ struct pinctrl_ops { unsigned *num_pins); void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset); + int (*dt_node_to_map) (struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps); + void (*dt_free_map) (struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps); }; /** -- cgit v1.2.3 From d1e90e9e7467dbfe521b25ba79f520bf676ebc36 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 30 Mar 2012 11:25:40 +0530 Subject: pinctrl: replace list_*() with get_*_count() Most of the SoC drivers implement list_groups() and list_functions() routines for pinctrl and pinmux. These routines continue returning zero until the selector argument is greater than total count of available groups or functions. This patch replaces these list_*() routines with get_*_count() routines, which returns the number of available selection for SoC driver. pinctrl layer will use this value to check the range it can choose. This patch fixes all user drivers for this change. There are other routines in user drivers, which have checks to check validity of selector passed to them. It is also no more required and hence removed. Documentation updated as well. Acked-by: Stephen Warren Signed-off-by: Viresh Kumar [Folded in fix and fixed a minor merge artifact manually] Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 37 +++++++++++++++---------------------- drivers/pinctrl/core.c | 10 ++++++---- drivers/pinctrl/pinconf.c | 3 ++- drivers/pinctrl/pinctrl-pxa3xx.c | 24 ++++++++++-------------- drivers/pinctrl/pinctrl-sirf.c | 20 ++++++-------------- drivers/pinctrl/pinctrl-tegra.c | 40 ++++++---------------------------------- drivers/pinctrl/pinctrl-u300.c | 20 ++++++-------------- drivers/pinctrl/pinmux.c | 11 +++++++---- include/linux/pinctrl/pinctrl.h | 6 ++---- include/linux/pinctrl/pinmux.h | 7 +++---- 10 files changed, 63 insertions(+), 115 deletions(-) (limited to 'include/linux') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 2d88b3c7b61c..eb46b1c0b07a 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -152,11 +152,9 @@ static const struct foo_group foo_groups[] = { }; -static int foo_list_groups(struct pinctrl_dev *pctldev, unsigned selector) +static int foo_get_groups_count(struct pinctrl_dev *pctldev) { - if (selector >= ARRAY_SIZE(foo_groups)) - return -EINVAL; - return 0; + return ARRAY_SIZE(foo_groups); } static const char *foo_get_group_name(struct pinctrl_dev *pctldev, @@ -175,7 +173,7 @@ static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, } static struct pinctrl_ops foo_pctrl_ops = { - .list_groups = foo_list_groups, + .get_groups_count = foo_get_groups_count, .get_group_name = foo_get_group_name, .get_group_pins = foo_get_group_pins, }; @@ -186,13 +184,12 @@ static struct pinctrl_desc foo_desc = { .pctlops = &foo_pctrl_ops, }; -The pin control subsystem will call the .list_groups() function repeatedly -beginning on 0 until it returns non-zero to determine legal selectors, then -it will call the other functions to retrieve the name and pins of the group. -Maintaining the data structure of the groups is up to the driver, this is -just a simple example - in practice you may need more entries in your group -structure, for example specific register ranges associated with each group -and so on. +The pin control subsystem will call the .get_groups_count() function to +determine total number of legal selectors, then it will call the other functions +to retrieve the name and pins of the group. Maintaining the data structure of +the groups is up to the driver, this is just a simple example - in practice you +may need more entries in your group structure, for example specific register +ranges associated with each group and so on. Pin configuration @@ -606,11 +603,9 @@ static const struct foo_group foo_groups[] = { }; -static int foo_list_groups(struct pinctrl_dev *pctldev, unsigned selector) +static int foo_get_groups_count(struct pinctrl_dev *pctldev) { - if (selector >= ARRAY_SIZE(foo_groups)) - return -EINVAL; - return 0; + return ARRAY_SIZE(foo_groups); } static const char *foo_get_group_name(struct pinctrl_dev *pctldev, @@ -629,7 +624,7 @@ static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, } static struct pinctrl_ops foo_pctrl_ops = { - .list_groups = foo_list_groups, + .get_groups_count = foo_get_groups_count, .get_group_name = foo_get_group_name, .get_group_pins = foo_get_group_pins, }; @@ -663,11 +658,9 @@ static const struct foo_pmx_func foo_functions[] = { }, }; -int foo_list_funcs(struct pinctrl_dev *pctldev, unsigned selector) +int foo_get_functions_count(struct pinctrl_dev *pctldev) { - if (selector >= ARRAY_SIZE(foo_functions)) - return -EINVAL; - return 0; + return ARRAY_SIZE(foo_functions); } const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector) @@ -703,7 +696,7 @@ void foo_disable(struct pinctrl_dev *pctldev, unsigned selector, } struct pinmux_ops foo_pmxops = { - .list_functions = foo_list_funcs, + .get_functions_count = foo_get_functions_count, .get_function_name = foo_get_fname, .get_function_groups = foo_get_groups, .enable = foo_enable, diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 832f71dcd8c4..7ff869007ba4 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -319,9 +319,10 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, const char *pin_group) { const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + unsigned ngroups = pctlops->get_groups_count(pctldev); unsigned group_selector = 0; - while (pctlops->list_groups(pctldev, group_selector) >= 0) { + while (group_selector < ngroups) { const char *gname = pctlops->get_group_name(pctldev, group_selector); if (!strcmp(gname, pin_group)) { @@ -941,12 +942,13 @@ static int pinctrl_groups_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; const struct pinctrl_ops *ops = pctldev->desc->pctlops; - unsigned selector = 0; + unsigned ngroups, selector = 0; + ngroups = ops->get_groups_count(pctldev); mutex_lock(&pinctrl_mutex); seq_puts(s, "registered pin groups:\n"); - while (ops->list_groups(pctldev, selector) >= 0) { + while (selector < ngroups) { const unsigned *pins; unsigned num_pins; const char *gname = ops->get_group_name(pctldev, selector); @@ -1261,7 +1263,7 @@ static int pinctrl_check_ops(struct pinctrl_dev *pctldev) const struct pinctrl_ops *ops = pctldev->desc->pctlops; if (!ops || - !ops->list_groups || + !ops->get_groups_count || !ops->get_group_name || !ops->get_group_pins) return -EINVAL; diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 7321e8601294..eb3a14f4b866 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -495,6 +495,7 @@ static int pinconf_groups_show(struct seq_file *s, void *what) struct pinctrl_dev *pctldev = s->private; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; const struct pinconf_ops *ops = pctldev->desc->confops; + unsigned ngroups = pctlops->get_groups_count(pctldev); unsigned selector = 0; if (!ops || !ops->pin_config_group_get) @@ -505,7 +506,7 @@ static int pinconf_groups_show(struct seq_file *s, void *what) mutex_lock(&pinctrl_mutex); - while (pctlops->list_groups(pctldev, selector) >= 0) { + while (selector < ngroups) { const char *gname = pctlops->get_group_name(pctldev, selector); seq_printf(s, "%u (%s):", selector, gname); diff --git a/drivers/pinctrl/pinctrl-pxa3xx.c b/drivers/pinctrl/pinctrl-pxa3xx.c index 079dce0e93e9..7644e42ac211 100644 --- a/drivers/pinctrl/pinctrl-pxa3xx.c +++ b/drivers/pinctrl/pinctrl-pxa3xx.c @@ -25,20 +25,18 @@ static struct pinctrl_gpio_range pxa3xx_pinctrl_gpio_range = { .pin_base = 0, }; -static int pxa3xx_list_groups(struct pinctrl_dev *pctrldev, unsigned selector) +static int pxa3xx_get_groups_count(struct pinctrl_dev *pctrldev) { struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - if (selector >= info->num_grps) - return -EINVAL; - return 0; + + return info->num_grps; } static const char *pxa3xx_get_group_name(struct pinctrl_dev *pctrldev, unsigned selector) { struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - if (selector >= info->num_grps) - return NULL; + return info->grps[selector].name; } @@ -48,25 +46,23 @@ static int pxa3xx_get_group_pins(struct pinctrl_dev *pctrldev, unsigned *num_pins) { struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - if (selector >= info->num_grps) - return -EINVAL; + *pins = info->grps[selector].pins; *num_pins = info->grps[selector].npins; return 0; } static struct pinctrl_ops pxa3xx_pctrl_ops = { - .list_groups = pxa3xx_list_groups, + .get_groups_count = pxa3xx_get_groups_count, .get_group_name = pxa3xx_get_group_name, .get_group_pins = pxa3xx_get_group_pins, }; -static int pxa3xx_pmx_list_func(struct pinctrl_dev *pctrldev, unsigned func) +static int pxa3xx_pmx_get_funcs_count(struct pinctrl_dev *pctrldev) { struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); - if (func >= info->num_funcs) - return -EINVAL; - return 0; + + return info->num_funcs; } static const char *pxa3xx_pmx_get_func_name(struct pinctrl_dev *pctrldev, @@ -170,7 +166,7 @@ static int pxa3xx_pmx_request_gpio(struct pinctrl_dev *pctrldev, } static struct pinmux_ops pxa3xx_pmx_ops = { - .list_functions = pxa3xx_pmx_list_func, + .get_functions_count = pxa3xx_pmx_get_funcs_count, .get_function_name = pxa3xx_pmx_get_func_name, .get_function_groups = pxa3xx_pmx_get_groups, .enable = pxa3xx_pmx_enable, diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c index 6b3534cc051a..ba15b1a29e52 100644 --- a/drivers/pinctrl/pinctrl-sirf.c +++ b/drivers/pinctrl/pinctrl-sirf.c @@ -853,18 +853,14 @@ static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = { SIRFSOC_PIN_GROUP("gpsgrp", gps_pins), }; -static int sirfsoc_list_groups(struct pinctrl_dev *pctldev, unsigned selector) +static int sirfsoc_get_groups_count(struct pinctrl_dev *pctldev) { - if (selector >= ARRAY_SIZE(sirfsoc_pin_groups)) - return -EINVAL; - return 0; + return ARRAY_SIZE(sirfsoc_pin_groups); } static const char *sirfsoc_get_group_name(struct pinctrl_dev *pctldev, unsigned selector) { - if (selector >= ARRAY_SIZE(sirfsoc_pin_groups)) - return NULL; return sirfsoc_pin_groups[selector].name; } @@ -872,8 +868,6 @@ static int sirfsoc_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector const unsigned **pins, unsigned *num_pins) { - if (selector >= ARRAY_SIZE(sirfsoc_pin_groups)) - return -EINVAL; *pins = sirfsoc_pin_groups[selector].pins; *num_pins = sirfsoc_pin_groups[selector].num_pins; return 0; @@ -886,7 +880,7 @@ static void sirfsoc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s } static struct pinctrl_ops sirfsoc_pctrl_ops = { - .list_groups = sirfsoc_list_groups, + .get_groups_count = sirfsoc_get_groups_count, .get_group_name = sirfsoc_get_group_name, .get_group_pins = sirfsoc_get_group_pins, .pin_dbg_show = sirfsoc_pin_dbg_show, @@ -1033,11 +1027,9 @@ static void sirfsoc_pinmux_disable(struct pinctrl_dev *pmxdev, unsigned selector sirfsoc_pinmux_endisable(spmx, selector, false); } -static int sirfsoc_pinmux_list_funcs(struct pinctrl_dev *pmxdev, unsigned selector) +static int sirfsoc_pinmux_get_funcs_count(struct pinctrl_dev *pmxdev) { - if (selector >= ARRAY_SIZE(sirfsoc_pmx_functions)) - return -EINVAL; - return 0; + return ARRAY_SIZE(sirfsoc_pmx_functions); } static const char *sirfsoc_pinmux_get_func_name(struct pinctrl_dev *pctldev, @@ -1074,9 +1066,9 @@ static int sirfsoc_pinmux_request_gpio(struct pinctrl_dev *pmxdev, } static struct pinmux_ops sirfsoc_pinmux_ops = { - .list_functions = sirfsoc_pinmux_list_funcs, .enable = sirfsoc_pinmux_enable, .disable = sirfsoc_pinmux_disable, + .get_functions_count = sirfsoc_pinmux_get_funcs_count, .get_function_name = sirfsoc_pinmux_get_func_name, .get_function_groups = sirfsoc_pinmux_get_groups, .gpio_request_enable = sirfsoc_pinmux_request_gpio, diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index 9b329688120c..41311a2a4256 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -53,15 +53,11 @@ static inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg) writel(val, pmx->regs[bank] + reg); } -static int tegra_pinctrl_list_groups(struct pinctrl_dev *pctldev, - unsigned group) +static int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - if (group >= pmx->soc->ngroups) - return -EINVAL; - - return 0; + return pmx->soc->ngroups; } static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev, @@ -69,9 +65,6 @@ static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev, { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - if (group >= pmx->soc->ngroups) - return NULL; - return pmx->soc->groups[group].name; } @@ -82,9 +75,6 @@ static int tegra_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - if (group >= pmx->soc->ngroups) - return -EINVAL; - *pins = pmx->soc->groups[group].pins; *num_pins = pmx->soc->groups[group].npins; @@ -99,21 +89,17 @@ static void tegra_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, } static struct pinctrl_ops tegra_pinctrl_ops = { - .list_groups = tegra_pinctrl_list_groups, + .get_groups_count = tegra_pinctrl_get_groups_count, .get_group_name = tegra_pinctrl_get_group_name, .get_group_pins = tegra_pinctrl_get_group_pins, .pin_dbg_show = tegra_pinctrl_pin_dbg_show, }; -static int tegra_pinctrl_list_funcs(struct pinctrl_dev *pctldev, - unsigned function) +static int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - if (function >= pmx->soc->nfunctions) - return -EINVAL; - - return 0; + return pmx->soc->nfunctions; } static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev, @@ -121,9 +107,6 @@ static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev, { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - if (function >= pmx->soc->nfunctions) - return NULL; - return pmx->soc->functions[function].name; } @@ -134,9 +117,6 @@ static int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - if (function >= pmx->soc->nfunctions) - return -EINVAL; - *groups = pmx->soc->functions[function].groups; *num_groups = pmx->soc->functions[function].ngroups; @@ -151,8 +131,6 @@ static int tegra_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function, int i; u32 val; - if (group >= pmx->soc->ngroups) - return -EINVAL; g = &pmx->soc->groups[group]; if (g->mux_reg < 0) @@ -180,8 +158,6 @@ static void tegra_pinctrl_disable(struct pinctrl_dev *pctldev, const struct tegra_pingroup *g; u32 val; - if (group >= pmx->soc->ngroups) - return; g = &pmx->soc->groups[group]; if (g->mux_reg < 0) @@ -194,7 +170,7 @@ static void tegra_pinctrl_disable(struct pinctrl_dev *pctldev, } static struct pinmux_ops tegra_pinmux_ops = { - .list_functions = tegra_pinctrl_list_funcs, + .get_functions_count = tegra_pinctrl_get_funcs_count, .get_function_name = tegra_pinctrl_get_func_name, .get_function_groups = tegra_pinctrl_get_func_groups, .enable = tegra_pinctrl_enable, @@ -324,8 +300,6 @@ static int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, s16 reg; u32 val, mask; - if (group >= pmx->soc->ngroups) - return -EINVAL; g = &pmx->soc->groups[group]; ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width); @@ -353,8 +327,6 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, s16 reg; u32 val, mask; - if (group >= pmx->soc->ngroups) - return -EINVAL; g = &pmx->soc->groups[group]; ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width); diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 26eb8ccd72d5..05d029911be6 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -836,18 +836,14 @@ static const struct u300_pin_group u300_pin_groups[] = { }, }; -static int u300_list_groups(struct pinctrl_dev *pctldev, unsigned selector) +static int u300_get_groups_count(struct pinctrl_dev *pctldev) { - if (selector >= ARRAY_SIZE(u300_pin_groups)) - return -EINVAL; - return 0; + return ARRAY_SIZE(u300_pin_groups); } static const char *u300_get_group_name(struct pinctrl_dev *pctldev, unsigned selector) { - if (selector >= ARRAY_SIZE(u300_pin_groups)) - return NULL; return u300_pin_groups[selector].name; } @@ -855,8 +851,6 @@ static int u300_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, const unsigned **pins, unsigned *num_pins) { - if (selector >= ARRAY_SIZE(u300_pin_groups)) - return -EINVAL; *pins = u300_pin_groups[selector].pins; *num_pins = u300_pin_groups[selector].num_pins; return 0; @@ -869,7 +863,7 @@ static void u300_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, } static struct pinctrl_ops u300_pctrl_ops = { - .list_groups = u300_list_groups, + .get_groups_count = u300_get_groups_count, .get_group_name = u300_get_group_name, .get_group_pins = u300_get_group_pins, .pin_dbg_show = u300_pin_dbg_show, @@ -991,11 +985,9 @@ static void u300_pmx_disable(struct pinctrl_dev *pctldev, unsigned selector, u300_pmx_endisable(upmx, selector, false); } -static int u300_pmx_list_funcs(struct pinctrl_dev *pctldev, unsigned selector) +static int u300_pmx_get_funcs_count(struct pinctrl_dev *pctldev) { - if (selector >= ARRAY_SIZE(u300_pmx_functions)) - return -EINVAL; - return 0; + return ARRAY_SIZE(u300_pmx_functions); } static const char *u300_pmx_get_func_name(struct pinctrl_dev *pctldev, @@ -1014,7 +1006,7 @@ static int u300_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector, } static struct pinmux_ops u300_pmx_ops = { - .list_functions = u300_pmx_list_funcs, + .get_functions_count = u300_pmx_get_funcs_count, .get_function_name = u300_pmx_get_func_name, .get_function_groups = u300_pmx_get_groups, .enable = u300_pmx_enable, diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c index 4e62783a573a..375b214780e9 100644 --- a/drivers/pinctrl/pinmux.c +++ b/drivers/pinctrl/pinmux.c @@ -33,10 +33,11 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev) { const struct pinmux_ops *ops = pctldev->desc->pmxops; + unsigned nfuncs = ops->get_functions_count(pctldev); unsigned selector = 0; /* Check that we implement required operations */ - if (!ops->list_functions || + if (!ops->get_functions_count || !ops->get_function_name || !ops->get_function_groups || !ops->enable || @@ -44,7 +45,7 @@ int pinmux_check_ops(struct pinctrl_dev *pctldev) return -EINVAL; /* Check that all functions registered have names */ - while (ops->list_functions(pctldev, selector) >= 0) { + while (selector < nfuncs) { const char *fname = ops->get_function_name(pctldev, selector); if (!fname) { @@ -287,10 +288,11 @@ static int pinmux_func_name_to_selector(struct pinctrl_dev *pctldev, const char *function) { const struct pinmux_ops *ops = pctldev->desc->pmxops; + unsigned nfuncs = ops->get_functions_count(pctldev); unsigned selector = 0; /* See if this pctldev has this function */ - while (ops->list_functions(pctldev, selector) >= 0) { + while (selector < nfuncs) { const char *fname = ops->get_function_name(pctldev, selector); @@ -477,11 +479,12 @@ static int pinmux_functions_show(struct seq_file *s, void *what) { struct pinctrl_dev *pctldev = s->private; const struct pinmux_ops *pmxops = pctldev->desc->pmxops; + unsigned nfuncs = pmxops->get_functions_count(pctldev); unsigned func_selector = 0; mutex_lock(&pinctrl_mutex); - while (pmxops->list_functions(pctldev, func_selector) >= 0) { + while (func_selector < nfuncs) { const char *func = pmxops->get_function_name(pctldev, func_selector); const char * const *groups; diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index aa92cdeb99fd..c22d0409d2ef 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -66,9 +66,7 @@ struct pinctrl_gpio_range { /** * struct pinctrl_ops - global pin control operations, to be implemented by * pin controller drivers. - * @list_groups: list the number of selectable named groups available - * in this pinmux driver, the core will begin on 0 and call this - * repeatedly as long as it returns >= 0 to enumerate the groups + * @get_groups_count: Returns the count of total number of groups registered. * @get_group_name: return the group name of the pin group * @get_group_pins: return an array of pins corresponding to a certain * group selector @pins, and the size of the array in @num_pins @@ -76,7 +74,7 @@ struct pinctrl_gpio_range { * info for a certain pin in debugfs */ struct pinctrl_ops { - int (*list_groups) (struct pinctrl_dev *pctldev, unsigned selector); + int (*get_groups_count) (struct pinctrl_dev *pctldev); const char *(*get_group_name) (struct pinctrl_dev *pctldev, unsigned selector); int (*get_group_pins) (struct pinctrl_dev *pctldev, diff --git a/include/linux/pinctrl/pinmux.h b/include/linux/pinctrl/pinmux.h index 47e9237edd47..dd7bef61d066 100644 --- a/include/linux/pinctrl/pinmux.h +++ b/include/linux/pinctrl/pinmux.h @@ -29,9 +29,8 @@ struct pinctrl_dev; * is allowed to answer "no" by returning a negative error code * @free: the reverse function of the request() callback, frees a pin after * being requested - * @list_functions: list the number of selectable named functions available - * in this pinmux driver, the core will begin on 0 and call this - * repeatedly as long as it returns >= 0 to enumerate mux settings + * @get_functions_count: returns number of selectable named functions available + * in this pinmux driver * @get_function_name: return the function name of the muxing selector, * called by the core to figure out which mux setting it shall map a * certain device to @@ -62,7 +61,7 @@ struct pinctrl_dev; struct pinmux_ops { int (*request) (struct pinctrl_dev *pctldev, unsigned offset); int (*free) (struct pinctrl_dev *pctldev, unsigned offset); - int (*list_functions) (struct pinctrl_dev *pctldev, unsigned selector); + int (*get_functions_count) (struct pinctrl_dev *pctldev); const char *(*get_function_name) (struct pinctrl_dev *pctldev, unsigned selector); int (*get_function_groups) (struct pinctrl_dev *pctldev, -- cgit v1.2.3 From c541adc637066407d4cda9db14dcb0e618966a4c Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 4 Apr 2012 09:27:46 -0600 Subject: dt: add property iteration helpers This patch adds macros of_property_for_each_u32() and of_property_for_each_string(), which iterate over an array of values within a device-tree property. Usage is for example: struct property *prop; const __be32 *p; u32 u; of_property_for_each_u32(np, "propname", prop, p, u) printk("U32 value: %x\n", u); struct property *prop; const char *s; of_property_for_each_string(np, "propname", prop, s) printk("String value: %s\n", s); Based on work by Rob Herring Cc: Grant Likely Signed-off-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Linus Walleij --- drivers/of/base.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/linux/of.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) (limited to 'include/linux') diff --git a/drivers/of/base.c b/drivers/of/base.c index 580644986945..d9bfd49b1935 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1260,3 +1260,44 @@ int of_alias_get_id(struct device_node *np, const char *stem) return id; } EXPORT_SYMBOL_GPL(of_alias_get_id); + +const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, + u32 *pu) +{ + const void *curv = cur; + + if (!prop) + return NULL; + + if (!cur) { + curv = prop->value; + goto out_val; + } + + curv += sizeof(*cur); + if (curv >= prop->value + prop->length) + return NULL; + +out_val: + *pu = be32_to_cpup(curv); + return curv; +} +EXPORT_SYMBOL_GPL(of_prop_next_u32); + +const char *of_prop_next_string(struct property *prop, const char *cur) +{ + const void *curv = cur; + + if (!prop) + return NULL; + + if (!cur) + return prop->value; + + curv += strlen(cur) + 1; + if (curv >= prop->value + prop->length) + return NULL; + + return curv; +} +EXPORT_SYMBOL_GPL(of_prop_next_string); diff --git a/include/linux/of.h b/include/linux/of.h index fa7fb1d97458..e3f942d9da89 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -259,6 +259,37 @@ extern void of_detach_node(struct device_node *); #endif #define of_match_ptr(_ptr) (_ptr) + +/* + * struct property *prop; + * const __be32 *p; + * u32 u; + * + * of_property_for_each_u32(np, "propname", prop, p, u) + * printk("U32 value: %x\n", u); + */ +const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, + u32 *pu); +#define of_property_for_each_u32(np, propname, prop, p, u) \ + for (prop = of_find_property(np, propname, NULL), \ + p = of_prop_next_u32(prop, NULL, &u); \ + p; \ + p = of_prop_next_u32(prop, p, &u)) + +/* + * struct property *prop; + * const char *s; + * + * of_property_for_each_string(np, "propname", prop, s) + * printk("String value: %s\n", s); + */ +const char *of_prop_next_string(struct property *prop, const char *cur); +#define of_property_for_each_string(np, propname, prop, s) \ + for (prop = of_find_property(np, propname, NULL), \ + s = of_prop_next_string(prop, NULL); \ + s; \ + s = of_prop_next_string(prop, s)) + #else /* CONFIG_OF */ static inline bool of_have_populated_dt(void) @@ -349,6 +380,10 @@ static inline int of_machine_is_compatible(const char *compat) #define of_match_ptr(_ptr) NULL #define of_match_node(_matches, _node) NULL +#define of_property_for_each_u32(np, propname, prop, p, u) \ + while (0) +#define of_property_for_each_string(np, propname, prop, s) \ + while (0) #endif /* CONFIG_OF */ /** -- cgit v1.2.3 From 6cb4158757a8629e14851e7802f3b6bfaa7d6f00 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 13 Apr 2012 10:49:06 -0600 Subject: pinctrl: allow pctldevs to decode pin config in debugfs Add a pinconf op so that pin controller drivers can decode their pin config settings for debugfs. Signed-off-by: Stephen Warren Signed-off-by: Linus Walleij --- drivers/pinctrl/pinconf.c | 31 +++++++++++++++++++++++++++---- include/linux/pinctrl/pinconf.h | 5 +++++ 2 files changed, 32 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 384dcc166e44..0133a69ad117 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -379,8 +379,16 @@ int pinconf_apply_setting(struct pinctrl_setting const *setting) void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map) { + struct pinctrl_dev *pctldev; + const struct pinconf_ops *confops; int i; + pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name); + if (pctldev) + confops = pctldev->desc->confops; + else + confops = NULL; + switch (map->type) { case PIN_MAP_TYPE_CONFIGS_PIN: seq_printf(s, "pin "); @@ -394,8 +402,15 @@ void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map) seq_printf(s, "%s\n", map->data.configs.group_or_pin); - for (i = 0; i < map->data.configs.num_configs; i++) - seq_printf(s, "config %08lx\n", map->data.configs.configs[i]); + for (i = 0; i < map->data.configs.num_configs; i++) { + seq_printf(s, "config "); + if (confops && confops->pin_config_config_dbg_show) + confops->pin_config_config_dbg_show(pctldev, s, + map->data.configs.configs[i]); + else + seq_printf(s, "%08lx", map->data.configs.configs[i]); + seq_printf(s, "\n"); + } } void pinconf_show_setting(struct seq_file *s, @@ -403,6 +418,7 @@ void pinconf_show_setting(struct seq_file *s, { struct pinctrl_dev *pctldev = setting->pctldev; const struct pinctrl_ops *pctlops = pctldev->desc->pctlops; + const struct pinconf_ops *confops = pctldev->desc->confops; struct pin_desc *desc; int i; @@ -428,8 +444,15 @@ void pinconf_show_setting(struct seq_file *s, * FIXME: We should really get the pin controler to dump the config * values, so they can be decoded to something meaningful. */ - for (i = 0; i < setting->data.configs.num_configs; i++) - seq_printf(s, " %08lx", setting->data.configs.configs[i]); + for (i = 0; i < setting->data.configs.num_configs; i++) { + seq_printf(s, " "); + if (confops && confops->pin_config_config_dbg_show) + confops->pin_config_config_dbg_show(pctldev, s, + setting->data.configs.configs[i]); + else + seq_printf(s, "%08lx", + setting->data.configs.configs[i]); + } seq_printf(s, "\n"); } diff --git a/include/linux/pinctrl/pinconf.h b/include/linux/pinctrl/pinconf.h index ec431f03362d..7b9d5f00ed37 100644 --- a/include/linux/pinctrl/pinconf.h +++ b/include/linux/pinctrl/pinconf.h @@ -33,6 +33,8 @@ struct seq_file; * per-device info for a certain pin in debugfs * @pin_config_group_dbg_show: optional debugfs display hook that will provide * per-device info for a certain group in debugfs + * @pin_config_config_dbg_show: optional debugfs display hook that will decode + * and display a driver's pin configuration parameter */ struct pinconf_ops { #ifdef CONFIG_GENERIC_PINCONF @@ -56,6 +58,9 @@ struct pinconf_ops { void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s, unsigned selector); + void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned long config); }; #endif -- cgit v1.2.3 From 6d4ca1fb467932773da7b808c52f3d7ef4461ba0 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 16 Apr 2012 10:51:00 -0600 Subject: pinctrl: implement devm_pinctrl_get()/put() These functions allow the driver core to automatically clean up any allocations made by drivers, thus leading to simplified drivers. Signed-off-by: Stephen Warren Signed-off-by: Linus Walleij --- Documentation/driver-model/devres.txt | 4 +++ Documentation/pinctrl.txt | 48 ++++++++++++++++++------------ drivers/pinctrl/core.c | 56 +++++++++++++++++++++++++++++++++++ include/linux/pinctrl/consumer.h | 44 +++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 2a596a4fc23e..ef4fa7b423d2 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -276,3 +276,7 @@ REGULATOR devm_regulator_get() devm_regulator_put() devm_regulator_bulk_get() + +PINCTRL + devm_pinctrl_get() + devm_pinctrl_put() diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 4431c3e727ba..e40f4b4e1977 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -945,13 +945,13 @@ case), we define a mapping like this: The result of grabbing this mapping from the device with something like this (see next paragraph): - p = pinctrl_get(dev); + p = devm_pinctrl_get(dev); s = pinctrl_lookup_state(p, "8bit"); ret = pinctrl_select_state(p, s); or more simply: - p = pinctrl_get_select(dev, "8bit"); + p = devm_pinctrl_get_select(dev, "8bit"); Will be that you activate all the three bottom records in the mapping at once. Since they share the same name, pin controller device, function and @@ -985,7 +985,7 @@ foo_probe() /* Allocate a state holder named "foo" etc */ struct foo_state *foo = ...; - foo->p = pinctrl_get(&device); + foo->p = devm_pinctrl_get(&device); if (IS_ERR(foo->p)) { /* FIXME: clean up "foo" here */ return PTR_ERR(foo->p); @@ -993,24 +993,17 @@ foo_probe() foo->s = pinctrl_lookup_state(foo->p, PINCTRL_STATE_DEFAULT); if (IS_ERR(foo->s)) { - pinctrl_put(foo->p); /* FIXME: clean up "foo" here */ return PTR_ERR(s); } ret = pinctrl_select_state(foo->s); if (ret < 0) { - pinctrl_put(foo->p); /* FIXME: clean up "foo" here */ return ret; } } -foo_remove() -{ - pinctrl_put(state->p); -} - This get/lookup/select/put sequence can just as well be handled by bus drivers if you don't want each and every driver to handle it and you know the arrangement on your bus. @@ -1022,6 +1015,11 @@ The semantics of the pinctrl APIs are: kernel memory to hold the pinmux state. All mapping table parsing or similar slow operations take place within this API. +- devm_pinctrl_get() is a variant of pinctrl_get() that causes pinctrl_put() + to be called automatically on the retrieved pointer when the associated + device is removed. It is recommended to use this function over plain + pinctrl_get(). + - pinctrl_lookup_state() is called in process context to obtain a handle to a specific state for a the client device. This operation may be slow too. @@ -1034,14 +1032,25 @@ The semantics of the pinctrl APIs are: - pinctrl_put() frees all information associated with a pinctrl handle. +- devm_pinctrl_put() is a variant of pinctrl_put() that may be used to + explicitly destroy a pinctrl object returned by devm_pinctrl_get(). + However, use of this function will be rare, due to the automatic cleanup + that will occur even without calling it. + + pinctrl_get() must be paired with a plain pinctrl_put(). + pinctrl_get() may not be paired with devm_pinctrl_put(). + devm_pinctrl_get() can optionally be paired with devm_pinctrl_put(). + devm_pinctrl_get() may not be paired with plain pinctrl_put(). + Usually the pin control core handled the get/put pair and call out to the device drivers bookkeeping operations, like checking available functions and the associated pins, whereas the enable/disable pass on to the pin controller driver which takes care of activating and/or deactivating the mux setting by quickly poking some registers. -The pins are allocated for your device when you issue the pinctrl_get() call, -after this you should be able to see this in the debugfs listing of all pins. +The pins are allocated for your device when you issue the devm_pinctrl_get() +call, after this you should be able to see this in the debugfs listing of all +pins. NOTE: the pinctrl system will return -EPROBE_DEFER if it cannot find the requested pinctrl handles, for example if the pinctrl driver has not yet @@ -1092,13 +1101,13 @@ it, disables and releases it, and muxes it in on the pins defined by group B: #include -foo_switch() -{ - struct pinctrl *p; - struct pinctrl_state *s1, *s2; +struct pinctrl *p; +struct pinctrl_state *s1, *s2; +foo_probe() +{ /* Setup */ - p = pinctrl_get(&device); + p = devm_pinctrl_get(&device); if (IS_ERR(p)) ... @@ -1109,7 +1118,10 @@ foo_switch() s2 = pinctrl_lookup_state(foo->p, "pos-B"); if (IS_ERR(s2)) ... +} +foo_switch() +{ /* Enable on position A */ ret = pinctrl_select_state(s1); if (ret < 0) @@ -1123,8 +1135,6 @@ foo_switch() ... ... - - pinctrl_put(p); } The above has to be done from process context. diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 59027ab8347a..2eaa1876534b 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include "core.h" @@ -801,6 +802,61 @@ int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) } EXPORT_SYMBOL_GPL(pinctrl_select_state); +static void devm_pinctrl_release(struct device *dev, void *res) +{ + pinctrl_put(*(struct pinctrl **)res); +} + +/** + * struct devm_pinctrl_get() - Resource managed pinctrl_get() + * @dev: the device to obtain the handle for + * + * If there is a need to explicitly destroy the returned struct pinctrl, + * devm_pinctrl_put() should be used, rather than plain pinctrl_put(). + */ +struct pinctrl *devm_pinctrl_get(struct device *dev) +{ + struct pinctrl **ptr, *p; + + ptr = devres_alloc(devm_pinctrl_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + p = pinctrl_get(dev); + if (!IS_ERR(p)) { + *ptr = p; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return p; +} +EXPORT_SYMBOL_GPL(devm_pinctrl_get); + +static int devm_pinctrl_match(struct device *dev, void *res, void *data) +{ + struct pinctrl **p = res; + + return *p == data; +} + +/** + * devm_pinctrl_put() - Resource managed pinctrl_put() + * @p: the pinctrl handle to release + * + * Deallocate a struct pinctrl obtained via devm_pinctrl_get(). Normally + * this function will not need to be called and the resource management + * code will ensure that the resource is freed. + */ +void devm_pinctrl_put(struct pinctrl *p) +{ + WARN_ON(devres_destroy(p->dev, devm_pinctrl_release, + devm_pinctrl_match, p)); + pinctrl_put(p); +} +EXPORT_SYMBOL_GPL(devm_pinctrl_put); + int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps, bool dup, bool locked) { diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index 191e72688481..6dd96fb45482 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -36,6 +36,9 @@ extern struct pinctrl_state * __must_check pinctrl_lookup_state( const char *name); extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s); +extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev); +extern void devm_pinctrl_put(struct pinctrl *p); + #else /* !CONFIG_PINCTRL */ static inline int pinctrl_request_gpio(unsigned gpio) @@ -79,6 +82,15 @@ static inline int pinctrl_select_state(struct pinctrl *p, return 0; } +static inline struct pinctrl * __must_check devm_pinctrl_get(struct device *dev) +{ + return NULL; +} + +static inline void devm_pinctrl_put(struct pinctrl *p) +{ +} + #endif /* CONFIG_PINCTRL */ static inline struct pinctrl * __must_check pinctrl_get_select( @@ -113,6 +125,38 @@ static inline struct pinctrl * __must_check pinctrl_get_select_default( return pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT); } +static inline struct pinctrl * __must_check devm_pinctrl_get_select( + struct device *dev, const char *name) +{ + struct pinctrl *p; + struct pinctrl_state *s; + int ret; + + p = devm_pinctrl_get(dev); + if (IS_ERR(p)) + return p; + + s = pinctrl_lookup_state(p, name); + if (IS_ERR(s)) { + devm_pinctrl_put(p); + return ERR_PTR(PTR_ERR(s)); + } + + ret = pinctrl_select_state(p, s); + if (ret < 0) { + devm_pinctrl_put(p); + return ERR_PTR(ret); + } + + return p; +} + +static inline struct pinctrl * __must_check devm_pinctrl_get_select_default( + struct device *dev) +{ + return devm_pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT); +} + #ifdef CONFIG_PINCONF extern int pin_config_get(const char *dev_name, const char *name, -- cgit v1.2.3 From 434103adea3f63f6550f4b2bd16653328f933a66 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 16 Mar 2012 16:06:07 -0600 Subject: usb: ehci-tegra: Add vbus_gpio to platform data Add a vbus_gpio field to platform data. This mirrors the device tree property nvidia,vbus-gpio. This makes the VBUS GPIO handling identical between booting with board files and device tree; the driver always does it. This removes the need for board files to request and initialize the GPIO early during their boot process, perhaps even before the GPIO driver is ready to process the request. Cc: Greg Kroah-Hartman Cc: Alan Stern Cc: linux-usb@vger.kernel.org Signed-off-by: Stephen Warren Acked-by: Olof Johansson --- arch/arm/mach-tegra/devices.c | 3 +++ drivers/usb/host/ehci-tegra.c | 13 +++++++------ include/linux/platform_data/tegra_usb.h | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c index 5f6b867e20b4..748b40cb7fcf 100644 --- a/arch/arm/mach-tegra/devices.c +++ b/arch/arm/mach-tegra/devices.c @@ -448,17 +448,20 @@ static struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = { struct tegra_ehci_platform_data tegra_ehci1_pdata = { .operating_mode = TEGRA_USB_OTG, .power_down_on_bus_suspend = 1, + .vbus_gpio = -1, }; struct tegra_ehci_platform_data tegra_ehci2_pdata = { .phy_config = &tegra_ehci2_ulpi_phy_config, .operating_mode = TEGRA_USB_HOST, .power_down_on_bus_suspend = 1, + .vbus_gpio = -1, }; struct tegra_ehci_platform_data tegra_ehci3_pdata = { .operating_mode = TEGRA_USB_HOST, .power_down_on_bus_suspend = 1, + .vbus_gpio = -1, }; static u64 tegra_ehci_dmamask = DMA_BIT_MASK(32); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 73544bd440bd..9692bef159f5 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -581,15 +581,16 @@ static const struct hc_driver tegra_ehci_hc_driver = { .port_handed_over = ehci_port_handed_over, }; -static int setup_vbus_gpio(struct platform_device *pdev) +static int setup_vbus_gpio(struct platform_device *pdev, + struct tegra_ehci_platform_data *pdata) { int err = 0; int gpio; - if (!pdev->dev.of_node) - return 0; - - gpio = of_get_named_gpio(pdev->dev.of_node, "nvidia,vbus-gpio", 0); + gpio = pdata->vbus_gpio; + if (!gpio_is_valid(gpio)) + gpio = of_get_named_gpio(pdev->dev.of_node, + "nvidia,vbus-gpio", 0); if (!gpio_is_valid(gpio)) return 0; @@ -633,7 +634,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) if (!pdev->dev.dma_mask) pdev->dev.dma_mask = &tegra_ehci_dma_mask; - setup_vbus_gpio(pdev); + setup_vbus_gpio(pdev, pdata); tegra = kzalloc(sizeof(struct tegra_ehci_hcd), GFP_KERNEL); if (!tegra) diff --git a/include/linux/platform_data/tegra_usb.h b/include/linux/platform_data/tegra_usb.h index 6bca5b569acb..66c673fef408 100644 --- a/include/linux/platform_data/tegra_usb.h +++ b/include/linux/platform_data/tegra_usb.h @@ -26,6 +26,7 @@ struct tegra_ehci_platform_data { /* power down the phy on bus suspend */ int power_down_on_bus_suspend; void *phy_config; + int vbus_gpio; }; #endif /* _TEGRA_USB_H_ */ -- cgit v1.2.3 From 06c4998be96f2e1f304cf79d5e9d1662d864f7d1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 17 Apr 2012 17:08:56 +0800 Subject: regulator: tps65090: Use generic regmap enable/disable operations This patch converts tps65090 regulator driver to use generic regmap enable/disable operations. Also move struct tps65090 to include/linux/mfd/tps65090.h because the regulator driver needs to access the rmap field of struct tps65090. Signed-off-by: Axel Lin Acked-by: Venu Byravarasu Signed-off-by: Mark Brown --- drivers/mfd/tps65090.c | 11 ------ drivers/regulator/tps65090-regulator.c | 64 ++++------------------------------ include/linux/mfd/tps65090.h | 13 +++++++ 3 files changed, 20 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index a66d4df51293..47f802bf1848 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -78,17 +78,6 @@ static struct mfd_cell tps65090s[] = { }, }; -struct tps65090 { - struct mutex lock; - struct device *dev; - struct i2c_client *client; - struct regmap *rmap; - struct irq_chip irq_chip; - struct mutex irq_lock; - int irq_base; - unsigned int id; -}; - int tps65090_write(struct device *dev, int reg, uint8_t val) { struct tps65090 *tps = dev_get_drvdata(dev); diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 6bbf760be80a..661fcecd1cec 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -29,10 +29,6 @@ struct tps65090_regulator { int id; - /* Regulator register address.*/ - u8 reg_en_reg; - u8 en_bit; - /* used by regulator core */ struct regulator_desc desc; @@ -40,64 +36,14 @@ struct tps65090_regulator { struct device *dev; }; -static inline struct device *to_tps65090_dev(struct regulator_dev *rdev) -{ - return rdev_get_dev(rdev)->parent->parent; -} - -static int tps65090_reg_is_enabled(struct regulator_dev *rdev) -{ - struct tps65090_regulator *ri = rdev_get_drvdata(rdev); - struct device *parent = to_tps65090_dev(rdev); - uint8_t control; - int ret; - - ret = tps65090_read(parent, ri->reg_en_reg, &control); - if (ret < 0) { - dev_err(&rdev->dev, "Error in reading reg 0x%x\n", - ri->reg_en_reg); - return ret; - } - return (((control >> ri->en_bit) & 1) == 1); -} - -static int tps65090_reg_enable(struct regulator_dev *rdev) -{ - struct tps65090_regulator *ri = rdev_get_drvdata(rdev); - struct device *parent = to_tps65090_dev(rdev); - int ret; - - ret = tps65090_set_bits(parent, ri->reg_en_reg, ri->en_bit); - if (ret < 0) - dev_err(&rdev->dev, "Error in updating reg 0x%x\n", - ri->reg_en_reg); - return ret; -} - -static int tps65090_reg_disable(struct regulator_dev *rdev) -{ - struct tps65090_regulator *ri = rdev_get_drvdata(rdev); - struct device *parent = to_tps65090_dev(rdev); - int ret; - - ret = tps65090_clr_bits(parent, ri->reg_en_reg, ri->en_bit); - if (ret < 0) - dev_err(&rdev->dev, "Error in updating reg 0x%x\n", - ri->reg_en_reg); - - return ret; -} - static struct regulator_ops tps65090_ops = { - .enable = tps65090_reg_enable, - .disable = tps65090_reg_disable, - .is_enabled = tps65090_reg_is_enabled, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, }; #define tps65090_REG(_id) \ { \ - .reg_en_reg = (TPS65090_ID_##_id) + 12, \ - .en_bit = 0, \ .id = TPS65090_ID_##_id, \ .desc = { \ .name = tps65090_rails(_id), \ @@ -105,6 +51,8 @@ static struct regulator_ops tps65090_ops = { .ops = &tps65090_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .enable_reg = (TPS65090_ID_##_id) + 12, \ + .enable_mask = BIT(0), \ }, \ } @@ -136,6 +84,7 @@ static inline struct tps65090_regulator *find_regulator_info(int id) static int __devinit tps65090_regulator_probe(struct platform_device *pdev) { + struct tps65090 *tps65090_mfd = dev_get_drvdata(pdev->dev.parent); struct tps65090_regulator *ri = NULL; struct regulator_config config = { }; struct regulator_dev *rdev; @@ -155,6 +104,7 @@ static int __devinit tps65090_regulator_probe(struct platform_device *pdev) config.dev = &pdev->dev; config.init_data = &tps_pdata->regulator; config.driver_data = ri; + config.regmap = tps65090_mfd->rmap; rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 38e31c55adbb..6bc31d854626 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -22,6 +22,19 @@ #ifndef __LINUX_MFD_TPS65090_H #define __LINUX_MFD_TPS65090_H +#include + +struct tps65090 { + struct mutex lock; + struct device *dev; + struct i2c_client *client; + struct regmap *rmap; + struct irq_chip irq_chip; + struct mutex irq_lock; + int irq_base; + unsigned int id; +}; + struct tps65090_subdev_info { int id; const char *name; -- cgit v1.2.3 From e167d9fbb881c030f93563fd364c8a0b8c5cd6d3 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 15 Mar 2012 23:49:56 +0100 Subject: bcma: scan for extra address space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some cores like the USB core have two address spaces. In the USB host controller one address space is used for the OHCI and the other for the EHCI controller interface. The USB controller is the only core I found with two address spaces. This code is based on the AI scan function ai_scan() in shared/aiutils.c in the Broadcom SDK. CC: Rafał Miłecki CC: linux-wireless@vger.kernel.org Signed-off-by: Hauke Mehrtens Signed-off-by: Greg Kroah-Hartman --- drivers/bcma/scan.c | 19 ++++++++++++++++++- include/linux/bcma/bcma.h | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index f94cccccfa56..3bea7fe25b20 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -297,6 +297,23 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr, return -EILSEQ; } + /* First Slave Address Descriptor should be port 0: + * the main register space for the core + */ + tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SLAVE, 0); + if (tmp <= 0) { + /* Try again to see if it is a bridge */ + tmp = bcma_erom_get_addr_desc(bus, eromptr, + SCAN_ADDR_TYPE_BRIDGE, 0); + if (tmp <= 0) { + return -EILSEQ; + } else { + pr_info("Bridge found\n"); + return -ENXIO; + } + } + core->addr = tmp; + /* get & parse slave ports */ for (i = 0; i < ports[1]; i++) { for (j = 0; ; j++) { @@ -309,7 +326,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr, break; } else { if (i == 0 && j == 0) - core->addr = tmp; + core->addr1 = tmp; } } } diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 5af9a075498f..98bb2901d7b7 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -139,6 +139,7 @@ struct bcma_device { u8 core_unit; u32 addr; + u32 addr1; u32 wrap; void __iomem *io_addr; -- cgit v1.2.3 From 67c88382e0bf92d7e09536ac47674c9fc9398b98 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 26 Mar 2012 21:01:50 +0200 Subject: USB: add EOPNOTSUPP to usb_translate_errors Allow drivers to return EOPNOTSUPP to user space even when filtered through usb_translate_errors. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/usb.h b/include/linux/usb.h index 73b68d1f2cb0..22e9b328975b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1627,6 +1627,7 @@ static inline int usb_translate_errors(int error_code) case 0: case -ENOMEM: case -ENODEV: + case -EOPNOTSUPP: return error_code; default: return -EIO; -- cgit v1.2.3 From 58c559e6509f276d0afb4621b2122e994e70160c Mon Sep 17 00:00:00 2001 From: Ramneek Mehresh Date: Tue, 20 Mar 2012 10:35:50 +0530 Subject: fsl/usb: Add controller version based ULPI and UTMI phy support Add support for ULPI and UTMI PHYs based on usb controller version info read from device-tree Example of USB Controller versioning info: Version 1.2 and below : MPC8536, MPC8315, etc Version 1.6 : P1020, P1010, P2020, P5020, etc Version 2.2 : PSC9131, PSC9132, P3060, etc No changes for non-DT users Signed-off-by: Ramneek Mehresh Acked-by: Li Yang Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/fsl_udc_core.c | 28 +++++++++++++++++++++----- drivers/usb/gadget/fsl_usb2_udc.h | 11 +++++++++++ drivers/usb/host/ehci-fsl.c | 35 ++++++++++++++++++++++++++------- drivers/usb/host/ehci-fsl.h | 13 ++++++++++++- drivers/usb/host/fsl-mph-dr-of.c | 41 +++++++++++++++++++++++++++++++++++++++ include/linux/fsl_devices.h | 9 ++++++++- 6 files changed, 123 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 5f94e79cd6b9..6087fa698a38 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2007,2011 Freescale Semiconductor, Inc. + * Copyright (C) 2004-2007,2011-2012 Freescale Semiconductor, Inc. * All rights reserved. * * Author: Li Yang @@ -58,9 +58,8 @@ static const char driver_name[] = "fsl-usb2-udc"; static const char driver_desc[] = DRIVER_DESC; static struct usb_dr_device *dr_regs; -#ifndef CONFIG_ARCH_MXC + static struct usb_sys_interface *usb_sys_regs; -#endif /* it is initialized in probe() */ static struct fsl_udc *udc_controller = NULL; @@ -244,10 +243,9 @@ static int dr_controller_setup(struct fsl_udc *udc) { unsigned int tmp, portctrl, ep_num; unsigned int max_no_of_ep; -#ifndef CONFIG_ARCH_MXC unsigned int ctrl; -#endif unsigned long timeout; + #define FSL_UDC_RESET_TIMEOUT 1000 /* Config PHY interface */ @@ -255,12 +253,32 @@ static int dr_controller_setup(struct fsl_udc *udc) portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH); switch (udc->phy_mode) { case FSL_USB2_PHY_ULPI: + if (udc->pdata->have_sysif_regs) { + if (udc->pdata->controller_ver) { + /* controller version 1.6 or above */ + ctrl = __raw_readl(&usb_sys_regs->control); + ctrl &= ~USB_CTRL_UTMI_PHY_EN; + ctrl |= USB_CTRL_USB_EN; + __raw_writel(ctrl, &usb_sys_regs->control); + } + } portctrl |= PORTSCX_PTS_ULPI; break; case FSL_USB2_PHY_UTMI_WIDE: portctrl |= PORTSCX_PTW_16BIT; /* fall through */ case FSL_USB2_PHY_UTMI: + if (udc->pdata->have_sysif_regs) { + if (udc->pdata->controller_ver) { + /* controller version 1.6 or above */ + ctrl = __raw_readl(&usb_sys_regs->control); + ctrl |= (USB_CTRL_UTMI_PHY_EN | + USB_CTRL_USB_EN); + __raw_writel(ctrl, &usb_sys_regs->control); + mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI + PHY CLK to become stable - 10ms*/ + } + } portctrl |= PORTSCX_PTS_UTMI; break; case FSL_USB2_PHY_SERIAL: diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index e651469fd39b..1212646841a3 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -1,4 +1,12 @@ /* + * Copyright (C) 2004,2012 Freescale Semiconductor, 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; either version 2 of the License, or (at your + * option) any later version. + * * Freescale USB device/endpoint management registers */ #ifndef __FSL_USB2_UDC_H @@ -348,6 +356,9 @@ struct usb_sys_interface { /* control Register Bit Masks */ #define USB_CTRL_IOENB 0x00000004 #define USB_CTRL_ULPI_INT0EN 0x00000001 +#define USB_CTRL_UTMI_PHY_EN 0x00000200 +#define USB_CTRL_USB_EN 0x00000004 +#define USB_CTRL_ULPI_PHY_CLK_SEL 0x00000400 /* Endpoint Queue Head data struct * Rem: all the variables of qh are LittleEndian Mode diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 3e7345172e03..653e577ab3ee 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -1,6 +1,6 @@ /* * Copyright 2005-2009 MontaVista Software, Inc. - * Copyright 2008 Freescale Semiconductor, Inc. + * Copyright 2008,2012 Freescale Semiconductor, 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 @@ -211,19 +211,32 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, usb_put_hcd(hcd); } -static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, +static void ehci_fsl_setup_phy(struct usb_hcd *hcd, enum fsl_usb2_phy_modes phy_mode, unsigned int port_offset) { - u32 portsc; - struct usb_hcd *hcd = ehci_to_hcd(ehci); + u32 portsc, temp; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); void __iomem *non_ehci = hcd->regs; + struct device *dev = hcd->self.controller; + struct fsl_usb2_platform_data *pdata = dev->platform_data; + + if (pdata->controller_ver < 0) { + dev_warn(hcd->self.controller, "Could not get controller version\n"); + return; + } portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]); portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW); switch (phy_mode) { case FSL_USB2_PHY_ULPI: + if (pdata->controller_ver) { + /* controller version 1.6 or above */ + temp = in_be32(non_ehci + FSL_SOC_USB_CTRL); + out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | + USB_CTRL_USB_EN | ULPI_PHY_CLK_SEL); + } portsc |= PORT_PTS_ULPI; break; case FSL_USB2_PHY_SERIAL: @@ -233,6 +246,14 @@ static void ehci_fsl_setup_phy(struct ehci_hcd *ehci, portsc |= PORT_PTS_PTW; /* fall through */ case FSL_USB2_PHY_UTMI: + if (pdata->controller_ver) { + /* controller version 1.6 or above */ + temp = in_be32(non_ehci + FSL_SOC_USB_CTRL); + out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | + UTMI_PHY_EN | USB_CTRL_USB_EN); + mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI PHY CLK to + become stable - 10ms*/ + } /* enable UTMI PHY */ setbits32(non_ehci + FSL_SOC_USB_CTRL, CTRL_UTMI_PHY_EN); portsc |= PORT_PTS_UTMI; @@ -271,7 +292,7 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) if ((pdata->operating_mode == FSL_USB2_DR_HOST) || (pdata->operating_mode == FSL_USB2_DR_OTG)) - ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0); + ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0); if (pdata->operating_mode == FSL_USB2_MPH_HOST) { unsigned int chip, rev, svr; @@ -285,9 +306,9 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci) ehci->has_fsl_port_bug = 1; if (pdata->port_enables & FSL_USB2_PORT0_ENABLED) - ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0); + ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0); if (pdata->port_enables & FSL_USB2_PORT1_ENABLED) - ehci_fsl_setup_phy(ehci, pdata->phy_mode, 1); + ehci_fsl_setup_phy(hcd, pdata->phy_mode, 1); } if (pdata->have_sysif_regs) { diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index 863fb0c080d7..88403684d10b 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2005-2010 Freescale Semiconductor, Inc. +/* Copyright (C) 2005-2010,2012 Freescale Semiconductor, Inc. * Copyright (c) 2005 MontaVista Software * * This program is free software; you can redistribute it and/or modify it @@ -50,4 +50,15 @@ #define CTRL_UTMI_PHY_EN (1<<9) #define CTRL_PHY_CLK_VALID (1 << 17) #define SNOOP_SIZE_2GB 0x1e + +/* control Register Bit Masks */ +#define ULPI_INT_EN (1<<0) +#define WU_INT_EN (1<<1) +#define USB_CTRL_USB_EN (1<<2) +#define LINE_STATE_FILTER__EN (1<<3) +#define KEEP_OTG_ON (1<<4) +#define OTG_PORT (1<<5) +#define PLL_RESET (1<<8) +#define UTMI_PHY_EN (1<<9) +#define ULPI_PHY_CLK_SEL (1<<10) #endif /* _EHCI_FSL_H */ diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index ab333ac6071d..22ff6b3a676f 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -119,6 +119,39 @@ error: static const struct of_device_id fsl_usb2_mph_dr_of_match[]; +static int usb_get_ver_info(struct device_node *np) +{ + int ver = -1; + + /* + * returns 1 for usb controller version 1.6 + * returns 2 for usb controller version 2.2 + * returns 0 otherwise + */ + if (of_device_is_compatible(np, "fsl-usb2-dr")) { + if (of_device_is_compatible(np, "fsl-usb2-dr-v1.6")) + ver = FSL_USB_VER_1_6; + else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.2")) + ver = FSL_USB_VER_2_2; + else /* for previous controller versions */ + ver = FSL_USB_VER_OLD; + + if (ver > -1) + return ver; + } + + if (of_device_is_compatible(np, "fsl-usb2-mph")) { + if (of_device_is_compatible(np, "fsl-usb2-mph-v1.6")) + ver = FSL_USB_VER_1_6; + else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.2")) + ver = FSL_USB_VER_2_2; + else /* for previous controller versions */ + ver = FSL_USB_VER_OLD; + } + + return ver; +} + static int __devinit fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev) { struct device_node *np = ofdev->dev.of_node; @@ -166,6 +199,14 @@ static int __devinit fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev) prop = of_get_property(np, "phy_type", NULL); pdata->phy_mode = determine_usb_phy(prop); + pdata->controller_ver = usb_get_ver_info(np); + + if (pdata->have_sysif_regs) { + if (pdata->controller_ver < 0) { + dev_warn(&ofdev->dev, "Could not get controller version\n"); + return -ENODEV; + } + } for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) { if (!dev_data->drivers[i]) diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index fffdf00f87b9..15be561e7397 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -6,7 +6,7 @@ * * Maintainer: Kumar Gala * - * Copyright 2004 Freescale Semiconductor, Inc + * Copyright 2004,2012 Freescale Semiconductor, 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 @@ -17,6 +17,12 @@ #ifndef _FSL_DEVICE_H_ #define _FSL_DEVICE_H_ +#define FSL_UTMI_PHY_DLY 10 /*As per P1010RM, delay for UTMI + PHY CLK to become stable - 10ms*/ +#define FSL_USB_VER_OLD 0 +#define FSL_USB_VER_1_6 1 +#define FSL_USB_VER_2_2 2 + #include /* @@ -63,6 +69,7 @@ struct platform_device; struct fsl_usb2_platform_data { /* board specific information */ + int controller_ver; enum fsl_usb2_operating_modes operating_mode; enum fsl_usb2_phy_modes phy_mode; unsigned int port_enables; -- cgit v1.2.3 From 3e0c70d050c7ed6d163897a6ac894f063c31b10f Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Thu, 12 Apr 2012 15:48:49 +0900 Subject: usb: ehci-sh: Add PHY init function with platform data In devices using ehci-sh, initialization of the PHY may be necessary. This adds platform data to ehci-sh and provide function to initialize PHY. Signed-off-by: Nobuhiro Iwamatsu CC: Shimoda, Yoshihiro Acked-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sh.c | 8 ++++++++ include/linux/platform_data/ehci-sh.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 include/linux/platform_data/ehci-sh.h (limited to 'include/linux') diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 9d9cf47d80da..ca819cdd0c5e 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -11,6 +11,7 @@ */ #include #include +#include struct ehci_sh_priv { struct clk *iclk, *fclk; @@ -100,6 +101,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) const struct hc_driver *driver = &ehci_sh_hc_driver; struct resource *res; struct ehci_sh_priv *priv; + struct ehci_sh_platdata *pdata; struct usb_hcd *hcd; int irq, ret; @@ -124,6 +126,9 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) goto fail_create_hcd; } + if (pdev->dev.platform_data != NULL) + pdata = pdev->dev.platform_data; + /* initialize hcd */ hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev, dev_name(&pdev->dev)); @@ -168,6 +173,9 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) clk_enable(priv->fclk); clk_enable(priv->iclk); + if (pdata && pdata->phy_init) + pdata->phy_init(); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret != 0) { dev_err(&pdev->dev, "Failed to add hcd"); diff --git a/include/linux/platform_data/ehci-sh.h b/include/linux/platform_data/ehci-sh.h new file mode 100644 index 000000000000..5c15a738e116 --- /dev/null +++ b/include/linux/platform_data/ehci-sh.h @@ -0,0 +1,28 @@ +/* + * EHCI SuperH driver platform data + * + * Copyright (C) 2012 Nobuhiro Iwamatsu + * Copyright (C) 2012 Renesas Solutions Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __USB_EHCI_SH_H +#define __USB_EHCI_SH_H + +struct ehci_sh_platdata { + void (*phy_init)(void); /* Phy init function */ +}; + +#endif /* __USB_EHCI_SH_H */ -- cgit v1.2.3 From b4161f0bb5f815ca6d8108062b8e3b650c18fe39 Mon Sep 17 00:00:00 2001 From: Ivo Sieben Date: Wed, 18 Apr 2012 08:29:34 +0200 Subject: Support M95040 SPI EEPROM Updated the generic SPI EEPROM driver AT25 for support of an additional address bit in the instruction byte. Certain EEPROMS have a size that is larger than the number of address bytes would allow (e.g. like M95040 from ST that has 512 Byte size but uses only one address byte (A0 to A7) for addressing.) For the extra address bit (A8, A16 or A24) bit 3 of the instruction byte is used. This instruction bit is normally defined as don't care for other AT25 like chips. Reviewed-by: Wolfram Sang Signed-off-by: Ivo Sieben Acked-by: Chris Wright Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 19 ++++++++++++++++--- include/linux/spi/eeprom.h | 10 ++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 01ab3c9b4cf7..0842c2994ee2 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -50,6 +50,7 @@ struct at25_data { #define AT25_SR_BP1 0x08 #define AT25_SR_WPEN 0x80 /* writeprotect enable */ +#define AT25_INSTR_BIT3 0x08 /* Additional address bit in instr */ #define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */ @@ -75,6 +76,7 @@ at25_ee_read( ssize_t status; struct spi_transfer t[2]; struct spi_message m; + u8 instr; if (unlikely(offset >= at25->bin.size)) return 0; @@ -84,7 +86,12 @@ at25_ee_read( return count; cp = command; - *cp++ = AT25_READ; + + instr = AT25_READ; + if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR) + if (offset >= (1U << (at25->addrlen * 8))) + instr |= AT25_INSTR_BIT3; + *cp++ = instr; /* 8/16/24-bit address is written MSB first */ switch (at25->addrlen) { @@ -167,14 +174,14 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off, /* For write, rollover is within the page ... so we write at * most one page, then manually roll over to the next page. */ - bounce[0] = AT25_WRITE; mutex_lock(&at25->lock); do { unsigned long timeout, retries; unsigned segment; unsigned offset = (unsigned) off; - u8 *cp = bounce + 1; + u8 *cp = bounce; int sr; + u8 instr; *cp = AT25_WREN; status = spi_write(at25->spi, cp, 1); @@ -184,6 +191,12 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off, break; } + instr = AT25_WRITE; + if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR) + if (offset >= (1U << (at25->addrlen * 8))) + instr |= AT25_INSTR_BIT3; + *cp++ = instr; + /* 8/16/24-bit address is written MSB first */ switch (at25->addrlen) { default: /* case 3 */ diff --git a/include/linux/spi/eeprom.h b/include/linux/spi/eeprom.h index 306e7b1c69ed..403e007aef68 100644 --- a/include/linux/spi/eeprom.h +++ b/include/linux/spi/eeprom.h @@ -20,6 +20,16 @@ struct spi_eeprom { #define EE_ADDR3 0x0004 /* 24 bit addrs */ #define EE_READONLY 0x0008 /* disallow writes */ + /* + * Certain EEPROMS have a size that is larger than the number of address + * bytes would allow (e.g. like M95040 from ST that has 512 Byte size + * but uses only one address byte (A0 to A7) for addressing.) For + * the extra address bit (A8, A16 or A24) bit 3 of the instruction byte + * is used. This instruction bit is normally defined as don't care for + * other AT25 like chips. + */ +#define EE_INSTR_BIT3_IS_ADDR 0x0010 + /* for exporting this chip's data to other kernel code */ void (*setup)(struct memory_accessor *mem, void *context); void *context; -- cgit v1.2.3 From bf03f65b7967df5807ddef7b99f8a41d4c94fc70 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 10 Apr 2012 14:10:53 -0700 Subject: tegra, serial8250: add ->handle_break() uart_port op The "KT" serial port has another use case for a "received break" quirk, so before adding another special case to the 8250 core take this opportunity to push such quirks out of the core and into a uart_port op. Stephen says: "If the callback function is to no longer live in 8250.c itself, arch/arm/mach-tegra/devices.c isn't logically a good place to put it, and that file will be going away once we get rid of all the board files and move solely to device tree." ...so since 8250_pci.c houses all the quirks for pci serial devices this quirk is similarly housed in of_serial.c. Once the open firmware conversion completes the infrastructure details (include/linux/of_serial.h, and the export) can all be removed to make this self contained to of_serial.c. Cc: Nhan H Mai Cc: Colin Cross Cc: Olof Johansson [stephen: kill CONFIG_SERIAL_TEGRA in favor just using CONFIG_ARCH_TEGRA] Cc: Grant Likely Acked-by: Arnd Bergmann Acked-by: Sudhakar Mamillapalli Reported-by: Alan Cox Acked-by: Alan Cox Signed-off-by: Dan Williams Acked-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-tegra/board-harmony.c | 2 ++ arch/arm/mach-tegra/board-paz00.c | 3 +++ arch/arm/mach-tegra/board-seaboard.c | 2 ++ arch/arm/mach-tegra/board-trimslice.c | 2 ++ drivers/tty/serial/8250/8250.c | 34 +++------------------------------- drivers/tty/serial/of_serial.c | 26 ++++++++++++++++++++++++++ include/linux/of_serial.h | 17 +++++++++++++++++ include/linux/serial_8250.h | 1 + include/linux/serial_core.h | 5 +++++ 9 files changed, 61 insertions(+), 31 deletions(-) create mode 100644 include/linux/of_serial.h (limited to 'include/linux') diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c index c00aadb01e09..222182e00226 100644 --- a/arch/arm/mach-tegra/board-harmony.c +++ b/arch/arm/mach-tegra/board-harmony.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTD, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c index 330afdfa2475..d0735c70d688 100644 --- a/arch/arm/mach-tegra/board-paz00.c +++ b/arch/arm/mach-tegra/board-paz00.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTA, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, @@ -65,6 +67,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTC, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, diff --git a/arch/arm/mach-tegra/board-seaboard.c b/arch/arm/mach-tegra/board-seaboard.c index d669847f0485..5b687b8258c0 100644 --- a/arch/arm/mach-tegra/board-seaboard.c +++ b/arch/arm/mach-tegra/board-seaboard.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { /* Memory and IRQ filled in before registration */ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, diff --git a/arch/arm/mach-tegra/board-trimslice.c b/arch/arm/mach-tegra/board-trimslice.c index cd52820a3e37..f7358586b523 100644 --- a/arch/arm/mach-tegra/board-trimslice.c +++ b/arch/arm/mach-tegra/board-trimslice.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTA, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 5b149b466ec8..dffd623b7974 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -1331,27 +1331,6 @@ static void serial8250_enable_ms(struct uart_port *port) serial_port_out(port, UART_IER, up->ier); } -/* - * Clear the Tegra rx fifo after a break - * - * FIXME: This needs to become a port specific callback once we have a - * framework for this - */ -static void clear_rx_fifo(struct uart_8250_port *up) -{ - unsigned int status, tmout = 10000; - do { - status = serial_in(up, UART_LSR); - if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) - status = serial_in(up, UART_RX); - else - break; - if (--tmout == 0) - break; - udelay(1); - } while (1); -} - /* * serial8250_rx_chars: processes according to the passed in LSR * value, and returns the remaining LSR bits not handled @@ -1386,19 +1365,9 @@ serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) up->lsr_saved_flags = 0; if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { - /* - * For statistics only - */ if (lsr & UART_LSR_BI) { lsr &= ~(UART_LSR_FE | UART_LSR_PE); port->icount.brk++; - /* - * If tegra port then clear the rx fifo to - * accept another break/character. - */ - if (port->type == PORT_TEGRA) - clear_rx_fifo(up); - /* * We do the SysRQ and SAK checking * here because otherwise the break @@ -3037,6 +3006,7 @@ static int __devinit serial8250_probe(struct platform_device *dev) port.serial_in = p->serial_in; port.serial_out = p->serial_out; port.handle_irq = p->handle_irq; + port.handle_break = p->handle_break; port.set_termios = p->set_termios; port.pm = p->pm; port.dev = &dev->dev; @@ -3209,6 +3179,8 @@ int serial8250_register_port(struct uart_port *port) uart->port.set_termios = port->set_termios; if (port->pm) uart->port.pm = port->pm; + if (port->handle_break) + uart->port.handle_break = port->handle_break; if (serial8250_isa_config != NULL) serial8250_isa_config(0, &uart->port, diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index e8c9cee07d00..5410c0637266 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -12,10 +12,13 @@ #include #include #include +#include #include #include +#include #include #include +#include #include #include @@ -24,6 +27,26 @@ struct of_serial_info { int line; }; +#ifdef CONFIG_ARCH_TEGRA +void tegra_serial_handle_break(struct uart_port *p) +{ + unsigned int status, tmout = 10000; + + do { + status = p->serial_in(p, UART_LSR); + if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) + status = p->serial_in(p, UART_RX); + else + break; + if (--tmout == 0) + break; + udelay(1); + } while (1); +} +/* FIXME remove this export when tegra finishes conversion to open firmware */ +EXPORT_SYMBOL_GPL(tegra_serial_handle_break); +#endif + /* * Fill a struct uart_port for a given device node */ @@ -84,6 +107,9 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, | UPF_FIXED_PORT | UPF_FIXED_TYPE; port->dev = &ofdev->dev; + if (type == PORT_TEGRA) + port->handle_break = tegra_serial_handle_break; + return 0; } diff --git a/include/linux/of_serial.h b/include/linux/of_serial.h new file mode 100644 index 000000000000..4a73ed80b4c0 --- /dev/null +++ b/include/linux/of_serial.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_OF_SERIAL_H +#define __LINUX_OF_SERIAL_H + +/* + * FIXME remove this file when tegra finishes conversion to open firmware, + * expectation is that all quirks will then be self-contained in + * drivers/tty/serial/of_serial.c. + */ +#ifdef CONFIG_ARCH_TEGRA +extern void tegra_serial_handle_break(struct uart_port *port); +#else +static inline void tegra_serial_handle_break(struct uart_port *port) +{ +} +#endif + +#endif /* __LINUX_OF_SERIAL */ diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 8f012f8ac8e9..a522fd977aad 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -38,6 +38,7 @@ struct plat_serial8250_port { int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned old); + void (*handle_break)(struct uart_port *); }; /* diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index f51bf2e70c69..0dd752f3039d 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -310,6 +310,7 @@ struct uart_port { int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned int old); + void (*handle_break)(struct uart_port *); unsigned int irq; /* irq number */ unsigned long irqflags; /* irq flags */ unsigned int uartclk; /* base uart clock */ @@ -533,6 +534,10 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) static inline int uart_handle_break(struct uart_port *port) { struct uart_state *state = port->state; + + if (port->handle_break) + port->handle_break(port); + #ifdef SUPPORT_SYSRQ if (port->cons && port->cons->index == port->line) { if (!port->sysrq) { -- cgit v1.2.3 From a15d49fd3094cff90e5410ca454a870e0a722fe1 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 16 Apr 2012 15:06:25 +0200 Subject: driver core: check start node in klist_iter_init_node klist_iter_init_node() takes a node as a start argument. However, this node might not be valid anymore. This patch updates the klist_iter_init_node() and dependent functions to return an error if so. All calling functions have been audited to check for a return code here. Signed-off-by: Hannes Reinecke Cc: Greg Kroah-Hartmann Cc: Kay Sievers Cc: Stable Kernel Cc: Linux Kernel Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 46 +++++++++++++++++++++++++++++----------------- drivers/base/class.c | 32 ++++++++++++++++++++------------ drivers/base/driver.c | 18 +++++++++++------- include/linux/device.h | 10 +++++----- include/linux/klist.h | 2 +- lib/klist.c | 14 ++++++++++---- 6 files changed, 76 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 2bcef657a60c..76aed01a8b2c 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -296,11 +296,13 @@ int bus_for_each_dev(struct bus_type *bus, struct device *start, if (!bus) return -EINVAL; - klist_iter_init_node(&bus->p->klist_devices, &i, - (start ? &start->p->knode_bus : NULL)); - while ((dev = next_device(&i)) && !error) - error = fn(dev, data); - klist_iter_exit(&i); + error = klist_iter_init_node(&bus->p->klist_devices, &i, + (start ? &start->p->knode_bus : NULL)); + if (!error) { + while ((dev = next_device(&i)) && !error) + error = fn(dev, data); + klist_iter_exit(&i); + } return error; } EXPORT_SYMBOL_GPL(bus_for_each_dev); @@ -330,8 +332,10 @@ struct device *bus_find_device(struct bus_type *bus, if (!bus) return NULL; - klist_iter_init_node(&bus->p->klist_devices, &i, - (start ? &start->p->knode_bus : NULL)); + if (klist_iter_init_node(&bus->p->klist_devices, &i, + (start ? &start->p->knode_bus : NULL)) < 0) + return NULL; + while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; @@ -384,7 +388,9 @@ struct device *subsys_find_device_by_id(struct bus_type *subsys, unsigned int id return NULL; if (hint) { - klist_iter_init_node(&subsys->p->klist_devices, &i, &hint->p->knode_bus); + if (klist_iter_init_node(&subsys->p->klist_devices, &i, + &hint->p->knode_bus) < 0) + return NULL; dev = next_device(&i); if (dev && dev->id == id && get_device(dev)) { klist_iter_exit(&i); @@ -446,11 +452,13 @@ int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, if (!bus) return -EINVAL; - klist_iter_init_node(&bus->p->klist_drivers, &i, - start ? &start->p->knode_bus : NULL); - while ((drv = next_driver(&i)) && !error) - error = fn(drv, data); - klist_iter_exit(&i); + error = klist_iter_init_node(&bus->p->klist_drivers, &i, + start ? &start->p->knode_bus : NULL); + if (!error) { + while ((drv = next_driver(&i)) && !error) + error = fn(drv, data); + klist_iter_exit(&i); + } return error; } EXPORT_SYMBOL_GPL(bus_for_each_drv); @@ -1111,15 +1119,19 @@ EXPORT_SYMBOL_GPL(bus_sort_breadthfirst); * otherwise if it is NULL, the iteration starts at the beginning of * the list. */ -void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, - struct device *start, const struct device_type *type) +int subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, + struct device *start, const struct device_type *type) { struct klist_node *start_knode = NULL; + int error; if (start) start_knode = &start->p->knode_bus; - klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, start_knode); - iter->type = type; + error = klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, + start_knode); + if (!error) + iter->type = type; + return error; } EXPORT_SYMBOL_GPL(subsys_dev_iter_init); diff --git a/drivers/base/class.c b/drivers/base/class.c index 03243d4002fd..23dbc661d4a0 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -301,15 +301,20 @@ void class_destroy(struct class *cls) * otherwise if it is NULL, the iteration starts at the beginning of * the list. */ -void class_dev_iter_init(struct class_dev_iter *iter, struct class *class, - struct device *start, const struct device_type *type) +int class_dev_iter_init(struct class_dev_iter *iter, struct class *class, + struct device *start, const struct device_type *type) { struct klist_node *start_knode = NULL; + int error; if (start) start_knode = &start->knode_class; - klist_iter_init_node(&class->p->klist_devices, &iter->ki, start_knode); - iter->type = type; + error = klist_iter_init_node(&class->p->klist_devices, &iter->ki, + start_knode); + if (!error) + iter->type = type; + + return error; } EXPORT_SYMBOL_GPL(class_dev_iter_init); @@ -387,14 +392,15 @@ int class_for_each_device(struct class *class, struct device *start, return -EINVAL; } - class_dev_iter_init(&iter, class, start, NULL); - while ((dev = class_dev_iter_next(&iter))) { - error = fn(dev, data); - if (error) - break; + error = class_dev_iter_init(&iter, class, start, NULL); + if (!error) { + while ((dev = class_dev_iter_next(&iter))) { + error = fn(dev, data); + if (error) + break; + } + class_dev_iter_exit(&iter); } - class_dev_iter_exit(&iter); - return error; } EXPORT_SYMBOL_GPL(class_for_each_device); @@ -434,7 +440,9 @@ struct device *class_find_device(struct class *class, struct device *start, return NULL; } - class_dev_iter_init(&iter, class, start, NULL); + if (class_dev_iter_init(&iter, class, start, NULL) < 0) + return NULL; + while ((dev = class_dev_iter_next(&iter))) { if (match(dev, data)) { get_device(dev); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 3ec3896c83a6..16f6dd2c4403 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -49,11 +49,13 @@ int driver_for_each_device(struct device_driver *drv, struct device *start, if (!drv) return -EINVAL; - klist_iter_init_node(&drv->p->klist_devices, &i, - start ? &start->p->knode_driver : NULL); - while ((dev = next_device(&i)) && !error) - error = fn(dev, data); - klist_iter_exit(&i); + error = klist_iter_init_node(&drv->p->klist_devices, &i, + start ? &start->p->knode_driver : NULL); + if (!error) { + while ((dev = next_device(&i)) && !error) + error = fn(dev, data); + klist_iter_exit(&i); + } return error; } EXPORT_SYMBOL_GPL(driver_for_each_device); @@ -83,8 +85,10 @@ struct device *driver_find_device(struct device_driver *drv, if (!drv) return NULL; - klist_iter_init_node(&drv->p->klist_devices, &i, - (start ? &start->p->knode_driver : NULL)); + if (klist_iter_init_node(&drv->p->klist_devices, &i, + (start ? &start->p->knode_driver : NULL)) < 0) + return NULL; + while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; diff --git a/include/linux/device.h b/include/linux/device.h index 5ad17cccdd71..50429b911b21 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -128,7 +128,7 @@ struct subsys_dev_iter { struct klist_iter ki; const struct device_type *type; }; -void subsys_dev_iter_init(struct subsys_dev_iter *iter, +int subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, struct device *start, const struct device_type *type); @@ -380,10 +380,10 @@ int class_compat_create_link(struct class_compat *cls, struct device *dev, void class_compat_remove_link(struct class_compat *cls, struct device *dev, struct device *device_link); -extern void class_dev_iter_init(struct class_dev_iter *iter, - struct class *class, - struct device *start, - const struct device_type *type); +extern int class_dev_iter_init(struct class_dev_iter *iter, + struct class *class, + struct device *start, + const struct device_type *type); extern struct device *class_dev_iter_next(struct class_dev_iter *iter); extern void class_dev_iter_exit(struct class_dev_iter *iter); diff --git a/include/linux/klist.h b/include/linux/klist.h index a370ce57cf1d..9f633230f189 100644 --- a/include/linux/klist.h +++ b/include/linux/klist.h @@ -60,7 +60,7 @@ struct klist_iter { extern void klist_iter_init(struct klist *k, struct klist_iter *i); -extern void klist_iter_init_node(struct klist *k, struct klist_iter *i, +extern int klist_iter_init_node(struct klist *k, struct klist_iter *i, struct klist_node *n); extern void klist_iter_exit(struct klist_iter *i); extern struct klist_node *klist_next(struct klist_iter *i); diff --git a/lib/klist.c b/lib/klist.c index 0874e41609a6..a2741a7d9784 100644 --- a/lib/klist.c +++ b/lib/klist.c @@ -278,13 +278,19 @@ EXPORT_SYMBOL_GPL(klist_node_attached); * Similar to klist_iter_init(), but starts the action off with @n, * instead of with the list head. */ -void klist_iter_init_node(struct klist *k, struct klist_iter *i, - struct klist_node *n) +int klist_iter_init_node(struct klist *k, struct klist_iter *i, + struct klist_node *n) { + if (n) { + kref_get(&n->n_ref); + if (!n->n_klist) { + kref_put(&n->n_ref); + return -ENODEV; + } + } i->i_klist = k; i->i_cur = n; - if (n) - kref_get(&n->n_ref); + return 0; } EXPORT_SYMBOL_GPL(klist_iter_init_node); -- cgit v1.2.3 From c0ecaa06d6d40cb02cd264ddb308e82f0a49ec91 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Apr 2012 11:53:25 +0200 Subject: genirq: Streamline irq_action There is no need to have flags as unsigned long. Make it unsigned int and put it together with irq so it does not waste space on 64 bit. Make irq unsigned int while at it. Signed-off-by: Thomas Gleixner --- include/linux/interrupt.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 2aea5d22db07..c91171599cb6 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -93,27 +93,27 @@ typedef irqreturn_t (*irq_handler_t)(int, void *); /** * struct irqaction - per interrupt action descriptor * @handler: interrupt handler function - * @flags: flags (see IRQF_* above) * @name: name of the device * @dev_id: cookie to identify the device * @percpu_dev_id: cookie to identify the device * @next: pointer to the next irqaction for shared interrupts * @irq: interrupt number - * @dir: pointer to the proc/irq/NN/name entry + * @flags: flags (see IRQF_* above) * @thread_fn: interrupt handler function for threaded interrupts * @thread: thread pointer for threaded interrupts * @thread_flags: flags related to @thread * @thread_mask: bitmask for keeping track of @thread activity + * @dir: pointer to the proc/irq/NN/name entry */ struct irqaction { irq_handler_t handler; - unsigned long flags; void *dev_id; void __percpu *percpu_dev_id; struct irqaction *next; - int irq; irq_handler_t thread_fn; struct task_struct *thread; + unsigned int irq; + unsigned int flags; unsigned long thread_flags; unsigned long thread_mask; const char *name; -- cgit v1.2.3 From e245afe984b120704f15bc8d391fdb6cf96cfe0c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 17 Apr 2012 08:41:58 -0300 Subject: [media] videodev2.h: Fix VIDIOC_QUERYMENU ioctl regression Fixes a regression in VIDIOC_QUERYMENU introduced when the __s64 value field was added to the union. On a 64-bit system this will change the size of this v4l2_querymenu structure from 44 to 48 bytes, thus breaking the ABI. By adding the packed attribute it is working again. Tested on both 64 and 32 bit systems. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- include/linux/videodev2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e69cacc9e9ea..5a09ac3f7683 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1176,7 +1176,7 @@ struct v4l2_querymenu { __s64 value; }; __u32 reserved; -}; +} __attribute__ ((packed)); /* Control flags */ #define V4L2_CTRL_FLAG_DISABLED 0x0001 -- cgit v1.2.3 From 6026aa907b16677d32593c5b7dea134380f51f7f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 3 Apr 2012 11:58:42 +0100 Subject: ARM: 7369/1: amba: add functions to add devices dynamically Add two functions to add APB and AHB devices to the amba (PrimeCell) bus dynamically. This is modeled after the static definition macros recently introduced into and can help us in factoring out a bunch of code across the kernel. Since a lot of call sites seem to be using a returned struct amba device* pointer, let's use that. Signed-off-by: Linus Walleij Signed-off-by: Russell King --- drivers/amba/bus.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/amba/bus.h | 8 ++++++++ 2 files changed, 57 insertions(+) (limited to 'include/linux') diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index cc273226dbd0..9469d8722038 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -543,6 +543,55 @@ int amba_device_add(struct amba_device *dev, struct resource *parent) } EXPORT_SYMBOL_GPL(amba_device_add); +static struct amba_device * +amba_aphb_device_add(struct device *parent, const char *name, + resource_size_t base, size_t size, int irq1, int irq2, + void *pdata, unsigned int periphid, u64 dma_mask) +{ + struct amba_device *dev; + int ret; + + dev = amba_device_alloc(name, base, size); + if (!dev) + return ERR_PTR(-ENOMEM); + + dev->dma_mask = dma_mask; + dev->dev.coherent_dma_mask = dma_mask; + dev->irq[0] = irq1; + dev->irq[1] = irq2; + dev->periphid = periphid; + dev->dev.platform_data = pdata; + dev->dev.parent = parent; + + ret = amba_device_add(dev, &iomem_resource); + if (ret) { + amba_device_put(dev); + return ERR_PTR(ret); + } + + return dev; +} + +struct amba_device * +amba_apb_device_add(struct device *parent, const char *name, + resource_size_t base, size_t size, int irq1, int irq2, + void *pdata, unsigned int periphid) +{ + return amba_aphb_device_add(parent, name, base, size, irq1, irq2, pdata, + periphid, 0); +} +EXPORT_SYMBOL_GPL(amba_apb_device_add); + +struct amba_device * +amba_ahb_device_add(struct device *parent, const char *name, + resource_size_t base, size_t size, int irq1, int irq2, + void *pdata, unsigned int periphid) +{ + return amba_aphb_device_add(parent, name, base, size, irq1, irq2, pdata, + periphid, ~0ULL); +} +EXPORT_SYMBOL_GPL(amba_ahb_device_add); + static void amba_device_initialize(struct amba_device *dev, const char *name) { device_initialize(&dev->dev); diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 8d54f79457ba..d36417158d8f 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -63,6 +63,14 @@ struct amba_device *amba_device_alloc(const char *, resource_size_t, size_t); void amba_device_put(struct amba_device *); int amba_device_add(struct amba_device *, struct resource *); int amba_device_register(struct amba_device *, struct resource *); +struct amba_device *amba_apb_device_add(struct device *parent, const char *name, + resource_size_t base, size_t size, + int irq1, int irq2, void *pdata, + unsigned int periphid); +struct amba_device *amba_ahb_device_add(struct device *parent, const char *name, + resource_size_t base, size_t size, + int irq1, int irq2, void *pdata, + unsigned int periphid); void amba_device_unregister(struct amba_device *); struct amba_device *amba_find_device(const char *, struct device *, unsigned int, unsigned int); int amba_request_regions(struct amba_device *, const char *); -- cgit v1.2.3 From a8a97db984bdc5e89d42e41891543d2daaf314cb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 5 Apr 2012 11:42:09 +0100 Subject: ARM: 7376/1: clkdev: Implement managed clk_get() Allow clk API users to simplify their cleanup paths by providing a managed version of clk_get() and clk_put(). Signed-off-by: Mark Brown Signed-off-by: Russell King --- Documentation/driver-model/devres.txt | 4 ++++ drivers/clk/clkdev.c | 45 +++++++++++++++++++++++++++++++++++ include/linux/clk.h | 32 +++++++++++++++++++++++++ 3 files changed, 81 insertions(+) (limited to 'include/linux') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 2a596a4fc23e..9faac6ae3525 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -276,3 +276,7 @@ REGULATOR devm_regulator_get() devm_regulator_put() devm_regulator_bulk_get() + +CLOCK + devm_clk_get() + devm_clk_put() diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 6db161f64ae0..a9a113782821 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -89,6 +89,51 @@ void clk_put(struct clk *clk) } EXPORT_SYMBOL(clk_put); +static void devm_clk_release(struct device *dev, void *res) +{ + clk_put(*(struct clk **)res); +} + +struct clk *devm_clk_get(struct device *dev, const char *id) +{ + struct clk **ptr, *clk; + + ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + clk = clk_get(dev, id); + if (!IS_ERR(clk)) { + *ptr = clk; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return clk; +} +EXPORT_SYMBOL(devm_clk_get); + +static int devm_clk_match(struct device *dev, void *res, void *data) +{ + struct clk **c = res; + if (!c || !*c) { + WARN_ON(!c || !*c); + return 0; + } + return *c == data; +} + +void devm_clk_put(struct device *dev, struct clk *clk) +{ + int ret; + + ret = devres_destroy(dev, devm_clk_release, devm_clk_match, clk); + + WARN_ON(ret); +} +EXPORT_SYMBOL(devm_clk_put); + void clkdev_add(struct clk_lookup *cl) { mutex_lock(&clocks_mutex); diff --git a/include/linux/clk.h b/include/linux/clk.h index b0252726df61..70cf722ac3af 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -100,6 +100,26 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); */ struct clk *clk_get(struct device *dev, const char *id); +/** + * devm_clk_get - lookup and obtain a managed reference to a clock producer. + * @dev: device for clock "consumer" + * @id: clock comsumer ID + * + * Returns a struct clk corresponding to the clock producer, or + * valid IS_ERR() condition containing errno. The implementation + * uses @dev and @id to determine the clock consumer, and thereby + * the clock producer. (IOW, @id may be identical strings, but + * clk_get may return different clock producers depending on @dev.) + * + * Drivers must assume that the clock source is not enabled. + * + * devm_clk_get should not be called from within interrupt context. + * + * The clock will automatically be freed when the device is unbound + * from the bus. + */ +struct clk *devm_clk_get(struct device *dev, const char *id); + /** * clk_prepare - prepare a clock source * @clk: clock source @@ -206,6 +226,18 @@ unsigned long clk_get_rate(struct clk *clk); */ void clk_put(struct clk *clk); +/** + * devm_clk_put - "free" a managed clock source + * @dev: device used to acuqire the clock + * @clk: clock source acquired with devm_clk_get() + * + * Note: drivers must ensure that all clk_enable calls made on this + * clock source are balanced by clk_disable calls prior to calling + * this function. + * + * clk_put should not be called from within interrupt context. + */ +void devm_clk_put(struct device *dev, struct clk *clk); /* * The remaining APIs are optional for machine class support. -- cgit v1.2.3 From bf1ac5ca6f0dede02635588704d216da474b4baa Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Apr 2012 23:19:25 +0000 Subject: nf_bridge: remove holes in struct nf_bridge_info Put use & mask on same location to avoid two holes on 64bit arches Signed-off-by: Eric Dumazet Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/skbuff.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 775292a66fa4..f25795ca6753 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -117,11 +117,11 @@ struct nf_conntrack { #ifdef CONFIG_BRIDGE_NETFILTER struct nf_bridge_info { - atomic_t use; - struct net_device *physindev; - struct net_device *physoutdev; - unsigned int mask; - unsigned long data[32 / sizeof(unsigned long)]; + atomic_t use; + unsigned int mask; + struct net_device *physindev; + struct net_device *physoutdev; + unsigned long data[32 / sizeof(unsigned long)]; }; #endif -- cgit v1.2.3 From f78146b0f9230765c6315b2e14f56112513389ad Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Wed, 18 Apr 2012 19:22:47 +0300 Subject: KVM: Fix page-crossing MMIO MMIO that are split across a page boundary are currently broken - the code does not expect to be aborted by the exit to userspace for the first MMIO fragment. This patch fixes the problem by generalizing the current code for handling 16-byte MMIOs to handle a number of "fragments", and changes the MMIO code to create those fragments. Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/ia64/include/asm/kvm_host.h | 2 + arch/ia64/kvm/kvm-ia64.c | 10 ++-- arch/x86/kvm/x86.c | 114 +++++++++++++++++++++++++++------------ include/linux/kvm_host.h | 31 +++++++++-- 4 files changed, 115 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/include/asm/kvm_host.h b/arch/ia64/include/asm/kvm_host.h index c4b4bac3d09e..6d6a5ac48d85 100644 --- a/arch/ia64/include/asm/kvm_host.h +++ b/arch/ia64/include/asm/kvm_host.h @@ -449,6 +449,8 @@ struct kvm_vcpu_arch { char log_buf[VMM_LOG_LEN]; union context host; union context guest; + + char mmio_data[8]; }; struct kvm_vm_stat { diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 9d80ff8d9eff..882ab21a8dcd 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -232,12 +232,12 @@ static int handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) if ((p->addr & PAGE_MASK) == IOAPIC_DEFAULT_BASE_ADDRESS) goto mmio; vcpu->mmio_needed = 1; - vcpu->mmio_phys_addr = kvm_run->mmio.phys_addr = p->addr; - vcpu->mmio_size = kvm_run->mmio.len = p->size; + vcpu->mmio_fragments[0].gpa = kvm_run->mmio.phys_addr = p->addr; + vcpu->mmio_fragments[0].len = kvm_run->mmio.len = p->size; vcpu->mmio_is_write = kvm_run->mmio.is_write = !p->dir; if (vcpu->mmio_is_write) - memcpy(vcpu->mmio_data, &p->data, p->size); + memcpy(vcpu->arch.mmio_data, &p->data, p->size); memcpy(kvm_run->mmio.data, &p->data, p->size); kvm_run->exit_reason = KVM_EXIT_MMIO; return 0; @@ -719,7 +719,7 @@ static void kvm_set_mmio_data(struct kvm_vcpu *vcpu) struct kvm_mmio_req *p = kvm_get_vcpu_ioreq(vcpu); if (!vcpu->mmio_is_write) - memcpy(&p->data, vcpu->mmio_data, 8); + memcpy(&p->data, vcpu->arch.mmio_data, 8); p->state = STATE_IORESP_READY; } @@ -739,7 +739,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) } if (vcpu->mmio_needed) { - memcpy(vcpu->mmio_data, kvm_run->mmio.data, 8); + memcpy(vcpu->arch.mmio_data, kvm_run->mmio.data, 8); kvm_set_mmio_data(vcpu); vcpu->mmio_read_completed = 1; vcpu->mmio_needed = 0; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0d9a57875f0b..4de705cdcafd 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3718,9 +3718,8 @@ struct read_write_emulator_ops { static int read_prepare(struct kvm_vcpu *vcpu, void *val, int bytes) { if (vcpu->mmio_read_completed) { - memcpy(val, vcpu->mmio_data, bytes); trace_kvm_mmio(KVM_TRACE_MMIO_READ, bytes, - vcpu->mmio_phys_addr, *(u64 *)val); + vcpu->mmio_fragments[0].gpa, *(u64 *)val); vcpu->mmio_read_completed = 0; return 1; } @@ -3756,8 +3755,9 @@ static int read_exit_mmio(struct kvm_vcpu *vcpu, gpa_t gpa, static int write_exit_mmio(struct kvm_vcpu *vcpu, gpa_t gpa, void *val, int bytes) { - memcpy(vcpu->mmio_data, val, bytes); - memcpy(vcpu->run->mmio.data, vcpu->mmio_data, 8); + struct kvm_mmio_fragment *frag = &vcpu->mmio_fragments[0]; + + memcpy(vcpu->run->mmio.data, frag->data, frag->len); return X86EMUL_CONTINUE; } @@ -3784,10 +3784,7 @@ static int emulator_read_write_onepage(unsigned long addr, void *val, gpa_t gpa; int handled, ret; bool write = ops->write; - - if (ops->read_write_prepare && - ops->read_write_prepare(vcpu, val, bytes)) - return X86EMUL_CONTINUE; + struct kvm_mmio_fragment *frag; ret = vcpu_mmio_gva_to_gpa(vcpu, addr, &gpa, exception, write); @@ -3813,15 +3810,19 @@ mmio: bytes -= handled; val += handled; - vcpu->mmio_needed = 1; - vcpu->run->exit_reason = KVM_EXIT_MMIO; - vcpu->run->mmio.phys_addr = vcpu->mmio_phys_addr = gpa; - vcpu->mmio_size = bytes; - vcpu->run->mmio.len = min(vcpu->mmio_size, 8); - vcpu->run->mmio.is_write = vcpu->mmio_is_write = write; - vcpu->mmio_index = 0; + while (bytes) { + unsigned now = min(bytes, 8U); - return ops->read_write_exit_mmio(vcpu, gpa, val, bytes); + frag = &vcpu->mmio_fragments[vcpu->mmio_nr_fragments++]; + frag->gpa = gpa; + frag->data = val; + frag->len = now; + + gpa += now; + val += now; + bytes -= now; + } + return X86EMUL_CONTINUE; } int emulator_read_write(struct x86_emulate_ctxt *ctxt, unsigned long addr, @@ -3830,10 +3831,18 @@ int emulator_read_write(struct x86_emulate_ctxt *ctxt, unsigned long addr, struct read_write_emulator_ops *ops) { struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); + gpa_t gpa; + int rc; + + if (ops->read_write_prepare && + ops->read_write_prepare(vcpu, val, bytes)) + return X86EMUL_CONTINUE; + + vcpu->mmio_nr_fragments = 0; /* Crossing a page boundary? */ if (((addr + bytes - 1) ^ addr) & PAGE_MASK) { - int rc, now; + int now; now = -addr & ~PAGE_MASK; rc = emulator_read_write_onepage(addr, val, now, exception, @@ -3846,8 +3855,25 @@ int emulator_read_write(struct x86_emulate_ctxt *ctxt, unsigned long addr, bytes -= now; } - return emulator_read_write_onepage(addr, val, bytes, exception, - vcpu, ops); + rc = emulator_read_write_onepage(addr, val, bytes, exception, + vcpu, ops); + if (rc != X86EMUL_CONTINUE) + return rc; + + if (!vcpu->mmio_nr_fragments) + return rc; + + gpa = vcpu->mmio_fragments[0].gpa; + + vcpu->mmio_needed = 1; + vcpu->mmio_cur_fragment = 0; + + vcpu->run->mmio.len = vcpu->mmio_fragments[0].len; + vcpu->run->mmio.is_write = vcpu->mmio_is_write = ops->write; + vcpu->run->exit_reason = KVM_EXIT_MMIO; + vcpu->run->mmio.phys_addr = gpa; + + return ops->read_write_exit_mmio(vcpu, gpa, val, bytes); } static int emulator_read_emulated(struct x86_emulate_ctxt *ctxt, @@ -5446,33 +5472,55 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) return r; } +/* + * Implements the following, as a state machine: + * + * read: + * for each fragment + * write gpa, len + * exit + * copy data + * execute insn + * + * write: + * for each fragment + * write gpa, len + * copy data + * exit + */ static int complete_mmio(struct kvm_vcpu *vcpu) { struct kvm_run *run = vcpu->run; + struct kvm_mmio_fragment *frag; int r; if (!(vcpu->arch.pio.count || vcpu->mmio_needed)) return 1; if (vcpu->mmio_needed) { - vcpu->mmio_needed = 0; + /* Complete previous fragment */ + frag = &vcpu->mmio_fragments[vcpu->mmio_cur_fragment++]; if (!vcpu->mmio_is_write) - memcpy(vcpu->mmio_data + vcpu->mmio_index, - run->mmio.data, 8); - vcpu->mmio_index += 8; - if (vcpu->mmio_index < vcpu->mmio_size) { - run->exit_reason = KVM_EXIT_MMIO; - run->mmio.phys_addr = vcpu->mmio_phys_addr + vcpu->mmio_index; - memcpy(run->mmio.data, vcpu->mmio_data + vcpu->mmio_index, 8); - run->mmio.len = min(vcpu->mmio_size - vcpu->mmio_index, 8); - run->mmio.is_write = vcpu->mmio_is_write; - vcpu->mmio_needed = 1; - return 0; + memcpy(frag->data, run->mmio.data, frag->len); + if (vcpu->mmio_cur_fragment == vcpu->mmio_nr_fragments) { + vcpu->mmio_needed = 0; + if (vcpu->mmio_is_write) + return 1; + vcpu->mmio_read_completed = 1; + goto done; } + /* Initiate next fragment */ + ++frag; + run->exit_reason = KVM_EXIT_MMIO; + run->mmio.phys_addr = frag->gpa; if (vcpu->mmio_is_write) - return 1; - vcpu->mmio_read_completed = 1; + memcpy(run->mmio.data, frag->data, frag->len); + run->mmio.len = frag->len; + run->mmio.is_write = vcpu->mmio_is_write; + return 0; + } +done: vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); r = emulate_instruction(vcpu, EMULTYPE_NO_DECODE); srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index a2d00b1bbf54..186ffab0b9f0 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -34,6 +34,20 @@ #define KVM_MMIO_SIZE 8 #endif +/* + * If we support unaligned MMIO, at most one fragment will be split into two: + */ +#ifdef KVM_UNALIGNED_MMIO +# define KVM_EXTRA_MMIO_FRAGMENTS 1 +#else +# define KVM_EXTRA_MMIO_FRAGMENTS 0 +#endif + +#define KVM_USER_MMIO_SIZE 8 + +#define KVM_MAX_MMIO_FRAGMENTS \ + (KVM_MMIO_SIZE / KVM_USER_MMIO_SIZE + KVM_EXTRA_MMIO_FRAGMENTS) + /* * vcpu->requests bit members */ @@ -117,6 +131,16 @@ enum { EXITING_GUEST_MODE }; +/* + * Sometimes a large or cross-page mmio needs to be broken up into separate + * exits for userspace servicing. + */ +struct kvm_mmio_fragment { + gpa_t gpa; + void *data; + unsigned len; +}; + struct kvm_vcpu { struct kvm *kvm; #ifdef CONFIG_PREEMPT_NOTIFIERS @@ -144,10 +168,9 @@ struct kvm_vcpu { int mmio_needed; int mmio_read_completed; int mmio_is_write; - int mmio_size; - int mmio_index; - unsigned char mmio_data[KVM_MMIO_SIZE]; - gpa_t mmio_phys_addr; + int mmio_cur_fragment; + int mmio_nr_fragments; + struct kvm_mmio_fragment mmio_fragments[KVM_MAX_MMIO_FRAGMENTS]; #endif #ifdef CONFIG_KVM_ASYNC_PF -- cgit v1.2.3 From 3256251f9850d00c8e4a4fd82440092bb0f1fd7d Mon Sep 17 00:00:00 2001 From: Francesco Virlinzi Date: Wed, 18 Apr 2012 19:48:19 +0000 Subject: stmmac: use custom init/exit functions in pm ops Freeze and restore can call the custom init/exit functions. Also the patch adds a custom data field that can be used for storing platform data useful on restore the embedded setup (e.g. GPIO, SYSCFG). Signed-off-by: Francesco Virlinzi Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 17 +++++++++++++---- include/linux/stmmac.h | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 12bd221561e5..ba30d38a6caf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -186,9 +186,6 @@ static int stmmac_pltfr_remove(struct platform_device *pdev) struct resource *res; int ret = stmmac_dvr_remove(ndev); - if (priv->plat->exit) - priv->plat->exit(pdev); - if (priv->plat->exit) priv->plat->exit(pdev); @@ -218,14 +215,26 @@ static int stmmac_pltfr_resume(struct device *dev) int stmmac_pltfr_freeze(struct device *dev) { + int ret; + struct plat_stmmacenet_data *plat_dat = dev_get_platdata(dev); struct net_device *ndev = dev_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); - return stmmac_freeze(ndev); + ret = stmmac_freeze(ndev); + if (plat_dat->exit) + plat_dat->exit(pdev); + + return ret; } int stmmac_pltfr_restore(struct device *dev) { + struct plat_stmmacenet_data *plat_dat = dev_get_platdata(dev); struct net_device *ndev = dev_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + + if (plat_dat->init) + plat_dat->init(pdev); return stmmac_restore(ndev); } diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index cf6403186359..f85c93d6e6da 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -109,6 +109,7 @@ struct plat_stmmacenet_data { int (*init)(struct platform_device *pdev); void (*exit)(struct platform_device *pdev); void *custom_cfg; + void *custom_data; void *bsp_priv; }; #endif -- cgit v1.2.3 From 7cd9c9bb57476167e83b7780dbc06d1dd601789d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 19 Apr 2012 19:17:30 -0700 Subject: Revert "driver core: check start node in klist_iter_init_node" This reverts commit a15d49fd3094cff90e5410ca454a870e0a722fe1 as that patch broke the build. Cc: Hannes Reinecke Reported-by: Stephen Rothwell Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 46 +++++++++++++++++----------------------------- drivers/base/class.c | 32 ++++++++++++-------------------- drivers/base/driver.c | 18 +++++++----------- include/linux/device.h | 10 +++++----- include/linux/klist.h | 2 +- lib/klist.c | 14 ++++---------- 6 files changed, 46 insertions(+), 76 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 76aed01a8b2c..2bcef657a60c 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -296,13 +296,11 @@ int bus_for_each_dev(struct bus_type *bus, struct device *start, if (!bus) return -EINVAL; - error = klist_iter_init_node(&bus->p->klist_devices, &i, - (start ? &start->p->knode_bus : NULL)); - if (!error) { - while ((dev = next_device(&i)) && !error) - error = fn(dev, data); - klist_iter_exit(&i); - } + klist_iter_init_node(&bus->p->klist_devices, &i, + (start ? &start->p->knode_bus : NULL)); + while ((dev = next_device(&i)) && !error) + error = fn(dev, data); + klist_iter_exit(&i); return error; } EXPORT_SYMBOL_GPL(bus_for_each_dev); @@ -332,10 +330,8 @@ struct device *bus_find_device(struct bus_type *bus, if (!bus) return NULL; - if (klist_iter_init_node(&bus->p->klist_devices, &i, - (start ? &start->p->knode_bus : NULL)) < 0) - return NULL; - + klist_iter_init_node(&bus->p->klist_devices, &i, + (start ? &start->p->knode_bus : NULL)); while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; @@ -388,9 +384,7 @@ struct device *subsys_find_device_by_id(struct bus_type *subsys, unsigned int id return NULL; if (hint) { - if (klist_iter_init_node(&subsys->p->klist_devices, &i, - &hint->p->knode_bus) < 0) - return NULL; + klist_iter_init_node(&subsys->p->klist_devices, &i, &hint->p->knode_bus); dev = next_device(&i); if (dev && dev->id == id && get_device(dev)) { klist_iter_exit(&i); @@ -452,13 +446,11 @@ int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, if (!bus) return -EINVAL; - error = klist_iter_init_node(&bus->p->klist_drivers, &i, - start ? &start->p->knode_bus : NULL); - if (!error) { - while ((drv = next_driver(&i)) && !error) - error = fn(drv, data); - klist_iter_exit(&i); - } + klist_iter_init_node(&bus->p->klist_drivers, &i, + start ? &start->p->knode_bus : NULL); + while ((drv = next_driver(&i)) && !error) + error = fn(drv, data); + klist_iter_exit(&i); return error; } EXPORT_SYMBOL_GPL(bus_for_each_drv); @@ -1119,19 +1111,15 @@ EXPORT_SYMBOL_GPL(bus_sort_breadthfirst); * otherwise if it is NULL, the iteration starts at the beginning of * the list. */ -int subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, - struct device *start, const struct device_type *type) +void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, + struct device *start, const struct device_type *type) { struct klist_node *start_knode = NULL; - int error; if (start) start_knode = &start->p->knode_bus; - error = klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, - start_knode); - if (!error) - iter->type = type; - return error; + klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, start_knode); + iter->type = type; } EXPORT_SYMBOL_GPL(subsys_dev_iter_init); diff --git a/drivers/base/class.c b/drivers/base/class.c index 23dbc661d4a0..03243d4002fd 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -301,20 +301,15 @@ void class_destroy(struct class *cls) * otherwise if it is NULL, the iteration starts at the beginning of * the list. */ -int class_dev_iter_init(struct class_dev_iter *iter, struct class *class, - struct device *start, const struct device_type *type) +void class_dev_iter_init(struct class_dev_iter *iter, struct class *class, + struct device *start, const struct device_type *type) { struct klist_node *start_knode = NULL; - int error; if (start) start_knode = &start->knode_class; - error = klist_iter_init_node(&class->p->klist_devices, &iter->ki, - start_knode); - if (!error) - iter->type = type; - - return error; + klist_iter_init_node(&class->p->klist_devices, &iter->ki, start_knode); + iter->type = type; } EXPORT_SYMBOL_GPL(class_dev_iter_init); @@ -392,15 +387,14 @@ int class_for_each_device(struct class *class, struct device *start, return -EINVAL; } - error = class_dev_iter_init(&iter, class, start, NULL); - if (!error) { - while ((dev = class_dev_iter_next(&iter))) { - error = fn(dev, data); - if (error) - break; - } - class_dev_iter_exit(&iter); + class_dev_iter_init(&iter, class, start, NULL); + while ((dev = class_dev_iter_next(&iter))) { + error = fn(dev, data); + if (error) + break; } + class_dev_iter_exit(&iter); + return error; } EXPORT_SYMBOL_GPL(class_for_each_device); @@ -440,9 +434,7 @@ struct device *class_find_device(struct class *class, struct device *start, return NULL; } - if (class_dev_iter_init(&iter, class, start, NULL) < 0) - return NULL; - + class_dev_iter_init(&iter, class, start, NULL); while ((dev = class_dev_iter_next(&iter))) { if (match(dev, data)) { get_device(dev); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 16f6dd2c4403..3ec3896c83a6 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -49,13 +49,11 @@ int driver_for_each_device(struct device_driver *drv, struct device *start, if (!drv) return -EINVAL; - error = klist_iter_init_node(&drv->p->klist_devices, &i, - start ? &start->p->knode_driver : NULL); - if (!error) { - while ((dev = next_device(&i)) && !error) - error = fn(dev, data); - klist_iter_exit(&i); - } + klist_iter_init_node(&drv->p->klist_devices, &i, + start ? &start->p->knode_driver : NULL); + while ((dev = next_device(&i)) && !error) + error = fn(dev, data); + klist_iter_exit(&i); return error; } EXPORT_SYMBOL_GPL(driver_for_each_device); @@ -85,10 +83,8 @@ struct device *driver_find_device(struct device_driver *drv, if (!drv) return NULL; - if (klist_iter_init_node(&drv->p->klist_devices, &i, - (start ? &start->p->knode_driver : NULL)) < 0) - return NULL; - + klist_iter_init_node(&drv->p->klist_devices, &i, + (start ? &start->p->knode_driver : NULL)); while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; diff --git a/include/linux/device.h b/include/linux/device.h index 50429b911b21..5ad17cccdd71 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -128,7 +128,7 @@ struct subsys_dev_iter { struct klist_iter ki; const struct device_type *type; }; -int subsys_dev_iter_init(struct subsys_dev_iter *iter, +void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, struct device *start, const struct device_type *type); @@ -380,10 +380,10 @@ int class_compat_create_link(struct class_compat *cls, struct device *dev, void class_compat_remove_link(struct class_compat *cls, struct device *dev, struct device *device_link); -extern int class_dev_iter_init(struct class_dev_iter *iter, - struct class *class, - struct device *start, - const struct device_type *type); +extern void class_dev_iter_init(struct class_dev_iter *iter, + struct class *class, + struct device *start, + const struct device_type *type); extern struct device *class_dev_iter_next(struct class_dev_iter *iter); extern void class_dev_iter_exit(struct class_dev_iter *iter); diff --git a/include/linux/klist.h b/include/linux/klist.h index 9f633230f189..a370ce57cf1d 100644 --- a/include/linux/klist.h +++ b/include/linux/klist.h @@ -60,7 +60,7 @@ struct klist_iter { extern void klist_iter_init(struct klist *k, struct klist_iter *i); -extern int klist_iter_init_node(struct klist *k, struct klist_iter *i, +extern void klist_iter_init_node(struct klist *k, struct klist_iter *i, struct klist_node *n); extern void klist_iter_exit(struct klist_iter *i); extern struct klist_node *klist_next(struct klist_iter *i); diff --git a/lib/klist.c b/lib/klist.c index a2741a7d9784..0874e41609a6 100644 --- a/lib/klist.c +++ b/lib/klist.c @@ -278,19 +278,13 @@ EXPORT_SYMBOL_GPL(klist_node_attached); * Similar to klist_iter_init(), but starts the action off with @n, * instead of with the list head. */ -int klist_iter_init_node(struct klist *k, struct klist_iter *i, - struct klist_node *n) +void klist_iter_init_node(struct klist *k, struct klist_iter *i, + struct klist_node *n) { - if (n) { - kref_get(&n->n_ref); - if (!n->n_klist) { - kref_put(&n->n_ref); - return -ENODEV; - } - } i->i_klist = k; i->i_cur = n; - return 0; + if (n) + kref_get(&n->n_ref); } EXPORT_SYMBOL_GPL(klist_iter_init_node); -- cgit v1.2.3 From 8bd435b30ecacb69bbb8b2d3e251f770b807c5b2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 13 Apr 2012 13:11:28 -0700 Subject: blkcg: remove static policy ID enums Remove BLKIO_POLICY_* enums and let blkio_policy_register() allocate @pol->plid dynamically on registration. The maximum number of blkcg policies which can be registered at the same time is defined by BLKCG_MAX_POLS constant added to include/linux/blkdev.h. Note that blkio_policy_register() now may fail. Policy init functions updated accordingly and unnecessary ifdefs removed from cfq_init(). Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 59 +++++++++++++++++++++++++++++++++++++------------- block/blk-cgroup.h | 15 ++++--------- block/blk-throttle.c | 4 +--- block/cfq-iosched.c | 25 +++++++++++---------- include/linux/blkdev.h | 7 +++++- 5 files changed, 69 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index b1231524a097..2d4d7d6d9ae9 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -31,7 +31,7 @@ static LIST_HEAD(all_q_list); struct blkio_cgroup blkio_root_cgroup = { .cfq_weight = 2 * CFQ_WEIGHT_DEFAULT }; EXPORT_SYMBOL_GPL(blkio_root_cgroup); -static struct blkio_policy_type *blkio_policy[BLKIO_NR_POLICIES]; +static struct blkio_policy_type *blkio_policy[BLKCG_MAX_POLS]; struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup) { @@ -67,7 +67,7 @@ static void blkg_free(struct blkio_group *blkg) if (!blkg) return; - for (i = 0; i < BLKIO_NR_POLICIES; i++) { + for (i = 0; i < BLKCG_MAX_POLS; i++) { struct blkio_policy_type *pol = blkio_policy[i]; struct blkg_policy_data *pd = blkg->pd[i]; @@ -107,7 +107,7 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, blkg->refcnt = 1; cgroup_path(blkcg->css.cgroup, blkg->path, sizeof(blkg->path)); - for (i = 0; i < BLKIO_NR_POLICIES; i++) { + for (i = 0; i < BLKCG_MAX_POLS; i++) { struct blkio_policy_type *pol = blkio_policy[i]; struct blkg_policy_data *pd; @@ -127,7 +127,7 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, } /* invoke per-policy init */ - for (i = 0; i < BLKIO_NR_POLICIES; i++) { + for (i = 0; i < BLKCG_MAX_POLS; i++) { struct blkio_policy_type *pol = blkio_policy[i]; if (pol) @@ -320,7 +320,7 @@ blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val) * anyway. If you get hit by a race, retry. */ hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) { - for (i = 0; i < BLKIO_NR_POLICIES; i++) { + for (i = 0; i < BLKCG_MAX_POLS; i++) { struct blkio_policy_type *pol = blkio_policy[i]; if (pol && pol->ops.blkio_reset_group_stats_fn) @@ -729,46 +729,75 @@ struct cgroup_subsys blkio_subsys = { }; EXPORT_SYMBOL_GPL(blkio_subsys); -void blkio_policy_register(struct blkio_policy_type *blkiop) +/** + * blkio_policy_register - register a blkcg policy + * @blkiop: blkcg policy to register + * + * Register @blkiop with blkcg core. Might sleep and @blkiop may be + * modified on successful registration. Returns 0 on success and -errno on + * failure. + */ +int blkio_policy_register(struct blkio_policy_type *blkiop) { struct request_queue *q; + int i, ret; mutex_lock(&blkcg_pol_mutex); - blkcg_bypass_start(); + /* find an empty slot */ + ret = -ENOSPC; + for (i = 0; i < BLKCG_MAX_POLS; i++) + if (!blkio_policy[i]) + break; + if (i >= BLKCG_MAX_POLS) + goto out_unlock; - BUG_ON(blkio_policy[blkiop->plid]); - blkio_policy[blkiop->plid] = blkiop; + /* register and update blkgs */ + blkiop->plid = i; + blkio_policy[i] = blkiop; + + blkcg_bypass_start(); list_for_each_entry(q, &all_q_list, all_q_node) update_root_blkg_pd(q, blkiop); - blkcg_bypass_end(); + /* everything is in place, add intf files for the new policy */ if (blkiop->cftypes) WARN_ON(cgroup_add_cftypes(&blkio_subsys, blkiop->cftypes)); - + ret = 0; +out_unlock: mutex_unlock(&blkcg_pol_mutex); + return ret; } EXPORT_SYMBOL_GPL(blkio_policy_register); +/** + * blkiop_policy_unregister - unregister a blkcg policy + * @blkiop: blkcg policy to unregister + * + * Undo blkio_policy_register(@blkiop). Might sleep. + */ void blkio_policy_unregister(struct blkio_policy_type *blkiop) { struct request_queue *q; mutex_lock(&blkcg_pol_mutex); + if (WARN_ON(blkio_policy[blkiop->plid] != blkiop)) + goto out_unlock; + + /* kill the intf files first */ if (blkiop->cftypes) cgroup_rm_cftypes(&blkio_subsys, blkiop->cftypes); - blkcg_bypass_start(); - - BUG_ON(blkio_policy[blkiop->plid] != blkiop); + /* unregister and update blkgs */ blkio_policy[blkiop->plid] = NULL; + blkcg_bypass_start(); list_for_each_entry(q, &all_q_list, all_q_node) update_root_blkg_pd(q, blkiop); blkcg_bypass_end(); - +out_unlock: mutex_unlock(&blkcg_pol_mutex); } EXPORT_SYMBOL_GPL(blkio_policy_unregister); diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 26949731108f..be80d6eb6531 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -17,13 +17,6 @@ #include #include -enum blkio_policy_id { - BLKIO_POLICY_PROP = 0, /* Proportional Bandwidth division */ - BLKIO_POLICY_THROTL, /* Throttling */ - - BLKIO_NR_POLICIES, -}; - /* Max limits for throttle policy */ #define THROTL_IOPS_MAX UINT_MAX @@ -86,7 +79,7 @@ struct blkio_group { /* reference count */ int refcnt; - struct blkg_policy_data *pd[BLKIO_NR_POLICIES]; + struct blkg_policy_data *pd[BLKCG_MAX_POLS]; struct rcu_head rcu_head; }; @@ -103,7 +96,7 @@ struct blkio_policy_ops { struct blkio_policy_type { struct blkio_policy_ops ops; - enum blkio_policy_id plid; + int plid; size_t pdata_size; /* policy specific private data size */ struct cftype *cftypes; /* cgroup files for the policy */ }; @@ -113,7 +106,7 @@ extern void blkcg_drain_queue(struct request_queue *q); extern void blkcg_exit_queue(struct request_queue *q); /* Blkio controller policy registration */ -extern void blkio_policy_register(struct blkio_policy_type *); +extern int blkio_policy_register(struct blkio_policy_type *); extern void blkio_policy_unregister(struct blkio_policy_type *); extern void blkg_destroy_all(struct request_queue *q, bool destroy_root); extern void update_root_blkg_pd(struct request_queue *q, @@ -329,7 +322,7 @@ struct blkio_policy_type { static inline int blkcg_init_queue(struct request_queue *q) { return 0; } static inline void blkcg_drain_queue(struct request_queue *q) { } static inline void blkcg_exit_queue(struct request_queue *q) { } -static inline void blkio_policy_register(struct blkio_policy_type *blkiop) { } +static inline int blkio_policy_register(struct blkio_policy_type *blkiop) { return 0; } static inline void blkio_policy_unregister(struct blkio_policy_type *blkiop) { } static inline void blkg_destroy_all(struct request_queue *q, bool destory_root) { } diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 07c17c27a628..0dc4645aa7fe 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1089,7 +1089,6 @@ static struct blkio_policy_type blkio_policy_throtl = { .blkio_exit_group_fn = throtl_exit_blkio_group, .blkio_reset_group_stats_fn = throtl_reset_group_stats, }, - .plid = BLKIO_POLICY_THROTL, .pdata_size = sizeof(struct throtl_grp), .cftypes = throtl_files, }; @@ -1271,8 +1270,7 @@ static int __init throtl_init(void) if (!kthrotld_workqueue) panic("Failed to create kthrotld\n"); - blkio_policy_register(&blkio_policy_throtl); - return 0; + return blkio_policy_register(&blkio_policy_throtl); } module_init(throtl_init); diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index d02f0ae9637f..08db2fc70c29 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -4157,7 +4157,6 @@ static struct blkio_policy_type blkio_policy_cfq = { .blkio_init_group_fn = cfq_init_blkio_group, .blkio_reset_group_stats_fn = cfqg_stats_reset, }, - .plid = BLKIO_POLICY_PROP, .pdata_size = sizeof(struct cfq_group), .cftypes = cfq_blkcg_files, }; @@ -4181,27 +4180,31 @@ static int __init cfq_init(void) #else cfq_group_idle = 0; #endif + + ret = blkio_policy_register(&blkio_policy_cfq); + if (ret) + return ret; + cfq_pool = KMEM_CACHE(cfq_queue, 0); if (!cfq_pool) - return -ENOMEM; + goto err_pol_unreg; ret = elv_register(&iosched_cfq); - if (ret) { - kmem_cache_destroy(cfq_pool); - return ret; - } + if (ret) + goto err_free_pool; -#ifdef CONFIG_CFQ_GROUP_IOSCHED - blkio_policy_register(&blkio_policy_cfq); -#endif return 0; + +err_free_pool: + kmem_cache_destroy(cfq_pool); +err_pol_unreg: + blkio_policy_unregister(&blkio_policy_cfq); + return ret; } static void __exit cfq_exit(void) { -#ifdef CONFIG_CFQ_GROUP_IOSCHED blkio_policy_unregister(&blkio_policy_cfq); -#endif elv_unregister(&iosched_cfq); kmem_cache_destroy(cfq_pool); } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 33f1b29e53f4..d2c69f8c188a 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -35,6 +35,12 @@ struct bsg_job; #define BLKDEV_MIN_RQ 4 #define BLKDEV_MAX_RQ 128 /* Default maximum */ +/* + * Maximum number of blkcg policies allowed to be registered concurrently. + * Defined here to simplify include dependency. + */ +#define BLKCG_MAX_POLS 2 + struct request; typedef void (rq_end_io_fn)(struct request *, int); @@ -363,7 +369,6 @@ struct request_queue { struct list_head icq_list; #ifdef CONFIG_BLK_CGROUP - /* XXX: array size hardcoded to avoid include dependency (temporary) */ struct list_head blkg_list; #endif -- cgit v1.2.3 From 03d8e11142a893ad322285d3c8a08e88b570cda1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 13 Apr 2012 13:11:32 -0700 Subject: blkcg: add request_queue->root_blkg With per-queue policy activation, root blkg creation will be moved to blkcg core. Add q->root_blkg in preparation. For blk-throtl, this replaces throtl_data->root_tg; however, cfq needs to keep cfqd->root_group for !CONFIG_CFQ_GROUP_IOSCHED. This is to prepare for per-queue policy activation and doesn't cause any functional difference. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-throttle.c | 16 ++++++++++------ block/cfq-iosched.c | 4 +++- include/linux/blkdev.h | 2 ++ 3 files changed, 15 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 6f1bfdf9a1b7..8c520fad6885 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -97,7 +97,6 @@ struct throtl_data /* service tree for active throtl groups */ struct throtl_rb_root tg_service_tree; - struct throtl_grp *root_tg; struct request_queue *queue; /* Total Number of queued bios on READ and WRITE lists */ @@ -131,6 +130,11 @@ static inline struct blkio_group *tg_to_blkg(struct throtl_grp *tg) return pdata_to_blkg(tg); } +static inline struct throtl_grp *td_root_tg(struct throtl_data *td) +{ + return blkg_to_tg(td->queue->root_blkg); +} + enum tg_state_flags { THROTL_TG_FLAG_on_rr = 0, /* on round-robin busy list */ }; @@ -261,7 +265,7 @@ throtl_grp *throtl_lookup_tg(struct throtl_data *td, struct blkio_cgroup *blkcg) * Avoid lookup in this case */ if (blkcg == &blkio_root_cgroup) - return td->root_tg; + return td_root_tg(td); return blkg_to_tg(blkg_lookup(blkcg, td->queue)); } @@ -277,7 +281,7 @@ static struct throtl_grp *throtl_lookup_create_tg(struct throtl_data *td, * Avoid lookup in this case */ if (blkcg == &blkio_root_cgroup) { - tg = td->root_tg; + tg = td_root_tg(td); } else { struct blkio_group *blkg; @@ -287,7 +291,7 @@ static struct throtl_grp *throtl_lookup_create_tg(struct throtl_data *td, if (!IS_ERR(blkg)) tg = blkg_to_tg(blkg); else if (!blk_queue_dead(q)) - tg = td->root_tg; + tg = td_root_tg(td); } return tg; @@ -1245,12 +1249,12 @@ int blk_throtl_init(struct request_queue *q) blkg = blkg_lookup_create(&blkio_root_cgroup, q, true); if (!IS_ERR(blkg)) - td->root_tg = blkg_to_tg(blkg); + q->root_blkg = blkg; spin_unlock_irq(q->queue_lock); rcu_read_unlock(); - if (!td->root_tg) { + if (!q->root_blkg) { kfree(td); return -ENOMEM; } diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index de95f9a2acf8..86440e04f3ee 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3964,8 +3964,10 @@ static int cfq_init_queue(struct request_queue *q) spin_lock_irq(q->queue_lock); blkg = blkg_lookup_create(&blkio_root_cgroup, q, true); - if (!IS_ERR(blkg)) + if (!IS_ERR(blkg)) { + q->root_blkg = blkg; cfqd->root_group = blkg_to_cfqg(blkg); + } spin_unlock_irq(q->queue_lock); rcu_read_unlock(); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d2c69f8c188a..b01c377fd739 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -31,6 +31,7 @@ struct blk_trace; struct request; struct sg_io_hdr; struct bsg_job; +struct blkio_group; #define BLKDEV_MIN_RQ 4 #define BLKDEV_MAX_RQ 128 /* Default maximum */ @@ -369,6 +370,7 @@ struct request_queue { struct list_head icq_list; #ifdef CONFIG_BLK_CGROUP + struct blkio_group *root_blkg; struct list_head blkg_list; #endif -- cgit v1.2.3 From a2b1693bac45ea3fe3ba612fd22c45f17449f610 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 13 Apr 2012 13:11:33 -0700 Subject: blkcg: implement per-queue policy activation All blkcg policies were assumed to be enabled on all request_queues. Due to various implementation obstacles, during the recent blkcg core updates, this was temporarily implemented as shooting down all !root blkgs on elevator switch and policy [de]registration combined with half-broken in-place root blkg updates. In addition to being buggy and racy, this meant losing all blkcg configurations across those events. Now that blkcg is cleaned up enough, this patch replaces the temporary implementation with proper per-queue policy activation. Each blkcg policy should call the new blkcg_[de]activate_policy() to enable and disable the policy on a specific queue. blkcg_activate_policy() allocates and installs policy data for the policy for all existing blkgs. blkcg_deactivate_policy() does the reverse. If a policy is not enabled for a given queue, blkg printing / config functions skip the respective blkg for the queue. blkcg_activate_policy() also takes care of root blkg creation, and cfq_init_queue() and blk_throtl_init() are updated accordingly. This replaces blkcg_bypass_{start|end}() and update_root_blkg_pd() unnecessary. Dropped. v2: cfq_init_queue() was returning uninitialized @ret on root_group alloc failure if !CONFIG_CFQ_GROUP_IOSCHED. Fixed. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 228 +++++++++++++++++++++++++++++++++---------------- block/blk-cgroup.h | 15 +++- block/blk-throttle.c | 52 +++++------ block/cfq-iosched.c | 37 ++++---- block/elevator.c | 2 - include/linux/blkdev.h | 1 + 6 files changed, 201 insertions(+), 134 deletions(-) (limited to 'include/linux') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index d6e4555c982f..d6d59ad105b4 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -54,6 +54,17 @@ struct blkio_cgroup *bio_blkio_cgroup(struct bio *bio) } EXPORT_SYMBOL_GPL(bio_blkio_cgroup); +static bool blkcg_policy_enabled(struct request_queue *q, + const struct blkio_policy_type *pol) +{ + return pol && test_bit(pol->plid, q->blkcg_pols); +} + +static size_t blkg_pd_size(const struct blkio_policy_type *pol) +{ + return sizeof(struct blkg_policy_data) + pol->pdata_size; +} + /** * blkg_free - free a blkg * @blkg: blkg to free @@ -111,12 +122,11 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, struct blkio_policy_type *pol = blkio_policy[i]; struct blkg_policy_data *pd; - if (!pol) + if (!blkcg_policy_enabled(q, pol)) continue; /* alloc per-policy data and attach it to blkg */ - pd = kzalloc_node(sizeof(*pd) + pol->pdata_size, GFP_ATOMIC, - q->node); + pd = kzalloc_node(blkg_pd_size(pol), GFP_ATOMIC, q->node); if (!pd) { blkg_free(blkg); return NULL; @@ -130,7 +140,7 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, for (i = 0; i < BLKCG_MAX_POLS; i++) { struct blkio_policy_type *pol = blkio_policy[i]; - if (pol) + if (blkcg_policy_enabled(blkg->q, pol)) pol->ops.blkio_init_group_fn(blkg); } @@ -236,36 +246,6 @@ static void blkg_destroy(struct blkio_group *blkg) blkg_put(blkg); } -/* - * XXX: This updates blkg policy data in-place for root blkg, which is - * necessary across elevator switch and policy registration as root blkgs - * aren't shot down. This broken and racy implementation is temporary. - * Eventually, blkg shoot down will be replaced by proper in-place update. - */ -void update_root_blkg_pd(struct request_queue *q, - const struct blkio_policy_type *pol) -{ - struct blkio_group *blkg = blkg_lookup(&blkio_root_cgroup, q); - struct blkg_policy_data *pd; - - if (!blkg) - return; - - kfree(blkg->pd[pol->plid]); - blkg->pd[pol->plid] = NULL; - - if (!pol) - return; - - pd = kzalloc(sizeof(*pd) + pol->pdata_size, GFP_KERNEL); - WARN_ON_ONCE(!pd); - - blkg->pd[pol->plid] = pd; - pd->blkg = blkg; - pol->ops.blkio_init_group_fn(blkg); -} -EXPORT_SYMBOL_GPL(update_root_blkg_pd); - /** * blkg_destroy_all - destroy all blkgs associated with a request_queue * @q: request_queue of interest @@ -339,7 +319,8 @@ blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val) for (i = 0; i < BLKCG_MAX_POLS; i++) { struct blkio_policy_type *pol = blkio_policy[i]; - if (pol && pol->ops.blkio_reset_group_stats_fn) + if (blkcg_policy_enabled(blkg->q, pol) && + pol->ops.blkio_reset_group_stats_fn) pol->ops.blkio_reset_group_stats_fn(blkg); } } @@ -385,7 +366,7 @@ void blkcg_print_blkgs(struct seq_file *sf, struct blkio_cgroup *blkcg, spin_lock_irq(&blkcg->lock); hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) - if (blkg->pd[pol->plid]) + if (blkcg_policy_enabled(blkg->q, pol)) total += prfill(sf, blkg->pd[pol->plid]->pdata, data); spin_unlock_irq(&blkcg->lock); @@ -510,7 +491,10 @@ int blkg_conf_prep(struct blkio_cgroup *blkcg, rcu_read_lock(); spin_lock_irq(disk->queue->queue_lock); - blkg = blkg_lookup_create(blkcg, disk->queue, false); + if (blkcg_policy_enabled(disk->queue, pol)) + blkg = blkg_lookup_create(blkcg, disk->queue, false); + else + blkg = ERR_PTR(-EINVAL); if (IS_ERR(blkg)) { ret = PTR_ERR(blkg); @@ -712,30 +696,6 @@ static int blkiocg_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) return ret; } -static void blkcg_bypass_start(void) - __acquires(&all_q_mutex) -{ - struct request_queue *q; - - mutex_lock(&all_q_mutex); - - list_for_each_entry(q, &all_q_list, all_q_node) { - blk_queue_bypass_start(q); - blkg_destroy_all(q, false); - } -} - -static void blkcg_bypass_end(void) - __releases(&all_q_mutex) -{ - struct request_queue *q; - - list_for_each_entry(q, &all_q_list, all_q_node) - blk_queue_bypass_end(q); - - mutex_unlock(&all_q_mutex); -} - struct cgroup_subsys blkio_subsys = { .name = "blkio", .create = blkiocg_create, @@ -748,6 +708,139 @@ struct cgroup_subsys blkio_subsys = { }; EXPORT_SYMBOL_GPL(blkio_subsys); +/** + * blkcg_activate_policy - activate a blkcg policy on a request_queue + * @q: request_queue of interest + * @pol: blkcg policy to activate + * + * Activate @pol on @q. Requires %GFP_KERNEL context. @q goes through + * bypass mode to populate its blkgs with policy_data for @pol. + * + * Activation happens with @q bypassed, so nobody would be accessing blkgs + * from IO path. Update of each blkg is protected by both queue and blkcg + * locks so that holding either lock and testing blkcg_policy_enabled() is + * always enough for dereferencing policy data. + * + * The caller is responsible for synchronizing [de]activations and policy + * [un]registerations. Returns 0 on success, -errno on failure. + */ +int blkcg_activate_policy(struct request_queue *q, + const struct blkio_policy_type *pol) +{ + LIST_HEAD(pds); + struct blkio_group *blkg; + struct blkg_policy_data *pd, *n; + int cnt = 0, ret; + + if (blkcg_policy_enabled(q, pol)) + return 0; + + blk_queue_bypass_start(q); + + /* make sure the root blkg exists and count the existing blkgs */ + spin_lock_irq(q->queue_lock); + + rcu_read_lock(); + blkg = blkg_lookup_create(&blkio_root_cgroup, q, true); + rcu_read_unlock(); + + if (IS_ERR(blkg)) { + ret = PTR_ERR(blkg); + goto out_unlock; + } + q->root_blkg = blkg; + + list_for_each_entry(blkg, &q->blkg_list, q_node) + cnt++; + + spin_unlock_irq(q->queue_lock); + + /* allocate policy_data for all existing blkgs */ + while (cnt--) { + pd = kzalloc_node(blkg_pd_size(pol), GFP_KERNEL, q->node); + if (!pd) { + ret = -ENOMEM; + goto out_free; + } + list_add_tail(&pd->alloc_node, &pds); + } + + /* + * Install the allocated pds. With @q bypassing, no new blkg + * should have been created while the queue lock was dropped. + */ + spin_lock_irq(q->queue_lock); + + list_for_each_entry(blkg, &q->blkg_list, q_node) { + if (WARN_ON(list_empty(&pds))) { + /* umm... this shouldn't happen, just abort */ + ret = -ENOMEM; + goto out_unlock; + } + pd = list_first_entry(&pds, struct blkg_policy_data, alloc_node); + list_del_init(&pd->alloc_node); + + /* grab blkcg lock too while installing @pd on @blkg */ + spin_lock(&blkg->blkcg->lock); + + blkg->pd[pol->plid] = pd; + pd->blkg = blkg; + pol->ops.blkio_init_group_fn(blkg); + + spin_unlock(&blkg->blkcg->lock); + } + + __set_bit(pol->plid, q->blkcg_pols); + ret = 0; +out_unlock: + spin_unlock_irq(q->queue_lock); +out_free: + blk_queue_bypass_end(q); + list_for_each_entry_safe(pd, n, &pds, alloc_node) + kfree(pd); + return ret; +} +EXPORT_SYMBOL_GPL(blkcg_activate_policy); + +/** + * blkcg_deactivate_policy - deactivate a blkcg policy on a request_queue + * @q: request_queue of interest + * @pol: blkcg policy to deactivate + * + * Deactivate @pol on @q. Follows the same synchronization rules as + * blkcg_activate_policy(). + */ +void blkcg_deactivate_policy(struct request_queue *q, + const struct blkio_policy_type *pol) +{ + struct blkio_group *blkg; + + if (!blkcg_policy_enabled(q, pol)) + return; + + blk_queue_bypass_start(q); + spin_lock_irq(q->queue_lock); + + __clear_bit(pol->plid, q->blkcg_pols); + + list_for_each_entry(blkg, &q->blkg_list, q_node) { + /* grab blkcg lock too while removing @pd from @blkg */ + spin_lock(&blkg->blkcg->lock); + + if (pol->ops.blkio_exit_group_fn) + pol->ops.blkio_exit_group_fn(blkg); + + kfree(blkg->pd[pol->plid]); + blkg->pd[pol->plid] = NULL; + + spin_unlock(&blkg->blkcg->lock); + } + + spin_unlock_irq(q->queue_lock); + blk_queue_bypass_end(q); +} +EXPORT_SYMBOL_GPL(blkcg_deactivate_policy); + /** * blkio_policy_register - register a blkcg policy * @blkiop: blkcg policy to register @@ -758,7 +851,6 @@ EXPORT_SYMBOL_GPL(blkio_subsys); */ int blkio_policy_register(struct blkio_policy_type *blkiop) { - struct request_queue *q; int i, ret; mutex_lock(&blkcg_pol_mutex); @@ -775,11 +867,6 @@ int blkio_policy_register(struct blkio_policy_type *blkiop) blkiop->plid = i; blkio_policy[i] = blkiop; - blkcg_bypass_start(); - list_for_each_entry(q, &all_q_list, all_q_node) - update_root_blkg_pd(q, blkiop); - blkcg_bypass_end(); - /* everything is in place, add intf files for the new policy */ if (blkiop->cftypes) WARN_ON(cgroup_add_cftypes(&blkio_subsys, blkiop->cftypes)); @@ -798,8 +885,6 @@ EXPORT_SYMBOL_GPL(blkio_policy_register); */ void blkio_policy_unregister(struct blkio_policy_type *blkiop) { - struct request_queue *q; - mutex_lock(&blkcg_pol_mutex); if (WARN_ON(blkio_policy[blkiop->plid] != blkiop)) @@ -811,11 +896,6 @@ void blkio_policy_unregister(struct blkio_policy_type *blkiop) /* unregister and update blkgs */ blkio_policy[blkiop->plid] = NULL; - - blkcg_bypass_start(); - list_for_each_entry(q, &all_q_list, all_q_node) - update_root_blkg_pd(q, blkiop); - blkcg_bypass_end(); out_unlock: mutex_unlock(&blkcg_pol_mutex); } diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index df1c7b290c22..66253a7c8ff4 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -64,6 +64,9 @@ struct blkg_policy_data { /* the blkg this per-policy data belongs to */ struct blkio_group *blkg; + /* used during policy activation */ + struct list_head alloc_node; + /* pol->pdata_size bytes of private data used by policy impl */ char pdata[] __aligned(__alignof__(unsigned long long)); }; @@ -108,9 +111,11 @@ extern void blkcg_exit_queue(struct request_queue *q); /* Blkio controller policy registration */ extern int blkio_policy_register(struct blkio_policy_type *); extern void blkio_policy_unregister(struct blkio_policy_type *); +extern int blkcg_activate_policy(struct request_queue *q, + const struct blkio_policy_type *pol); +extern void blkcg_deactivate_policy(struct request_queue *q, + const struct blkio_policy_type *pol); extern void blkg_destroy_all(struct request_queue *q, bool destroy_root); -extern void update_root_blkg_pd(struct request_queue *q, - const struct blkio_policy_type *pol); void blkcg_print_blkgs(struct seq_file *sf, struct blkio_cgroup *blkcg, u64 (*prfill)(struct seq_file *, void *, int), @@ -325,10 +330,12 @@ static inline void blkcg_drain_queue(struct request_queue *q) { } static inline void blkcg_exit_queue(struct request_queue *q) { } static inline int blkio_policy_register(struct blkio_policy_type *blkiop) { return 0; } static inline void blkio_policy_unregister(struct blkio_policy_type *blkiop) { } +static inline int blkcg_activate_policy(struct request_queue *q, + const struct blkio_policy_type *pol) { return 0; } +static inline void blkcg_deactivate_policy(struct request_queue *q, + const struct blkio_policy_type *pol) { } static inline void blkg_destroy_all(struct request_queue *q, bool destory_root) { } -static inline void update_root_blkg_pd(struct request_queue *q, - const struct blkio_policy_type *pol) { } static inline void *blkg_to_pdata(struct blkio_group *blkg, struct blkio_policy_type *pol) { return NULL; } diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 8c520fad6885..2fc964e06ea4 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -995,35 +995,31 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgrp); struct blkg_conf_ctx ctx; struct throtl_grp *tg; + struct throtl_data *td; int ret; ret = blkg_conf_prep(blkcg, &blkio_policy_throtl, buf, &ctx); if (ret) return ret; - ret = -EINVAL; tg = blkg_to_tg(ctx.blkg); - if (tg) { - struct throtl_data *td = ctx.blkg->q->td; - - if (!ctx.v) - ctx.v = -1; + td = ctx.blkg->q->td; - if (is_u64) - *(u64 *)((void *)tg + cft->private) = ctx.v; - else - *(unsigned int *)((void *)tg + cft->private) = ctx.v; + if (!ctx.v) + ctx.v = -1; - /* XXX: we don't need the following deferred processing */ - xchg(&tg->limits_changed, true); - xchg(&td->limits_changed, true); - throtl_schedule_delayed_work(td, 0); + if (is_u64) + *(u64 *)((void *)tg + cft->private) = ctx.v; + else + *(unsigned int *)((void *)tg + cft->private) = ctx.v; - ret = 0; - } + /* XXX: we don't need the following deferred processing */ + xchg(&tg->limits_changed, true); + xchg(&td->limits_changed, true); + throtl_schedule_delayed_work(td, 0); blkg_conf_finish(&ctx); - return ret; + return 0; } static int tg_set_conf_u64(struct cgroup *cgrp, struct cftype *cft, @@ -1230,7 +1226,7 @@ void blk_throtl_drain(struct request_queue *q) int blk_throtl_init(struct request_queue *q) { struct throtl_data *td; - struct blkio_group *blkg; + int ret; td = kzalloc_node(sizeof(*td), GFP_KERNEL, q->node); if (!td) @@ -1243,28 +1239,18 @@ int blk_throtl_init(struct request_queue *q) q->td = td; td->queue = q; - /* alloc and init root group. */ - rcu_read_lock(); - spin_lock_irq(q->queue_lock); - - blkg = blkg_lookup_create(&blkio_root_cgroup, q, true); - if (!IS_ERR(blkg)) - q->root_blkg = blkg; - - spin_unlock_irq(q->queue_lock); - rcu_read_unlock(); - - if (!q->root_blkg) { + /* activate policy */ + ret = blkcg_activate_policy(q, &blkio_policy_throtl); + if (ret) kfree(td); - return -ENOMEM; - } - return 0; + return ret; } void blk_throtl_exit(struct request_queue *q) { BUG_ON(!q->td); throtl_shutdown_wq(q); + blkcg_deactivate_policy(q, &blkio_policy_throtl); kfree(q->td); } diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 86440e04f3ee..0203652e1f34 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1406,8 +1406,7 @@ static int cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, ret = -EINVAL; cfqg = blkg_to_cfqg(ctx.blkg); - if (cfqg && (!ctx.v || (ctx.v >= CFQ_WEIGHT_MIN && - ctx.v <= CFQ_WEIGHT_MAX))) { + if (!ctx.v || (ctx.v >= CFQ_WEIGHT_MIN && ctx.v <= CFQ_WEIGHT_MAX)) { cfqg->dev_weight = ctx.v; cfqg->new_weight = cfqg->dev_weight ?: blkcg->cfq_weight; ret = 0; @@ -3938,7 +3937,7 @@ static void cfq_exit_queue(struct elevator_queue *e) #ifndef CONFIG_CFQ_GROUP_IOSCHED kfree(cfqd->root_group); #endif - update_root_blkg_pd(q, &blkio_policy_cfq); + blkcg_deactivate_policy(q, &blkio_policy_cfq); kfree(cfqd); } @@ -3946,7 +3945,7 @@ static int cfq_init_queue(struct request_queue *q) { struct cfq_data *cfqd; struct blkio_group *blkg __maybe_unused; - int i; + int i, ret; cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node); if (!cfqd) @@ -3960,28 +3959,20 @@ static int cfq_init_queue(struct request_queue *q) /* Init root group and prefer root group over other groups by default */ #ifdef CONFIG_CFQ_GROUP_IOSCHED - rcu_read_lock(); - spin_lock_irq(q->queue_lock); - - blkg = blkg_lookup_create(&blkio_root_cgroup, q, true); - if (!IS_ERR(blkg)) { - q->root_blkg = blkg; - cfqd->root_group = blkg_to_cfqg(blkg); - } + ret = blkcg_activate_policy(q, &blkio_policy_cfq); + if (ret) + goto out_free; - spin_unlock_irq(q->queue_lock); - rcu_read_unlock(); + cfqd->root_group = blkg_to_cfqg(q->root_blkg); #else + ret = -ENOMEM; cfqd->root_group = kzalloc_node(sizeof(*cfqd->root_group), GFP_KERNEL, cfqd->queue->node); - if (cfqd->root_group) - cfq_init_cfqg_base(cfqd->root_group); -#endif - if (!cfqd->root_group) { - kfree(cfqd); - return -ENOMEM; - } + if (!cfqd->root_group) + goto out_free; + cfq_init_cfqg_base(cfqd->root_group); +#endif cfqd->root_group->weight = 2 * CFQ_WEIGHT_DEFAULT; /* @@ -4031,6 +4022,10 @@ static int cfq_init_queue(struct request_queue *q) */ cfqd->last_delayed_sync = jiffies - HZ; return 0; + +out_free: + kfree(cfqd); + return ret; } /* diff --git a/block/elevator.c b/block/elevator.c index be3ab6df0fea..6a55d418896f 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -896,8 +896,6 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) ioc_clear_queue(q); spin_unlock_irq(q->queue_lock); - blkg_destroy_all(q, false); - /* allocate, init and register new elevator */ err = -ENOMEM; q->elevator = elevator_alloc(q, new_e); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index b01c377fd739..68720ab275d4 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -370,6 +370,7 @@ struct request_queue { struct list_head icq_list; #ifdef CONFIG_BLK_CGROUP + DECLARE_BITMAP (blkcg_pols, BLKCG_MAX_POLS); struct blkio_group *root_blkg; struct list_head blkg_list; #endif -- cgit v1.2.3 From 3c798398e393e5f9502dbab2b51e6c25e2e8f2ac Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 16 Apr 2012 13:57:25 -0700 Subject: blkcg: mass rename of blkcg API During the recent blkcg cleanup, most of blkcg API has changed to such extent that mass renaming wouldn't cause any noticeable pain. Take the chance and cleanup the naming. * Rename blkio_cgroup to blkcg. * Drop blkio / blkiocg prefixes and consistently use blkcg. * Rename blkio_group to blkcg_gq, which is consistent with io_cq but keep the blkg prefix / variable name. * Rename policy method type and field names to signify they're dealing with policy data. * Rename blkio_policy_type to blkcg_policy. This patch doesn't cause any functional change. Signed-off-by: Tejun Heo Cc: Vivek Goyal Signed-off-by: Jens Axboe --- block/blk-cgroup.c | 202 ++++++++++++++++++++++++------------------------- block/blk-cgroup.h | 109 +++++++++++++------------- block/blk-throttle.c | 72 +++++++++--------- block/cfq-iosched.c | 78 +++++++++---------- include/linux/blkdev.h | 4 +- 5 files changed, 230 insertions(+), 235 deletions(-) (limited to 'include/linux') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 63337024e4d7..997570329517 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -26,39 +26,39 @@ static DEFINE_MUTEX(blkcg_pol_mutex); -struct blkio_cgroup blkio_root_cgroup = { .cfq_weight = 2 * CFQ_WEIGHT_DEFAULT }; -EXPORT_SYMBOL_GPL(blkio_root_cgroup); +struct blkcg blkcg_root = { .cfq_weight = 2 * CFQ_WEIGHT_DEFAULT }; +EXPORT_SYMBOL_GPL(blkcg_root); -static struct blkio_policy_type *blkio_policy[BLKCG_MAX_POLS]; +static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS]; -struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup) +struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return container_of(cgroup_subsys_state(cgroup, blkio_subsys_id), - struct blkio_cgroup, css); + struct blkcg, css); } -EXPORT_SYMBOL_GPL(cgroup_to_blkio_cgroup); +EXPORT_SYMBOL_GPL(cgroup_to_blkcg); -static struct blkio_cgroup *task_blkio_cgroup(struct task_struct *tsk) +static struct blkcg *task_blkcg(struct task_struct *tsk) { return container_of(task_subsys_state(tsk, blkio_subsys_id), - struct blkio_cgroup, css); + struct blkcg, css); } -struct blkio_cgroup *bio_blkio_cgroup(struct bio *bio) +struct blkcg *bio_blkcg(struct bio *bio) { if (bio && bio->bi_css) - return container_of(bio->bi_css, struct blkio_cgroup, css); - return task_blkio_cgroup(current); + return container_of(bio->bi_css, struct blkcg, css); + return task_blkcg(current); } -EXPORT_SYMBOL_GPL(bio_blkio_cgroup); +EXPORT_SYMBOL_GPL(bio_blkcg); static bool blkcg_policy_enabled(struct request_queue *q, - const struct blkio_policy_type *pol) + const struct blkcg_policy *pol) { return pol && test_bit(pol->plid, q->blkcg_pols); } -static size_t blkg_pd_size(const struct blkio_policy_type *pol) +static size_t blkg_pd_size(const struct blkcg_policy *pol) { return sizeof(struct blkg_policy_data) + pol->pdata_size; } @@ -69,7 +69,7 @@ static size_t blkg_pd_size(const struct blkio_policy_type *pol) * * Free @blkg which may be partially allocated. */ -static void blkg_free(struct blkio_group *blkg) +static void blkg_free(struct blkcg_gq *blkg) { int i; @@ -77,14 +77,14 @@ static void blkg_free(struct blkio_group *blkg) return; for (i = 0; i < BLKCG_MAX_POLS; i++) { - struct blkio_policy_type *pol = blkio_policy[i]; + struct blkcg_policy *pol = blkcg_policy[i]; struct blkg_policy_data *pd = blkg->pd[i]; if (!pd) continue; - if (pol && pol->ops.blkio_exit_group_fn) - pol->ops.blkio_exit_group_fn(blkg); + if (pol && pol->ops.pd_exit_fn) + pol->ops.pd_exit_fn(blkg); kfree(pd); } @@ -99,10 +99,9 @@ static void blkg_free(struct blkio_group *blkg) * * Allocate a new blkg assocating @blkcg and @q. */ -static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, - struct request_queue *q) +static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q) { - struct blkio_group *blkg; + struct blkcg_gq *blkg; int i; /* alloc and init base part */ @@ -116,7 +115,7 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, blkg->refcnt = 1; for (i = 0; i < BLKCG_MAX_POLS; i++) { - struct blkio_policy_type *pol = blkio_policy[i]; + struct blkcg_policy *pol = blkcg_policy[i]; struct blkg_policy_data *pd; if (!blkcg_policy_enabled(q, pol)) @@ -135,19 +134,19 @@ static struct blkio_group *blkg_alloc(struct blkio_cgroup *blkcg, /* invoke per-policy init */ for (i = 0; i < BLKCG_MAX_POLS; i++) { - struct blkio_policy_type *pol = blkio_policy[i]; + struct blkcg_policy *pol = blkcg_policy[i]; if (blkcg_policy_enabled(blkg->q, pol)) - pol->ops.blkio_init_group_fn(blkg); + pol->ops.pd_init_fn(blkg); } return blkg; } -static struct blkio_group *__blkg_lookup(struct blkio_cgroup *blkcg, - struct request_queue *q) +static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, + struct request_queue *q) { - struct blkio_group *blkg; + struct blkcg_gq *blkg; struct hlist_node *n; hlist_for_each_entry_rcu(blkg, n, &blkcg->blkg_list, blkcg_node) @@ -165,8 +164,7 @@ static struct blkio_group *__blkg_lookup(struct blkio_cgroup *blkcg, * under RCU read lock and is guaranteed to return %NULL if @q is bypassing * - see blk_queue_bypass_start() for details. */ -struct blkio_group *blkg_lookup(struct blkio_cgroup *blkcg, - struct request_queue *q) +struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q) { WARN_ON_ONCE(!rcu_read_lock_held()); @@ -176,11 +174,11 @@ struct blkio_group *blkg_lookup(struct blkio_cgroup *blkcg, } EXPORT_SYMBOL_GPL(blkg_lookup); -static struct blkio_group *__blkg_lookup_create(struct blkio_cgroup *blkcg, - struct request_queue *q) +static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg, + struct request_queue *q) __releases(q->queue_lock) __acquires(q->queue_lock) { - struct blkio_group *blkg; + struct blkcg_gq *blkg; WARN_ON_ONCE(!rcu_read_lock_held()); lockdep_assert_held(q->queue_lock); @@ -213,8 +211,8 @@ out: return blkg; } -struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, - struct request_queue *q) +struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg, + struct request_queue *q) { /* * This could be the first entry point of blkcg implementation and @@ -226,10 +224,10 @@ struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, } EXPORT_SYMBOL_GPL(blkg_lookup_create); -static void blkg_destroy(struct blkio_group *blkg) +static void blkg_destroy(struct blkcg_gq *blkg) { struct request_queue *q = blkg->q; - struct blkio_cgroup *blkcg = blkg->blkcg; + struct blkcg *blkcg = blkg->blkcg; lockdep_assert_held(q->queue_lock); lockdep_assert_held(&blkcg->lock); @@ -255,12 +253,12 @@ static void blkg_destroy(struct blkio_group *blkg) */ static void blkg_destroy_all(struct request_queue *q) { - struct blkio_group *blkg, *n; + struct blkcg_gq *blkg, *n; lockdep_assert_held(q->queue_lock); list_for_each_entry_safe(blkg, n, &q->blkg_list, q_node) { - struct blkio_cgroup *blkcg = blkg->blkcg; + struct blkcg *blkcg = blkg->blkcg; spin_lock(&blkcg->lock); blkg_destroy(blkg); @@ -270,10 +268,10 @@ static void blkg_destroy_all(struct request_queue *q) static void blkg_rcu_free(struct rcu_head *rcu_head) { - blkg_free(container_of(rcu_head, struct blkio_group, rcu_head)); + blkg_free(container_of(rcu_head, struct blkcg_gq, rcu_head)); } -void __blkg_release(struct blkio_group *blkg) +void __blkg_release(struct blkcg_gq *blkg) { /* release the extra blkcg reference this blkg has been holding */ css_put(&blkg->blkcg->css); @@ -291,11 +289,11 @@ void __blkg_release(struct blkio_group *blkg) } EXPORT_SYMBOL_GPL(__blkg_release); -static int -blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val) +static int blkcg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, + u64 val) { - struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgroup); - struct blkio_group *blkg; + struct blkcg *blkcg = cgroup_to_blkcg(cgroup); + struct blkcg_gq *blkg; struct hlist_node *n; int i; @@ -309,11 +307,11 @@ blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val) */ hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) { for (i = 0; i < BLKCG_MAX_POLS; i++) { - struct blkio_policy_type *pol = blkio_policy[i]; + struct blkcg_policy *pol = blkcg_policy[i]; if (blkcg_policy_enabled(blkg->q, pol) && - pol->ops.blkio_reset_group_stats_fn) - pol->ops.blkio_reset_group_stats_fn(blkg); + pol->ops.pd_reset_stats_fn) + pol->ops.pd_reset_stats_fn(blkg); } } @@ -322,7 +320,7 @@ blkiocg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, u64 val) return 0; } -static const char *blkg_dev_name(struct blkio_group *blkg) +static const char *blkg_dev_name(struct blkcg_gq *blkg) { /* some drivers (floppy) instantiate a queue w/o disk registered */ if (blkg->q->backing_dev_info.dev) @@ -347,12 +345,12 @@ static const char *blkg_dev_name(struct blkio_group *blkg) * This is to be used to construct print functions for * cftype->read_seq_string method. */ -void blkcg_print_blkgs(struct seq_file *sf, struct blkio_cgroup *blkcg, +void blkcg_print_blkgs(struct seq_file *sf, struct blkcg *blkcg, u64 (*prfill)(struct seq_file *, void *, int), - const struct blkio_policy_type *pol, int data, + const struct blkcg_policy *pol, int data, bool show_total) { - struct blkio_group *blkg; + struct blkcg_gq *blkg; struct hlist_node *n; u64 total = 0; @@ -462,13 +460,12 @@ EXPORT_SYMBOL_GPL(blkg_prfill_rwstat); * value. This function returns with RCU read lock and queue lock held and * must be paired with blkg_conf_finish(). */ -int blkg_conf_prep(struct blkio_cgroup *blkcg, - const struct blkio_policy_type *pol, const char *input, - struct blkg_conf_ctx *ctx) +int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, + const char *input, struct blkg_conf_ctx *ctx) __acquires(rcu) __acquires(disk->queue->queue_lock) { struct gendisk *disk; - struct blkio_group *blkg; + struct blkcg_gq *blkg; unsigned int major, minor; unsigned long long v; int part, ret; @@ -529,16 +526,16 @@ void blkg_conf_finish(struct blkg_conf_ctx *ctx) } EXPORT_SYMBOL_GPL(blkg_conf_finish); -struct cftype blkio_files[] = { +struct cftype blkcg_files[] = { { .name = "reset_stats", - .write_u64 = blkiocg_reset_stats, + .write_u64 = blkcg_reset_stats, }, { } /* terminate */ }; /** - * blkiocg_pre_destroy - cgroup pre_destroy callback + * blkcg_pre_destroy - cgroup pre_destroy callback * @cgroup: cgroup of interest * * This function is called when @cgroup is about to go away and responsible @@ -548,15 +545,15 @@ struct cftype blkio_files[] = { * * This is the blkcg counterpart of ioc_release_fn(). */ -static int blkiocg_pre_destroy(struct cgroup *cgroup) +static int blkcg_pre_destroy(struct cgroup *cgroup) { - struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgroup); + struct blkcg *blkcg = cgroup_to_blkcg(cgroup); spin_lock_irq(&blkcg->lock); while (!hlist_empty(&blkcg->blkg_list)) { - struct blkio_group *blkg = hlist_entry(blkcg->blkg_list.first, - struct blkio_group, blkcg_node); + struct blkcg_gq *blkg = hlist_entry(blkcg->blkg_list.first, + struct blkcg_gq, blkcg_node); struct request_queue *q = blkg->q; if (spin_trylock(q->queue_lock)) { @@ -573,22 +570,22 @@ static int blkiocg_pre_destroy(struct cgroup *cgroup) return 0; } -static void blkiocg_destroy(struct cgroup *cgroup) +static void blkcg_destroy(struct cgroup *cgroup) { - struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgroup); + struct blkcg *blkcg = cgroup_to_blkcg(cgroup); - if (blkcg != &blkio_root_cgroup) + if (blkcg != &blkcg_root) kfree(blkcg); } -static struct cgroup_subsys_state *blkiocg_create(struct cgroup *cgroup) +static struct cgroup_subsys_state *blkcg_create(struct cgroup *cgroup) { static atomic64_t id_seq = ATOMIC64_INIT(0); - struct blkio_cgroup *blkcg; + struct blkcg *blkcg; struct cgroup *parent = cgroup->parent; if (!parent) { - blkcg = &blkio_root_cgroup; + blkcg = &blkcg_root; goto done; } @@ -656,7 +653,7 @@ void blkcg_exit_queue(struct request_queue *q) * of the main cic data structures. For now we allow a task to change * its cgroup only if it's the only owner of its ioc. */ -static int blkiocg_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +static int blkcg_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) { struct task_struct *task; struct io_context *ioc; @@ -677,12 +674,12 @@ static int blkiocg_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) struct cgroup_subsys blkio_subsys = { .name = "blkio", - .create = blkiocg_create, - .can_attach = blkiocg_can_attach, - .pre_destroy = blkiocg_pre_destroy, - .destroy = blkiocg_destroy, + .create = blkcg_create, + .can_attach = blkcg_can_attach, + .pre_destroy = blkcg_pre_destroy, + .destroy = blkcg_destroy, .subsys_id = blkio_subsys_id, - .base_cftypes = blkio_files, + .base_cftypes = blkcg_files, .module = THIS_MODULE, }; EXPORT_SYMBOL_GPL(blkio_subsys); @@ -704,10 +701,10 @@ EXPORT_SYMBOL_GPL(blkio_subsys); * [un]registerations. Returns 0 on success, -errno on failure. */ int blkcg_activate_policy(struct request_queue *q, - const struct blkio_policy_type *pol) + const struct blkcg_policy *pol) { LIST_HEAD(pds); - struct blkio_group *blkg; + struct blkcg_gq *blkg; struct blkg_policy_data *pd, *n; int cnt = 0, ret; @@ -720,7 +717,7 @@ int blkcg_activate_policy(struct request_queue *q, spin_lock_irq(q->queue_lock); rcu_read_lock(); - blkg = __blkg_lookup_create(&blkio_root_cgroup, q); + blkg = __blkg_lookup_create(&blkcg_root, q); rcu_read_unlock(); if (IS_ERR(blkg)) { @@ -764,7 +761,7 @@ int blkcg_activate_policy(struct request_queue *q, blkg->pd[pol->plid] = pd; pd->blkg = blkg; - pol->ops.blkio_init_group_fn(blkg); + pol->ops.pd_init_fn(blkg); spin_unlock(&blkg->blkcg->lock); } @@ -790,9 +787,9 @@ EXPORT_SYMBOL_GPL(blkcg_activate_policy); * blkcg_activate_policy(). */ void blkcg_deactivate_policy(struct request_queue *q, - const struct blkio_policy_type *pol) + const struct blkcg_policy *pol) { - struct blkio_group *blkg; + struct blkcg_gq *blkg; if (!blkcg_policy_enabled(q, pol)) return; @@ -810,8 +807,8 @@ void blkcg_deactivate_policy(struct request_queue *q, /* grab blkcg lock too while removing @pd from @blkg */ spin_lock(&blkg->blkcg->lock); - if (pol->ops.blkio_exit_group_fn) - pol->ops.blkio_exit_group_fn(blkg); + if (pol->ops.pd_exit_fn) + pol->ops.pd_exit_fn(blkg); kfree(blkg->pd[pol->plid]); blkg->pd[pol->plid] = NULL; @@ -825,14 +822,13 @@ void blkcg_deactivate_policy(struct request_queue *q, EXPORT_SYMBOL_GPL(blkcg_deactivate_policy); /** - * blkio_policy_register - register a blkcg policy - * @blkiop: blkcg policy to register + * blkcg_policy_register - register a blkcg policy + * @pol: blkcg policy to register * - * Register @blkiop with blkcg core. Might sleep and @blkiop may be - * modified on successful registration. Returns 0 on success and -errno on - * failure. + * Register @pol with blkcg core. Might sleep and @pol may be modified on + * successful registration. Returns 0 on success and -errno on failure. */ -int blkio_policy_register(struct blkio_policy_type *blkiop) +int blkcg_policy_register(struct blkcg_policy *pol) { int i, ret; @@ -841,45 +837,45 @@ int blkio_policy_register(struct blkio_policy_type *blkiop) /* find an empty slot */ ret = -ENOSPC; for (i = 0; i < BLKCG_MAX_POLS; i++) - if (!blkio_policy[i]) + if (!blkcg_policy[i]) break; if (i >= BLKCG_MAX_POLS) goto out_unlock; /* register and update blkgs */ - blkiop->plid = i; - blkio_policy[i] = blkiop; + pol->plid = i; + blkcg_policy[i] = pol; /* everything is in place, add intf files for the new policy */ - if (blkiop->cftypes) - WARN_ON(cgroup_add_cftypes(&blkio_subsys, blkiop->cftypes)); + if (pol->cftypes) + WARN_ON(cgroup_add_cftypes(&blkio_subsys, pol->cftypes)); ret = 0; out_unlock: mutex_unlock(&blkcg_pol_mutex); return ret; } -EXPORT_SYMBOL_GPL(blkio_policy_register); +EXPORT_SYMBOL_GPL(blkcg_policy_register); /** - * blkiop_policy_unregister - unregister a blkcg policy - * @blkiop: blkcg policy to unregister + * blkcg_policy_unregister - unregister a blkcg policy + * @pol: blkcg policy to unregister * - * Undo blkio_policy_register(@blkiop). Might sleep. + * Undo blkcg_policy_register(@pol). Might sleep. */ -void blkio_policy_unregister(struct blkio_policy_type *blkiop) +void blkcg_policy_unregister(struct blkcg_policy *pol) { mutex_lock(&blkcg_pol_mutex); - if (WARN_ON(blkio_policy[blkiop->plid] != blkiop)) + if (WARN_ON(blkcg_policy[pol->plid] != pol)) goto out_unlock; /* kill the intf files first */ - if (blkiop->cftypes) - cgroup_rm_cftypes(&blkio_subsys, blkiop->cftypes); + if (pol->cftypes) + cgroup_rm_cftypes(&blkio_subsys, pol->cftypes); /* unregister and update blkgs */ - blkio_policy[blkiop->plid] = NULL; + blkcg_policy[pol->plid] = NULL; out_unlock: mutex_unlock(&blkcg_pol_mutex); } -EXPORT_SYMBOL_GPL(blkio_policy_unregister); +EXPORT_SYMBOL_GPL(blkcg_policy_unregister); diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index b347aa08d166..a443b84d2c16 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -37,7 +37,7 @@ enum blkg_rwstat_type { BLKG_RWSTAT_TOTAL = BLKG_RWSTAT_NR, }; -struct blkio_cgroup { +struct blkcg { struct cgroup_subsys_state css; spinlock_t lock; struct hlist_head blkg_list; @@ -45,7 +45,7 @@ struct blkio_cgroup { /* for policies to test whether associated blkcg has changed */ uint64_t id; - /* TODO: per-policy storage in blkio_cgroup */ + /* TODO: per-policy storage in blkcg */ unsigned int cfq_weight; /* belongs to cfq */ }; @@ -62,7 +62,7 @@ struct blkg_rwstat { /* per-blkg per-policy data */ struct blkg_policy_data { /* the blkg this per-policy data belongs to */ - struct blkio_group *blkg; + struct blkcg_gq *blkg; /* used during policy activation */ struct list_head alloc_node; @@ -71,12 +71,13 @@ struct blkg_policy_data { char pdata[] __aligned(__alignof__(unsigned long long)); }; -struct blkio_group { +/* association between a blk cgroup and a request queue */ +struct blkcg_gq { /* Pointer to the associated request_queue */ struct request_queue *q; struct list_head q_node; struct hlist_node blkcg_node; - struct blkio_cgroup *blkcg; + struct blkcg *blkcg; /* reference count */ int refcnt; @@ -85,18 +86,18 @@ struct blkio_group { struct rcu_head rcu_head; }; -typedef void (blkio_init_group_fn)(struct blkio_group *blkg); -typedef void (blkio_exit_group_fn)(struct blkio_group *blkg); -typedef void (blkio_reset_group_stats_fn)(struct blkio_group *blkg); +typedef void (blkcg_pol_init_pd_fn)(struct blkcg_gq *blkg); +typedef void (blkcg_pol_exit_pd_fn)(struct blkcg_gq *blkg); +typedef void (blkcg_pol_reset_pd_stats_fn)(struct blkcg_gq *blkg); -struct blkio_policy_ops { - blkio_init_group_fn *blkio_init_group_fn; - blkio_exit_group_fn *blkio_exit_group_fn; - blkio_reset_group_stats_fn *blkio_reset_group_stats_fn; +struct blkcg_policy_ops { + blkcg_pol_init_pd_fn *pd_init_fn; + blkcg_pol_exit_pd_fn *pd_exit_fn; + blkcg_pol_reset_pd_stats_fn *pd_reset_stats_fn; }; -struct blkio_policy_type { - struct blkio_policy_ops ops; +struct blkcg_policy { + struct blkcg_policy_ops ops; int plid; /* policy specific private data size */ size_t pdata_size; @@ -104,29 +105,28 @@ struct blkio_policy_type { struct cftype *cftypes; }; -extern struct blkio_cgroup blkio_root_cgroup; +extern struct blkcg blkcg_root; -struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup); -struct blkio_cgroup *bio_blkio_cgroup(struct bio *bio); -struct blkio_group *blkg_lookup(struct blkio_cgroup *blkcg, - struct request_queue *q); -struct blkio_group *blkg_lookup_create(struct blkio_cgroup *blkcg, - struct request_queue *q); +struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup); +struct blkcg *bio_blkcg(struct bio *bio); +struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q); +struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg, + struct request_queue *q); int blkcg_init_queue(struct request_queue *q); void blkcg_drain_queue(struct request_queue *q); void blkcg_exit_queue(struct request_queue *q); /* Blkio controller policy registration */ -int blkio_policy_register(struct blkio_policy_type *); -void blkio_policy_unregister(struct blkio_policy_type *); +int blkcg_policy_register(struct blkcg_policy *pol); +void blkcg_policy_unregister(struct blkcg_policy *pol); int blkcg_activate_policy(struct request_queue *q, - const struct blkio_policy_type *pol); + const struct blkcg_policy *pol); void blkcg_deactivate_policy(struct request_queue *q, - const struct blkio_policy_type *pol); + const struct blkcg_policy *pol); -void blkcg_print_blkgs(struct seq_file *sf, struct blkio_cgroup *blkcg, +void blkcg_print_blkgs(struct seq_file *sf, struct blkcg *blkcg, u64 (*prfill)(struct seq_file *, void *, int), - const struct blkio_policy_type *pol, int data, + const struct blkcg_policy *pol, int data, bool show_total); u64 __blkg_prfill_u64(struct seq_file *sf, void *pdata, u64 v); u64 __blkg_prfill_rwstat(struct seq_file *sf, void *pdata, @@ -136,13 +136,12 @@ u64 blkg_prfill_rwstat(struct seq_file *sf, void *pdata, int off); struct blkg_conf_ctx { struct gendisk *disk; - struct blkio_group *blkg; + struct blkcg_gq *blkg; u64 v; }; -int blkg_conf_prep(struct blkio_cgroup *blkcg, - const struct blkio_policy_type *pol, const char *input, - struct blkg_conf_ctx *ctx); +int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, + const char *input, struct blkg_conf_ctx *ctx); void blkg_conf_finish(struct blkg_conf_ctx *ctx); @@ -153,8 +152,8 @@ void blkg_conf_finish(struct blkg_conf_ctx *ctx); * * Return pointer to private data associated with the @blkg-@pol pair. */ -static inline void *blkg_to_pdata(struct blkio_group *blkg, - struct blkio_policy_type *pol) +static inline void *blkg_to_pdata(struct blkcg_gq *blkg, + struct blkcg_policy *pol) { return blkg ? blkg->pd[pol->plid]->pdata : NULL; } @@ -165,7 +164,7 @@ static inline void *blkg_to_pdata(struct blkio_group *blkg, * * @pdata is policy private data. Determine the blkg it's associated with. */ -static inline struct blkio_group *pdata_to_blkg(void *pdata) +static inline struct blkcg_gq *pdata_to_blkg(void *pdata) { if (pdata) { struct blkg_policy_data *pd = @@ -183,7 +182,7 @@ static inline struct blkio_group *pdata_to_blkg(void *pdata) * * Format the path of the cgroup of @blkg into @buf. */ -static inline int blkg_path(struct blkio_group *blkg, char *buf, int buflen) +static inline int blkg_path(struct blkcg_gq *blkg, char *buf, int buflen) { int ret; @@ -201,14 +200,14 @@ static inline int blkg_path(struct blkio_group *blkg, char *buf, int buflen) * * The caller should be holding queue_lock and an existing reference. */ -static inline void blkg_get(struct blkio_group *blkg) +static inline void blkg_get(struct blkcg_gq *blkg) { lockdep_assert_held(blkg->q->queue_lock); WARN_ON_ONCE(!blkg->refcnt); blkg->refcnt++; } -void __blkg_release(struct blkio_group *blkg); +void __blkg_release(struct blkcg_gq *blkg); /** * blkg_put - put a blkg reference @@ -216,7 +215,7 @@ void __blkg_release(struct blkio_group *blkg); * * The caller should be holding queue_lock. */ -static inline void blkg_put(struct blkio_group *blkg) +static inline void blkg_put(struct blkcg_gq *blkg) { lockdep_assert_held(blkg->q->queue_lock); WARN_ON_ONCE(blkg->refcnt <= 0); @@ -343,32 +342,32 @@ static inline void blkg_rwstat_reset(struct blkg_rwstat *rwstat) struct cgroup; -struct blkio_group { +struct blkcg_gq { }; -struct blkio_policy_type { +struct blkcg_policy { }; -static inline struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup) { return NULL; } -static inline struct blkio_cgroup *bio_blkio_cgroup(struct bio *bio) { return NULL; } -static inline struct blkio_group *blkg_lookup(struct blkio_cgroup *blkcg, void *key) { return NULL; } +static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return NULL; } +static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; } +static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, void *key) { return NULL; } static inline int blkcg_init_queue(struct request_queue *q) { return 0; } static inline void blkcg_drain_queue(struct request_queue *q) { } static inline void blkcg_exit_queue(struct request_queue *q) { } -static inline int blkio_policy_register(struct blkio_policy_type *blkiop) { return 0; } -static inline void blkio_policy_unregister(struct blkio_policy_type *blkiop) { } +static inline int blkcg_policy_register(struct blkcg_policy *pol) { return 0; } +static inline void blkcg_policy_unregister(struct blkcg_policy *pol) { } static inline int blkcg_activate_policy(struct request_queue *q, - const struct blkio_policy_type *pol) { return 0; } + const struct blkcg_policy *pol) { return 0; } static inline void blkcg_deactivate_policy(struct request_queue *q, - const struct blkio_policy_type *pol) { } - -static inline void *blkg_to_pdata(struct blkio_group *blkg, - struct blkio_policy_type *pol) { return NULL; } -static inline struct blkio_group *pdata_to_blkg(void *pdata, - struct blkio_policy_type *pol) { return NULL; } -static inline char *blkg_path(struct blkio_group *blkg) { return NULL; } -static inline void blkg_get(struct blkio_group *blkg) { } -static inline void blkg_put(struct blkio_group *blkg) { } + const struct blkcg_policy *pol) { } + +static inline void *blkg_to_pdata(struct blkcg_gq *blkg, + struct blkcg_policy *pol) { return NULL; } +static inline struct blkcg_gq *pdata_to_blkg(void *pdata, + struct blkcg_policy *pol) { return NULL; } +static inline char *blkg_path(struct blkcg_gq *blkg) { return NULL; } +static inline void blkg_get(struct blkcg_gq *blkg) { } +static inline void blkg_put(struct blkcg_gq *blkg) { } #endif /* CONFIG_BLK_CGROUP */ #endif /* _BLK_CGROUP_H */ diff --git a/block/blk-throttle.c b/block/blk-throttle.c index e9b7a47f6da0..00c7eff66ecf 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -21,7 +21,7 @@ static int throtl_quantum = 32; /* Throttling is performed over 100ms slice and after that slice is renewed */ static unsigned long throtl_slice = HZ/10; /* 100 ms */ -static struct blkio_policy_type blkio_policy_throtl; +static struct blkcg_policy blkcg_policy_throtl; /* A workqueue to queue throttle related work */ static struct workqueue_struct *kthrotld_workqueue; @@ -120,12 +120,12 @@ static LIST_HEAD(tg_stats_alloc_list); static void tg_stats_alloc_fn(struct work_struct *); static DECLARE_DELAYED_WORK(tg_stats_alloc_work, tg_stats_alloc_fn); -static inline struct throtl_grp *blkg_to_tg(struct blkio_group *blkg) +static inline struct throtl_grp *blkg_to_tg(struct blkcg_gq *blkg) { - return blkg_to_pdata(blkg, &blkio_policy_throtl); + return blkg_to_pdata(blkg, &blkcg_policy_throtl); } -static inline struct blkio_group *tg_to_blkg(struct throtl_grp *tg) +static inline struct blkcg_gq *tg_to_blkg(struct throtl_grp *tg) { return pdata_to_blkg(tg); } @@ -208,7 +208,7 @@ alloc_stats: goto alloc_stats; } -static void throtl_init_blkio_group(struct blkio_group *blkg) +static void throtl_pd_init(struct blkcg_gq *blkg) { struct throtl_grp *tg = blkg_to_tg(blkg); @@ -233,7 +233,7 @@ static void throtl_init_blkio_group(struct blkio_group *blkg) spin_unlock(&tg_stats_alloc_lock); } -static void throtl_exit_blkio_group(struct blkio_group *blkg) +static void throtl_pd_exit(struct blkcg_gq *blkg) { struct throtl_grp *tg = blkg_to_tg(blkg); @@ -244,7 +244,7 @@ static void throtl_exit_blkio_group(struct blkio_group *blkg) free_percpu(tg->stats_cpu); } -static void throtl_reset_group_stats(struct blkio_group *blkg) +static void throtl_pd_reset_stats(struct blkcg_gq *blkg) { struct throtl_grp *tg = blkg_to_tg(blkg); int cpu; @@ -260,33 +260,33 @@ static void throtl_reset_group_stats(struct blkio_group *blkg) } } -static struct -throtl_grp *throtl_lookup_tg(struct throtl_data *td, struct blkio_cgroup *blkcg) +static struct throtl_grp *throtl_lookup_tg(struct throtl_data *td, + struct blkcg *blkcg) { /* - * This is the common case when there are no blkio cgroups. - * Avoid lookup in this case + * This is the common case when there are no blkcgs. Avoid lookup + * in this case */ - if (blkcg == &blkio_root_cgroup) + if (blkcg == &blkcg_root) return td_root_tg(td); return blkg_to_tg(blkg_lookup(blkcg, td->queue)); } static struct throtl_grp *throtl_lookup_create_tg(struct throtl_data *td, - struct blkio_cgroup *blkcg) + struct blkcg *blkcg) { struct request_queue *q = td->queue; struct throtl_grp *tg = NULL; /* - * This is the common case when there are no blkio cgroups. - * Avoid lookup in this case + * This is the common case when there are no blkcgs. Avoid lookup + * in this case */ - if (blkcg == &blkio_root_cgroup) { + if (blkcg == &blkcg_root) { tg = td_root_tg(td); } else { - struct blkio_group *blkg; + struct blkcg_gq *blkg; blkg = blkg_lookup_create(blkcg, q); @@ -665,7 +665,7 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg, return 0; } -static void throtl_update_dispatch_stats(struct blkio_group *blkg, u64 bytes, +static void throtl_update_dispatch_stats(struct blkcg_gq *blkg, u64 bytes, int rw) { struct throtl_grp *tg = blkg_to_tg(blkg); @@ -822,7 +822,7 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl) static void throtl_process_limit_change(struct throtl_data *td) { struct request_queue *q = td->queue; - struct blkio_group *blkg, *n; + struct blkcg_gq *blkg, *n; if (!td->limits_changed) return; @@ -951,9 +951,9 @@ static u64 tg_prfill_cpu_rwstat(struct seq_file *sf, void *pdata, int off) static int tg_print_cpu_rwstat(struct cgroup *cgrp, struct cftype *cft, struct seq_file *sf) { - struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgrp); + struct blkcg *blkcg = cgroup_to_blkcg(cgrp); - blkcg_print_blkgs(sf, blkcg, tg_prfill_cpu_rwstat, &blkio_policy_throtl, + blkcg_print_blkgs(sf, blkcg, tg_prfill_cpu_rwstat, &blkcg_policy_throtl, cft->private, true); return 0; } @@ -979,29 +979,29 @@ static u64 tg_prfill_conf_uint(struct seq_file *sf, void *pdata, int off) static int tg_print_conf_u64(struct cgroup *cgrp, struct cftype *cft, struct seq_file *sf) { - blkcg_print_blkgs(sf, cgroup_to_blkio_cgroup(cgrp), tg_prfill_conf_u64, - &blkio_policy_throtl, cft->private, false); + blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp), tg_prfill_conf_u64, + &blkcg_policy_throtl, cft->private, false); return 0; } static int tg_print_conf_uint(struct cgroup *cgrp, struct cftype *cft, struct seq_file *sf) { - blkcg_print_blkgs(sf, cgroup_to_blkio_cgroup(cgrp), tg_prfill_conf_uint, - &blkio_policy_throtl, cft->private, false); + blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp), tg_prfill_conf_uint, + &blkcg_policy_throtl, cft->private, false); return 0; } static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, bool is_u64) { - struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgrp); + struct blkcg *blkcg = cgroup_to_blkcg(cgrp); struct blkg_conf_ctx ctx; struct throtl_grp *tg; struct throtl_data *td; int ret; - ret = blkg_conf_prep(blkcg, &blkio_policy_throtl, buf, &ctx); + ret = blkg_conf_prep(blkcg, &blkcg_policy_throtl, buf, &ctx); if (ret) return ret; @@ -1086,11 +1086,11 @@ static void throtl_shutdown_wq(struct request_queue *q) cancel_delayed_work_sync(&td->throtl_work); } -static struct blkio_policy_type blkio_policy_throtl = { +static struct blkcg_policy blkcg_policy_throtl = { .ops = { - .blkio_init_group_fn = throtl_init_blkio_group, - .blkio_exit_group_fn = throtl_exit_blkio_group, - .blkio_reset_group_stats_fn = throtl_reset_group_stats, + .pd_init_fn = throtl_pd_init, + .pd_exit_fn = throtl_pd_exit, + .pd_reset_stats_fn = throtl_pd_reset_stats, }, .pdata_size = sizeof(struct throtl_grp), .cftypes = throtl_files, @@ -1101,7 +1101,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) struct throtl_data *td = q->td; struct throtl_grp *tg; bool rw = bio_data_dir(bio), update_disptime = true; - struct blkio_cgroup *blkcg; + struct blkcg *blkcg; bool throttled = false; if (bio->bi_rw & REQ_THROTTLED) { @@ -1118,7 +1118,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) * just update the dispatch stats in lockless manner and return. */ rcu_read_lock(); - blkcg = bio_blkio_cgroup(bio); + blkcg = bio_blkcg(bio); tg = throtl_lookup_tg(td, blkcg); if (tg) { if (tg_no_rule_group(tg, rw)) { @@ -1243,7 +1243,7 @@ int blk_throtl_init(struct request_queue *q) td->queue = q; /* activate policy */ - ret = blkcg_activate_policy(q, &blkio_policy_throtl); + ret = blkcg_activate_policy(q, &blkcg_policy_throtl); if (ret) kfree(td); return ret; @@ -1253,7 +1253,7 @@ void blk_throtl_exit(struct request_queue *q) { BUG_ON(!q->td); throtl_shutdown_wq(q); - blkcg_deactivate_policy(q, &blkio_policy_throtl); + blkcg_deactivate_policy(q, &blkcg_policy_throtl); kfree(q->td); } @@ -1263,7 +1263,7 @@ static int __init throtl_init(void) if (!kthrotld_workqueue) panic("Failed to create kthrotld\n"); - return blkio_policy_register(&blkio_policy_throtl); + return blkcg_policy_register(&blkcg_policy_throtl); } module_init(throtl_init); diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 901286b5f5cb..792218281d91 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -17,7 +17,7 @@ #include "blk.h" #include "blk-cgroup.h" -static struct blkio_policy_type blkio_policy_cfq __maybe_unused; +static struct blkcg_policy blkcg_policy_cfq __maybe_unused; /* * tunables @@ -202,7 +202,7 @@ struct cfqg_stats { struct blkg_stat dequeue; /* total time spent waiting for it to be assigned a timeslice. */ struct blkg_stat group_wait_time; - /* time spent idling for this blkio_group */ + /* time spent idling for this blkcg_gq */ struct blkg_stat idle_time; /* total time with empty current active q with other requests queued */ struct blkg_stat empty_time; @@ -553,12 +553,12 @@ static inline void cfqg_stats_update_avg_queue_size(struct cfq_group *cfqg) { } #ifdef CONFIG_CFQ_GROUP_IOSCHED -static inline struct cfq_group *blkg_to_cfqg(struct blkio_group *blkg) +static inline struct cfq_group *blkg_to_cfqg(struct blkcg_gq *blkg) { - return blkg_to_pdata(blkg, &blkio_policy_cfq); + return blkg_to_pdata(blkg, &blkcg_policy_cfq); } -static inline struct blkio_group *cfqg_to_blkg(struct cfq_group *cfqg) +static inline struct blkcg_gq *cfqg_to_blkg(struct cfq_group *cfqg) { return pdata_to_blkg(cfqg); } @@ -637,7 +637,7 @@ static inline void cfqg_stats_update_completion(struct cfq_group *cfqg, io_start_time - start_time); } -static void cfqg_stats_reset(struct blkio_group *blkg) +static void cfq_pd_reset_stats(struct blkcg_gq *blkg) { struct cfq_group *cfqg = blkg_to_cfqg(blkg); struct cfqg_stats *stats = &cfqg->stats; @@ -662,8 +662,8 @@ static void cfqg_stats_reset(struct blkio_group *blkg) #else /* CONFIG_CFQ_GROUP_IOSCHED */ -static inline struct cfq_group *blkg_to_cfqg(struct blkio_group *blkg) { return NULL; } -static inline struct blkio_group *cfqg_to_blkg(struct cfq_group *cfqg) { return NULL; } +static inline struct cfq_group *blkg_to_cfqg(struct blkcg_gq *blkg) { return NULL; } +static inline struct blkcg_gq *cfqg_to_blkg(struct cfq_group *cfqg) { return NULL; } static inline void cfqg_get(struct cfq_group *cfqg) { } static inline void cfqg_put(struct cfq_group *cfqg) { } @@ -1331,7 +1331,7 @@ static void cfq_init_cfqg_base(struct cfq_group *cfqg) } #ifdef CONFIG_CFQ_GROUP_IOSCHED -static void cfq_init_blkio_group(struct blkio_group *blkg) +static void cfq_pd_init(struct blkcg_gq *blkg) { struct cfq_group *cfqg = blkg_to_cfqg(blkg); @@ -1344,16 +1344,16 @@ static void cfq_init_blkio_group(struct blkio_group *blkg) * be held. */ static struct cfq_group *cfq_lookup_create_cfqg(struct cfq_data *cfqd, - struct blkio_cgroup *blkcg) + struct blkcg *blkcg) { struct request_queue *q = cfqd->queue; struct cfq_group *cfqg = NULL; - /* avoid lookup for the common case where there's no blkio cgroup */ - if (blkcg == &blkio_root_cgroup) { + /* avoid lookup for the common case where there's no blkcg */ + if (blkcg == &blkcg_root) { cfqg = cfqd->root_group; } else { - struct blkio_group *blkg; + struct blkcg_gq *blkg; blkg = blkg_lookup_create(blkcg, q); if (!IS_ERR(blkg)) @@ -1386,8 +1386,8 @@ static u64 cfqg_prfill_weight_device(struct seq_file *sf, void *pdata, int off) static int cfqg_print_weight_device(struct cgroup *cgrp, struct cftype *cft, struct seq_file *sf) { - blkcg_print_blkgs(sf, cgroup_to_blkio_cgroup(cgrp), - cfqg_prfill_weight_device, &blkio_policy_cfq, 0, + blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp), + cfqg_prfill_weight_device, &blkcg_policy_cfq, 0, false); return 0; } @@ -1395,19 +1395,19 @@ static int cfqg_print_weight_device(struct cgroup *cgrp, struct cftype *cft, static int cfq_print_weight(struct cgroup *cgrp, struct cftype *cft, struct seq_file *sf) { - seq_printf(sf, "%u\n", cgroup_to_blkio_cgroup(cgrp)->cfq_weight); + seq_printf(sf, "%u\n", cgroup_to_blkcg(cgrp)->cfq_weight); return 0; } static int cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, const char *buf) { - struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgrp); + struct blkcg *blkcg = cgroup_to_blkcg(cgrp); struct blkg_conf_ctx ctx; struct cfq_group *cfqg; int ret; - ret = blkg_conf_prep(blkcg, &blkio_policy_cfq, buf, &ctx); + ret = blkg_conf_prep(blkcg, &blkcg_policy_cfq, buf, &ctx); if (ret) return ret; @@ -1425,8 +1425,8 @@ static int cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, static int cfq_set_weight(struct cgroup *cgrp, struct cftype *cft, u64 val) { - struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgrp); - struct blkio_group *blkg; + struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + struct blkcg_gq *blkg; struct hlist_node *n; if (val < CFQ_WEIGHT_MIN || val > CFQ_WEIGHT_MAX) @@ -1449,9 +1449,9 @@ static int cfq_set_weight(struct cgroup *cgrp, struct cftype *cft, u64 val) static int cfqg_print_stat(struct cgroup *cgrp, struct cftype *cft, struct seq_file *sf) { - struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgrp); + struct blkcg *blkcg = cgroup_to_blkcg(cgrp); - blkcg_print_blkgs(sf, blkcg, blkg_prfill_stat, &blkio_policy_cfq, + blkcg_print_blkgs(sf, blkcg, blkg_prfill_stat, &blkcg_policy_cfq, cft->private, false); return 0; } @@ -1459,9 +1459,9 @@ static int cfqg_print_stat(struct cgroup *cgrp, struct cftype *cft, static int cfqg_print_rwstat(struct cgroup *cgrp, struct cftype *cft, struct seq_file *sf) { - struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgrp); + struct blkcg *blkcg = cgroup_to_blkcg(cgrp); - blkcg_print_blkgs(sf, blkcg, blkg_prfill_rwstat, &blkio_policy_cfq, + blkcg_print_blkgs(sf, blkcg, blkg_prfill_rwstat, &blkcg_policy_cfq, cft->private, true); return 0; } @@ -1485,10 +1485,10 @@ static u64 cfqg_prfill_avg_queue_size(struct seq_file *sf, void *pdata, int off) static int cfqg_print_avg_queue_size(struct cgroup *cgrp, struct cftype *cft, struct seq_file *sf) { - struct blkio_cgroup *blkcg = cgroup_to_blkio_cgroup(cgrp); + struct blkcg *blkcg = cgroup_to_blkcg(cgrp); blkcg_print_blkgs(sf, blkcg, cfqg_prfill_avg_queue_size, - &blkio_policy_cfq, 0, false); + &blkcg_policy_cfq, 0, false); return 0; } #endif /* CONFIG_DEBUG_BLK_CGROUP */ @@ -1580,7 +1580,7 @@ static struct cftype cfq_blkcg_files[] = { }; #else /* GROUP_IOSCHED */ static struct cfq_group *cfq_lookup_create_cfqg(struct cfq_data *cfqd, - struct blkio_cgroup *blkcg) + struct blkcg *blkcg) { return cfqd->root_group; } @@ -3135,7 +3135,7 @@ static void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio) uint64_t id; rcu_read_lock(); - id = bio_blkio_cgroup(bio)->id; + id = bio_blkcg(bio)->id; rcu_read_unlock(); /* @@ -3166,14 +3166,14 @@ static struct cfq_queue * cfq_find_alloc_queue(struct cfq_data *cfqd, bool is_sync, struct cfq_io_cq *cic, struct bio *bio, gfp_t gfp_mask) { - struct blkio_cgroup *blkcg; + struct blkcg *blkcg; struct cfq_queue *cfqq, *new_cfqq = NULL; struct cfq_group *cfqg; retry: rcu_read_lock(); - blkcg = bio_blkio_cgroup(bio); + blkcg = bio_blkcg(bio); cfqg = cfq_lookup_create_cfqg(cfqd, blkcg); cfqq = cic_to_cfqq(cic, is_sync); @@ -3944,14 +3944,14 @@ static void cfq_exit_queue(struct elevator_queue *e) #ifndef CONFIG_CFQ_GROUP_IOSCHED kfree(cfqd->root_group); #endif - blkcg_deactivate_policy(q, &blkio_policy_cfq); + blkcg_deactivate_policy(q, &blkcg_policy_cfq); kfree(cfqd); } static int cfq_init_queue(struct request_queue *q) { struct cfq_data *cfqd; - struct blkio_group *blkg __maybe_unused; + struct blkcg_gq *blkg __maybe_unused; int i, ret; cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node); @@ -3966,7 +3966,7 @@ static int cfq_init_queue(struct request_queue *q) /* Init root group and prefer root group over other groups by default */ #ifdef CONFIG_CFQ_GROUP_IOSCHED - ret = blkcg_activate_policy(q, &blkio_policy_cfq); + ret = blkcg_activate_policy(q, &blkcg_policy_cfq); if (ret) goto out_free; @@ -4156,10 +4156,10 @@ static struct elevator_type iosched_cfq = { }; #ifdef CONFIG_CFQ_GROUP_IOSCHED -static struct blkio_policy_type blkio_policy_cfq = { +static struct blkcg_policy blkcg_policy_cfq = { .ops = { - .blkio_init_group_fn = cfq_init_blkio_group, - .blkio_reset_group_stats_fn = cfqg_stats_reset, + .pd_init_fn = cfq_pd_init, + .pd_reset_stats_fn = cfq_pd_reset_stats, }, .pdata_size = sizeof(struct cfq_group), .cftypes = cfq_blkcg_files, @@ -4185,7 +4185,7 @@ static int __init cfq_init(void) cfq_group_idle = 0; #endif - ret = blkio_policy_register(&blkio_policy_cfq); + ret = blkcg_policy_register(&blkcg_policy_cfq); if (ret) return ret; @@ -4202,13 +4202,13 @@ static int __init cfq_init(void) err_free_pool: kmem_cache_destroy(cfq_pool); err_pol_unreg: - blkio_policy_unregister(&blkio_policy_cfq); + blkcg_policy_unregister(&blkcg_policy_cfq); return ret; } static void __exit cfq_exit(void) { - blkio_policy_unregister(&blkio_policy_cfq); + blkcg_policy_unregister(&blkcg_policy_cfq); elv_unregister(&iosched_cfq); kmem_cache_destroy(cfq_pool); } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 68720ab275d4..af33fb1adfee 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -31,7 +31,7 @@ struct blk_trace; struct request; struct sg_io_hdr; struct bsg_job; -struct blkio_group; +struct blkcg_gq; #define BLKDEV_MIN_RQ 4 #define BLKDEV_MAX_RQ 128 /* Default maximum */ @@ -371,7 +371,7 @@ struct request_queue { struct list_head icq_list; #ifdef CONFIG_BLK_CGROUP DECLARE_BITMAP (blkcg_pols, BLKCG_MAX_POLS); - struct blkio_group *root_blkg; + struct blkcg_gq *root_blkg; struct list_head blkg_list; #endif -- cgit v1.2.3 From de55d8716ac50a356cea736c29bb7db5ac3d0190 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Fri, 20 Apr 2012 14:16:22 +0900 Subject: Extcon (external connector): import Android's switch class and modify. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit External connector class (extcon) is based on and an extension of Android kernel's switch class located at linux/drivers/switch/. This patch provides the before-extension switch class moved to the location where the extcon will be located (linux/drivers/extcon/) and updates to handle class properly. The before-extension class, switch class of Android kernel, commits imported are: switch: switch class and GPIO drivers. (splitted) Author: Mike Lockwood switch: Use device_create instead of device_create_drvdata. Author: Arve Hjønnevåg In this patch, upon the commits of Android kernel, we have added: - Relocated and renamed for extcon. - Comments, module name, and author information are updated - Code clean for successing patches - Bugfix: enabling write access without write functions - Class/device/sysfs create/remove handling - Added comments about uevents - Format changes for extcon_dev_register() to have a parent dev. Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Reviewed-by: Mark Brown -- Changes from v7 - Compiler error fixed when it is compiled as a module. - Removed out-of-date Kconfig entry Changes from v6 - Updated comment/strings - Revised "Android-compatible" mode. * Automatically activated if CONFIG_ANDROID && !CONFIG_ANDROID_SWITCH * Creates /sys/class/switch/*, which is a copy of /sys/class/extcon/* Changes from v5 - Split the patch - Style fixes - "Android-compatible" mode is enabled by Kconfig option. Changes from v2 - Updated name_show - Sysfs entries are handled by class itself. - Updated the method to add/remove devices for the class - Comments on uevent send - Able to become a module - Compatible with Android platform Changes from RFC - Renamed to extcon (external connector) from multistate switch - Added a seperated directory (drivers/extcon) - Added kerneldoc comments - Removed unused variables from extcon_gpio.c - Added ABI Documentation. Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-extcon | 26 +++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/extcon/Kconfig | 17 ++ drivers/extcon/Makefile | 5 + drivers/extcon/extcon_class.c | 236 +++++++++++++++++++++++++++ include/linux/extcon.h | 82 ++++++++++ 7 files changed, 369 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-extcon create mode 100644 drivers/extcon/Kconfig create mode 100644 drivers/extcon/Makefile create mode 100644 drivers/extcon/extcon_class.c create mode 100644 include/linux/extcon.h (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon new file mode 100644 index 000000000000..59a4b1c708d5 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-extcon @@ -0,0 +1,26 @@ +What: /sys/class/extcon/.../ +Date: December 2011 +Contact: MyungJoo Ham +Description: + Provide a place in sysfs for the extcon objects. + This allows accessing extcon specific variables. + The name of extcon object denoted as ... is the name given + with extcon_dev_register. + +What: /sys/class/extcon/.../name +Date: December 2011 +Contact: MyungJoo Ham +Description: + The /sys/class/extcon/.../name shows the name of the extcon + object. If the extcon object has an optional callback + "show_name" defined, the callback will provide the name with + this sysfs node. + +What: /sys/class/extcon/.../state +Date: December 2011 +Contact: MyungJoo Ham +Description: + The /sys/class/extcon/.../state shows the cable attach/detach + information of the corresponding extcon object. If the extcon + objecct has an optional callback "show_state" defined, the + callback will provide the name with this sysfs node. diff --git a/drivers/Kconfig b/drivers/Kconfig index d236aef7e59f..0233ad979b7d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -140,4 +140,6 @@ source "drivers/virt/Kconfig" source "drivers/devfreq/Kconfig" +source "drivers/extcon/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 95952c82bf16..c41dfa92cd79 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -134,3 +134,4 @@ obj-$(CONFIG_VIRT_DRIVERS) += virt/ obj-$(CONFIG_HYPERV) += hv/ obj-$(CONFIG_PM_DEVFREQ) += devfreq/ +obj-$(CONFIG_EXTCON) += extcon/ diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig new file mode 100644 index 000000000000..7932e1bd9b02 --- /dev/null +++ b/drivers/extcon/Kconfig @@ -0,0 +1,17 @@ +menuconfig EXTCON + tristate "External Connector Class (extcon) support" + help + Say Y here to enable external connector class (extcon) support. + This allows monitoring external connectors by userspace + via sysfs and uevent and supports external connectors with + multiple states; i.e., an extcon that may have multiple + cables attached. For example, an external connector of a device + may be used to connect an HDMI cable and a AC adaptor, and to + host USB ports. Many of 30-pin connectors including PDMI are + also good examples. + +if EXTCON + +comment "Extcon Device Drivers" + +endif # MULTISTATE_SWITCH diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile new file mode 100644 index 000000000000..6bc69214fcd7 --- /dev/null +++ b/drivers/extcon/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for external connector class (extcon) devices +# + +obj-$(CONFIG_EXTCON) += extcon_class.o diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c new file mode 100644 index 000000000000..3c46368c279a --- /dev/null +++ b/drivers/extcon/extcon_class.c @@ -0,0 +1,236 @@ +/* + * drivers/extcon/extcon_class.c + * + * External connector (extcon) class driver + * + * Copyright (C) 2012 Samsung Electronics + * Author: Donggeun Kim + * Author: MyungJoo Ham + * + * based on android/drivers/switch/switch_class.c + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct class *extcon_class; +#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH) +static struct class_compat *switch_class; +#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ + +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); + + if (edev->print_state) { + int ret = edev->print_state(edev, buf); + + if (ret >= 0) + return ret; + /* Use default if failed */ + } + return sprintf(buf, "%u\n", edev->state); +} + +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); + + /* Optional callback given by the user */ + if (edev->print_name) { + int ret = edev->print_name(edev, buf); + if (ret >= 0) + return ret; + } + + return sprintf(buf, "%s\n", dev_name(edev->dev)); +} + +/** + * extcon_set_state() - Set the cable attach states of the extcon device. + * @edev: the extcon device + * @state: new cable attach status for @edev + * + * Changing the state sends uevent with environment variable containing + * the name of extcon device (envp[0]) and the state output (envp[1]). + * Tizen uses this format for extcon device to get events from ports. + * Android uses this format as well. + */ +void extcon_set_state(struct extcon_dev *edev, u32 state) +{ + char name_buf[120]; + char state_buf[120]; + char *prop_buf; + char *envp[3]; + int env_offset = 0; + int length; + + if (edev->state != state) { + edev->state = state; + + prop_buf = (char *)get_zeroed_page(GFP_KERNEL); + if (prop_buf) { + length = name_show(edev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(name_buf, sizeof(name_buf), + "NAME=%s", prop_buf); + envp[env_offset++] = name_buf; + } + length = state_show(edev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(state_buf, sizeof(state_buf), + "STATE=%s", prop_buf); + envp[env_offset++] = state_buf; + } + envp[env_offset] = NULL; + kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp); + free_page((unsigned long)prop_buf); + } else { + dev_err(edev->dev, "out of memory in extcon_set_state\n"); + kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE); + } + } +} +EXPORT_SYMBOL_GPL(extcon_set_state); + +static struct device_attribute extcon_attrs[] = { + __ATTR_RO(state), + __ATTR_RO(name), +}; + +static int create_extcon_class(void) +{ + if (!extcon_class) { + extcon_class = class_create(THIS_MODULE, "extcon"); + if (IS_ERR(extcon_class)) + return PTR_ERR(extcon_class); + extcon_class->dev_attrs = extcon_attrs; + +#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH) + switch_class = class_compat_register("switch"); + if (WARN(!switch_class, "cannot allocate")) + return -ENOMEM; +#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ + } + + return 0; +} + +static void extcon_cleanup(struct extcon_dev *edev, bool skip) +{ + if (!skip && get_device(edev->dev)) { + device_unregister(edev->dev); + put_device(edev->dev); + } + + kfree(edev->dev); +} + +static void extcon_dev_release(struct device *dev) +{ + struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); + + extcon_cleanup(edev, true); +} + +/** + * extcon_dev_register() - Register a new extcon device + * @edev : the new extcon device (should be allocated before calling) + * @dev : the parent device for this extcon device. + * + * Among the members of edev struct, please set the "user initializing data" + * in any case and set the "optional callbacks" if required. However, please + * do not set the values of "internal data", which are initialized by + * this function. + */ +int extcon_dev_register(struct extcon_dev *edev, struct device *dev) +{ + int ret; + + if (!extcon_class) { + ret = create_extcon_class(); + if (ret < 0) + return ret; + } + + edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL); + edev->dev->parent = dev; + edev->dev->class = extcon_class; + edev->dev->release = extcon_dev_release; + + dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev)); + ret = device_register(edev->dev); + if (ret) { + put_device(edev->dev); + goto err_dev; + } +#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH) + if (switch_class) + ret = class_compat_create_link(switch_class, edev->dev, + dev); +#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ + + dev_set_drvdata(edev->dev, edev); + edev->state = 0; + return 0; + +err_dev: + kfree(edev->dev); + return ret; +} +EXPORT_SYMBOL_GPL(extcon_dev_register); + +/** + * extcon_dev_unregister() - Unregister the extcon device. + * @edev: the extcon device instance to be unregitered. + * + * Note that this does not call kfree(edev) because edev was not allocated + * by this class. + */ +void extcon_dev_unregister(struct extcon_dev *edev) +{ + extcon_cleanup(edev, false); +} +EXPORT_SYMBOL_GPL(extcon_dev_unregister); + +static int __init extcon_class_init(void) +{ + return create_extcon_class(); +} +module_init(extcon_class_init); + +static void __exit extcon_class_exit(void) +{ + class_destroy(extcon_class); +} +module_exit(extcon_class_exit); + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_AUTHOR("Donggeun Kim "); +MODULE_AUTHOR("MyungJoo Ham "); +MODULE_DESCRIPTION("External connector (extcon) class driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/extcon.h b/include/linux/extcon.h new file mode 100644 index 000000000000..9cb3aaaf67f5 --- /dev/null +++ b/include/linux/extcon.h @@ -0,0 +1,82 @@ +/* + * External connector (extcon) class driver + * + * Copyright (C) 2012 Samsung Electronics + * Author: Donggeun Kim + * Author: MyungJoo Ham + * + * based on switch class driver + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#ifndef __LINUX_EXTCON_H__ +#define __LINUX_EXTCON_H__ + +/** + * struct extcon_dev - An extcon device represents one external connector. + * @name The name of this extcon device. Parent device name is used + * if NULL. + * @print_name An optional callback to override the method to print the + * name of the extcon device. + * @print_state An optional callback to override the method to print the + * status of the extcon device. + * @dev Device of this extcon. Do not provide at register-time. + * @state Attach/detach state of this extcon. Do not provide at + * register-time + * + * In most cases, users only need to provide "User initializing data" of + * this struct when registering an extcon. In some exceptional cases, + * optional callbacks may be needed. However, the values in "internal data" + * are overwritten by register function. + */ +struct extcon_dev { + /* --- Optional user initializing data --- */ + const char *name; + + /* --- Optional callbacks to override class functions --- */ + ssize_t (*print_name)(struct extcon_dev *edev, char *buf); + ssize_t (*print_state)(struct extcon_dev *edev, char *buf); + + /* --- Internal data. Please do not set. --- */ + struct device *dev; + u32 state; +}; + +#if IS_ENABLED(CONFIG_EXTCON) +extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev); +extern void extcon_dev_unregister(struct extcon_dev *edev); + +static inline u32 extcon_get_state(struct extcon_dev *edev) +{ + return edev->state; +} + +extern void extcon_set_state(struct extcon_dev *edev, u32 state); +#else /* CONFIG_EXTCON */ +static inline int extcon_dev_register(struct extcon_dev *edev, + struct device *dev) +{ + return 0; +} + +static inline void extcon_dev_unregister(struct extcon_dev *edev) { } + +static inline u32 extcon_get_state(struct extcon_dev *edev) +{ + return 0; +} + +static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { } +#endif /* CONFIG_EXTCON */ +#endif /* __LINUX_EXTCON_H__ */ -- cgit v1.2.3 From be48308a24c7651bf968b561dbd590edb8166d62 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Fri, 20 Apr 2012 14:16:23 +0900 Subject: Extcon: support generic GPIO extcon driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic GPIO extcon driver (an external connector device based on GPIO control) and imported from Android kernel. switch: switch class and GPIO drivers. (splitted) Author: Mike Lockwood switch: gpio: Don't call request_irq with interrupts disabled Author: Arve Hjønnevåg switch_gpio: Add missing #include Author: Mike Lockwood Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Reviewed-by: Mark Brown -- Changed from v7: - Style updates mentioned by Stephen Boyd and Mark Brown Changed from v5: - Splitted at v5 from the main extcon patch. - Added debounce time for irq handlers. - Use request_any_context_irq instead of request_irq - User needs to specify irq flags for GPIO interrupts (was fixed to IRQF_TRIGGER_LOW before) - Use module_platform_driver(). Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/Kconfig | 7 ++ drivers/extcon/Makefile | 1 + drivers/extcon/extcon_gpio.c | 169 +++++++++++++++++++++++++++++++++++++ include/linux/extcon/extcon_gpio.h | 52 ++++++++++++ 4 files changed, 229 insertions(+) create mode 100644 drivers/extcon/extcon_gpio.c create mode 100644 include/linux/extcon/extcon_gpio.h (limited to 'include/linux') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 7932e1bd9b02..85cecff36db3 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -14,4 +14,11 @@ if EXTCON comment "Extcon Device Drivers" +config EXTCON_GPIO + tristate "GPIO extcon support" + depends on GENERIC_GPIO + help + Say Y here to enable GPIO based extcon support. Note that GPIO + extcon supports single state per extcon instance. + endif # MULTISTATE_SWITCH diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 6bc69214fcd7..2c46d4176d18 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_EXTCON) += extcon_class.o +obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon_gpio.c new file mode 100644 index 000000000000..5c0f08506f93 --- /dev/null +++ b/drivers/extcon/extcon_gpio.c @@ -0,0 +1,169 @@ +/* + * drivers/extcon/extcon_gpio.c + * + * Single-state GPIO extcon driver based on extcon class + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * Modified by MyungJoo Ham to support extcon + * (originally switch class is supported) + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio_extcon_data { + struct extcon_dev edev; + unsigned gpio; + const char *state_on; + const char *state_off; + int irq; + struct delayed_work work; + unsigned long debounce_jiffies; +}; + +static void gpio_extcon_work(struct work_struct *work) +{ + int state; + struct gpio_extcon_data *data = + container_of(to_delayed_work(work), struct gpio_extcon_data, + work); + + state = gpio_get_value(data->gpio); + extcon_set_state(&data->edev, state); +} + +static irqreturn_t gpio_irq_handler(int irq, void *dev_id) +{ + struct gpio_extcon_data *extcon_data = dev_id; + + schedule_delayed_work(&extcon_data->work, + extcon_data->debounce_jiffies); + return IRQ_HANDLED; +} + +static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf) +{ + struct gpio_extcon_data *extcon_data = + container_of(edev, struct gpio_extcon_data, edev); + const char *state; + if (extcon_get_state(edev)) + state = extcon_data->state_on; + else + state = extcon_data->state_off; + + if (state) + return sprintf(buf, "%s\n", state); + return -EINVAL; +} + +static int __devinit gpio_extcon_probe(struct platform_device *pdev) +{ + struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data; + struct gpio_extcon_data *extcon_data; + int ret = 0; + + if (!pdata) + return -EBUSY; + if (!pdata->irq_flags) { + dev_err(&pdev->dev, "IRQ flag is not specified.\n"); + return -EINVAL; + } + + extcon_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data), + GFP_KERNEL); + if (!extcon_data) + return -ENOMEM; + + extcon_data->edev.name = pdata->name; + extcon_data->gpio = pdata->gpio; + extcon_data->state_on = pdata->state_on; + extcon_data->state_off = pdata->state_off; + if (pdata->state_on && pdata->state_off) + extcon_data->edev.print_state = extcon_gpio_print_state; + extcon_data->debounce_jiffies = msecs_to_jiffies(pdata->debounce); + + ret = extcon_dev_register(&extcon_data->edev, &pdev->dev); + if (ret < 0) + goto err_extcon_dev_register; + + ret = gpio_request_one(extcon_data->gpio, GPIOF_DIR_IN, pdev->name); + if (ret < 0) + goto err_request_gpio; + + INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work); + + extcon_data->irq = gpio_to_irq(extcon_data->gpio); + if (extcon_data->irq < 0) { + ret = extcon_data->irq; + goto err_detect_irq_num_failed; + } + + ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler, + pdata->irq_flags, pdev->name, + extcon_data); + if (ret < 0) + goto err_request_irq; + + /* Perform initial detection */ + gpio_extcon_work(&extcon_data->work.work); + + return 0; + +err_request_irq: +err_detect_irq_num_failed: + gpio_free(extcon_data->gpio); +err_request_gpio: + extcon_dev_unregister(&extcon_data->edev); +err_extcon_dev_register: + devm_kfree(&pdev->dev, extcon_data); + + return ret; +} + +static int __devexit gpio_extcon_remove(struct platform_device *pdev) +{ + struct gpio_extcon_data *extcon_data = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&extcon_data->work); + gpio_free(extcon_data->gpio); + extcon_dev_unregister(&extcon_data->edev); + devm_kfree(&pdev->dev, extcon_data); + + return 0; +} + +static struct platform_driver gpio_extcon = { + .probe = gpio_extcon_probe, + .remove = __devexit_p(gpio_extcon_remove), + .driver = { + .name = "extcon-gpio", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(gpio_extcon); + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("GPIO extcon driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/extcon/extcon_gpio.h b/include/linux/extcon/extcon_gpio.h new file mode 100644 index 000000000000..a2129b73dcb1 --- /dev/null +++ b/include/linux/extcon/extcon_gpio.h @@ -0,0 +1,52 @@ +/* + * External connector (extcon) class generic GPIO driver + * + * Copyright (C) 2012 Samsung Electronics + * Author: MyungJoo Ham + * + * based on switch class driver + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * +*/ +#ifndef __EXTCON_GPIO_H__ +#define __EXTCON_GPIO_H__ __FILE__ + +#include + +/** + * struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device. + * @name The name of this GPIO extcon device. + * @gpio Corresponding GPIO. + * @debounce Debounce time for GPIO IRQ in ms. + * @irq_flags IRQ Flags (e.g., IRQF_TRIGGER_LOW). + * @state_on print_state is overriden with state_on if attached. If Null, + * default method of extcon class is used. + * @state_off print_state is overriden with state_on if dettached. If Null, + * default method of extcon class is used. + * + * Note that in order for state_on or state_off to be valid, both state_on + * and state_off should be not NULL. If at least one of them is NULL, + * the print_state is not overriden. + */ +struct gpio_extcon_platform_data { + const char *name; + unsigned gpio; + unsigned long debounce; + unsigned long irq_flags; + + /* if NULL, "0" or "1" will be printed */ + const char *state_on; + const char *state_off; +}; + +#endif /* __EXTCON_GPIO_H__ */ -- cgit v1.2.3 From 74c5d09bd562edc220d6e076b8f1e118819c178f Mon Sep 17 00:00:00 2001 From: Donggeun Kim Date: Fri, 20 Apr 2012 14:16:24 +0900 Subject: Extcon: support notification based on the state changes. State changes of extcon devices have been notified via kobjet_uevent. This patch adds notifier interfaces in order to allow device drivers to get notified easily. Along with notifier interface, extcon_get_extcon_dev() function is added so that device drivers may discover a extcon_dev easily. Signed-off-by: Donggeun Kim Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Reviewed-by: Mark Brown -- Changes from RFC - Renamed switch to extcon - Bugfix: extcon_dev_unregister() - Bugfix: "edev->dev" is "internal" data. - Added kerneldoc comments. - Reworded comments. Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon_class.c | 66 +++++++++++++++++++++++++++++++++++++++++++ include/linux/extcon.h | 38 +++++++++++++++++++++++++ 2 files changed, 104 insertions(+) (limited to 'include/linux') diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c index 3c46368c279a..83a088f1edc4 100644 --- a/drivers/extcon/extcon_class.c +++ b/drivers/extcon/extcon_class.c @@ -36,6 +36,9 @@ struct class *extcon_class; static struct class_compat *switch_class; #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ +static LIST_HEAD(extcon_dev_list); +static DEFINE_MUTEX(extcon_dev_list_lock); + static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -75,6 +78,9 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, * the name of extcon device (envp[0]) and the state output (envp[1]). * Tizen uses this format for extcon device to get events from ports. * Android uses this format as well. + * + * Note that notifier provides the which bits are changes in the state + * variable with "val" to the callback. */ void extcon_set_state(struct extcon_dev *edev, u32 state) { @@ -84,10 +90,14 @@ void extcon_set_state(struct extcon_dev *edev, u32 state) char *envp[3]; int env_offset = 0; int length; + u32 old_state = edev->state; if (edev->state != state) { edev->state = state; + raw_notifier_call_chain(&edev->nh, old_state ^ edev->state, + edev); + prop_buf = (char *)get_zeroed_page(GFP_KERNEL); if (prop_buf) { length = name_show(edev->dev, NULL, prop_buf); @@ -117,6 +127,51 @@ void extcon_set_state(struct extcon_dev *edev, u32 state) } EXPORT_SYMBOL_GPL(extcon_set_state); +/** + * extcon_get_extcon_dev() - Get the extcon device instance from the name + * @extcon_name: The extcon name provided with extcon_dev_register() + */ +struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) +{ + struct extcon_dev *sd; + + mutex_lock(&extcon_dev_list_lock); + list_for_each_entry(sd, &extcon_dev_list, entry) { + if (!strcmp(sd->name, extcon_name)) + goto out; + } + sd = NULL; +out: + mutex_unlock(&extcon_dev_list_lock); + return sd; +} +EXPORT_SYMBOL_GPL(extcon_get_extcon_dev); + +/** + * extcon_register_notifier() - Register a notifee to get notified by + * any attach status changes from the extcon. + * @edev: the extcon device. + * @nb: a notifier block to be registered. + */ +int extcon_register_notifier(struct extcon_dev *edev, + struct notifier_block *nb) +{ + return raw_notifier_chain_register(&edev->nh, nb); +} +EXPORT_SYMBOL_GPL(extcon_register_notifier); + +/** + * extcon_unregister_notifier() - Unregister a notifee from the extcon device. + * @edev: the extcon device. + * @nb: a registered notifier block to be unregistered. + */ +int extcon_unregister_notifier(struct extcon_dev *edev, + struct notifier_block *nb) +{ + return raw_notifier_chain_unregister(&edev->nh, nb); +} +EXPORT_SYMBOL_GPL(extcon_unregister_notifier); + static struct device_attribute extcon_attrs[] = { __ATTR_RO(state), __ATTR_RO(name), @@ -142,6 +197,10 @@ static int create_extcon_class(void) static void extcon_cleanup(struct extcon_dev *edev, bool skip) { + mutex_lock(&extcon_dev_list_lock); + list_del(&edev->entry); + mutex_unlock(&extcon_dev_list_lock); + if (!skip && get_device(edev->dev)) { device_unregister(edev->dev); put_device(edev->dev); @@ -194,8 +253,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) dev); #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ + RAW_INIT_NOTIFIER_HEAD(&edev->nh); + dev_set_drvdata(edev->dev, edev); edev->state = 0; + + mutex_lock(&extcon_dev_list_lock); + list_add(&edev->entry, &extcon_dev_list); + mutex_unlock(&extcon_dev_list_lock); + return 0; err_dev: diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 9cb3aaaf67f5..c9c9afe12b47 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -23,6 +23,7 @@ #ifndef __LINUX_EXTCON_H__ #define __LINUX_EXTCON_H__ +#include /** * struct extcon_dev - An extcon device represents one external connector. * @name The name of this extcon device. Parent device name is used @@ -34,6 +35,9 @@ * @dev Device of this extcon. Do not provide at register-time. * @state Attach/detach state of this extcon. Do not provide at * register-time + * @nh Notifier for the state change events from this extcon + * @entry To support list of extcon devices so that uses can search + * for extcon devices based on the extcon name. * * In most cases, users only need to provide "User initializing data" of * this struct when registering an extcon. In some exceptional cases, @@ -51,11 +55,19 @@ struct extcon_dev { /* --- Internal data. Please do not set. --- */ struct device *dev; u32 state; + struct raw_notifier_head nh; + struct list_head entry; }; #if IS_ENABLED(CONFIG_EXTCON) + +/* + * Following APIs are for notifiers or configurations. + * Notifiers are the external port and connection devices. + */ extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev); extern void extcon_dev_unregister(struct extcon_dev *edev); +extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); static inline u32 extcon_get_state(struct extcon_dev *edev) { @@ -63,6 +75,15 @@ static inline u32 extcon_get_state(struct extcon_dev *edev) } extern void extcon_set_state(struct extcon_dev *edev, u32 state); + +/* + * Following APIs are to monitor every action of a notifier. + * Registerer gets notified for every external port of a connection device. + */ +extern int extcon_register_notifier(struct extcon_dev *edev, + struct notifier_block *nb); +extern int extcon_unregister_notifier(struct extcon_dev *edev, + struct notifier_block *nb); #else /* CONFIG_EXTCON */ static inline int extcon_dev_register(struct extcon_dev *edev, struct device *dev) @@ -78,5 +99,22 @@ static inline u32 extcon_get_state(struct extcon_dev *edev) } static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { } +static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) +{ + return NULL; +} + +static inline int extcon_register_notifier(struct extcon_dev *edev, + struct notifier_block *nb) +{ + return 0; +} + +static inline int extcon_unregister_notifier(struct extcon_dev *edev, + struct notifier_block *nb) +{ + return 0; +} + #endif /* CONFIG_EXTCON */ #endif /* __LINUX_EXTCON_H__ */ -- cgit v1.2.3 From 806d9dd71ff52ef09764585baaeec23afbb98560 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Fri, 20 Apr 2012 14:16:25 +0900 Subject: Extcon: support multiple states at a device. One switch device (e.g., MUIC(MAX8997, MAX77686, ...), and some 30-pin devices) may have multiple cables attached. For example, one 30-pin port may inhabit a USB cable, an HDMI cable, and a mic. Thus, one switch device requires multiple state bits each representing a type of cable. For such purpose, we use the 32bit state variable; thus, up to 32 different type of cables may be defined for a switch device. The list of possible cables is defined by the array of cable names in the switch_dev struct given to the class. Signed-off-by: Chanwoo Choi Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park -- Changes from V7 - Bugfixed in _call_per_cable() (incorrect nb) (Chanwoo Choi) - Compiler error in header for !CONFIG_EXTCON (Chanwoo Choi) Changes from V5 - Sysfs style reformed: subdirectory per cable. - Updated standard cable names - Removed unnecessary printf - Bugfixes after testing Changes from V4 - Bugfixes after more testing at Exynos4412 boards with userspace processses. Changes from V3 - Bugfixes after more testing at Exynos4412 boards. Changes from V2 - State can be stored by user - Documentation updated Changes from RFC - Switch is renamed to extcon - Added kerneldoc comments - Added APIs to support "standard" cable names - Added helper APIs to support notifier block registration with cable name. - Regrouped function list in the header file. Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-extcon | 63 +++- drivers/extcon/extcon_class.c | 439 ++++++++++++++++++++++++++- include/linux/extcon.h | 185 +++++++++++ 3 files changed, 667 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon index 59a4b1c708d5..19b18e986a57 100644 --- a/Documentation/ABI/testing/sysfs-class-extcon +++ b/Documentation/ABI/testing/sysfs-class-extcon @@ -1,5 +1,5 @@ What: /sys/class/extcon/.../ -Date: December 2011 +Date: February 2012 Contact: MyungJoo Ham Description: Provide a place in sysfs for the extcon objects. @@ -7,8 +7,16 @@ Description: The name of extcon object denoted as ... is the name given with extcon_dev_register. + One extcon device denotes a single external connector + port. An external connector may have multiple cables + attached simultaneously. Many of docks, cradles, and + accessory cables have such capability. For example, + the 30-pin port of Nuri board (/arch/arm/mach-exynos) + may have both HDMI and Charger attached, or analog audio, + video, and USB cables attached simulteneously. + What: /sys/class/extcon/.../name -Date: December 2011 +Date: February 2012 Contact: MyungJoo Ham Description: The /sys/class/extcon/.../name shows the name of the extcon @@ -17,10 +25,51 @@ Description: this sysfs node. What: /sys/class/extcon/.../state -Date: December 2011 +Date: February 2012 +Contact: MyungJoo Ham +Description: + The /sys/class/extcon/.../state shows and stores the cable + attach/detach information of the corresponding extcon object. + If the extcon object has an optional callback "show_state" + defined, the showing function is overriden with the optional + callback. + + If the default callback for showing function is used, the + format is like this: + # cat state + USB_OTG=1 + HDMI=0 + TA=1 + EAR_JACK=0 + # + In this example, the extcon device have USB_OTG and TA + cables attached and HDMI and EAR_JACK cables detached. + + In order to update the state of an extcon device, enter a hex + state number starting with 0x. + echo 0xHEX > state + + This updates the whole state of the extcon dev. + Inputs of all the methods are required to meet the + mutually_exclusive contidions if they exist. + + It is recommended to use this "global" state interface if + you need to enter the value atomically. The later state + interface associated with each cable cannot update + multiple cable states of an extcon device simultaneously. + +What: /sys/class/extcon/.../cable.x/name +Date: February 2012 +Contact: MyungJoo Ham +Description: + The /sys/class/extcon/.../cable.x/name shows the name of cable + "x" (integer between 0 and 31) of an extcon device. + +What: /sys/class/extcon/.../cable.x/state +Date: February 2012 Contact: MyungJoo Ham Description: - The /sys/class/extcon/.../state shows the cable attach/detach - information of the corresponding extcon object. If the extcon - objecct has an optional callback "show_state" defined, the - callback will provide the name with this sysfs node. + The /sys/class/extcon/.../cable.x/name shows and stores the + state of cable "x" (integer between 0 and 31) of an extcon + device. The state value is either 0 (detached) or 1 + (attached). diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c index 83a088f1edc4..403933bc3e9c 100644 --- a/drivers/extcon/extcon_class.c +++ b/drivers/extcon/extcon_class.c @@ -31,6 +31,39 @@ #include #include +/* + * extcon_cable_name suggests the standard cable names for commonly used + * cable types. + * + * However, please do not use extcon_cable_name directly for extcon_dev + * struct's supported_cable pointer unless your device really supports + * every single port-type of the following cable names. Please choose cable + * names that are actually used in your extcon device. + */ +const char *extcon_cable_name[] = { + [EXTCON_USB] = "USB", + [EXTCON_USB_HOST] = "USB-Host", + [EXTCON_TA] = "TA", + [EXTCON_FAST_CHARGER] = "Fast-charger", + [EXTCON_SLOW_CHARGER] = "Slow-charger", + [EXTCON_CHARGE_DOWNSTREAM] = "Charge-downstream", + [EXTCON_HDMI] = "HDMI", + [EXTCON_MHL] = "MHL", + [EXTCON_DVI] = "DVI", + [EXTCON_VGA] = "VGA", + [EXTCON_DOCK] = "Dock", + [EXTCON_LINE_IN] = "Line-in", + [EXTCON_LINE_OUT] = "Line-out", + [EXTCON_MIC_IN] = "Microphone", + [EXTCON_HEADPHONE_OUT] = "Headphone", + [EXTCON_SPDIF_IN] = "SPDIF-in", + [EXTCON_SPDIF_OUT] = "SPDIF-out", + [EXTCON_VIDEO_IN] = "Video-in", + [EXTCON_VIDEO_OUT] = "Video-out", + + NULL, +}; + struct class *extcon_class; #if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH) static struct class_compat *switch_class; @@ -42,6 +75,7 @@ static DEFINE_MUTEX(extcon_dev_list_lock); static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { + int i, count = 0; struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); if (edev->print_state) { @@ -51,7 +85,39 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, return ret; /* Use default if failed */ } - return sprintf(buf, "%u\n", edev->state); + + if (edev->max_supported == 0) + return sprintf(buf, "%u\n", edev->state); + + for (i = 0; i < SUPPORTED_CABLE_MAX; i++) { + if (!edev->supported_cable[i]) + break; + count += sprintf(buf + count, "%s=%d\n", + edev->supported_cable[i], + !!(edev->state & (1 << i))); + } + + return count; +} + +void extcon_set_state(struct extcon_dev *edev, u32 state); +static ssize_t state_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 state; + ssize_t ret = 0; + struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); + + ret = sscanf(buf, "0x%x", &state); + if (ret == 0) + ret = -EINVAL; + else + extcon_set_state(edev, state); + + if (ret < 0) + return ret; + + return count; } static ssize_t name_show(struct device *dev, struct device_attribute *attr, @@ -69,9 +135,52 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%s\n", dev_name(edev->dev)); } +static ssize_t cable_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct extcon_cable *cable = container_of(attr, struct extcon_cable, + attr_name); + + return sprintf(buf, "%s\n", + cable->edev->supported_cable[cable->cable_index]); +} + +static ssize_t cable_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct extcon_cable *cable = container_of(attr, struct extcon_cable, + attr_state); + + return sprintf(buf, "%d\n", + extcon_get_cable_state_(cable->edev, + cable->cable_index)); +} + +static ssize_t cable_state_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct extcon_cable *cable = container_of(attr, struct extcon_cable, + attr_state); + int ret, state; + + ret = sscanf(buf, "%d", &state); + if (ret == 0) + ret = -EINVAL; + else + ret = extcon_set_cable_state_(cable->edev, cable->cable_index, + state); + + if (ret < 0) + return ret; + return count; +} + /** - * extcon_set_state() - Set the cable attach states of the extcon device. + * extcon_update_state() - Update the cable attach states of the extcon device + * only for the masked bits. * @edev: the extcon device + * @mask: the bit mask to designate updated bits. * @state: new cable attach status for @edev * * Changing the state sends uevent with environment variable containing @@ -79,10 +188,10 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, * Tizen uses this format for extcon device to get events from ports. * Android uses this format as well. * - * Note that notifier provides the which bits are changes in the state - * variable with "val" to the callback. + * Note that the notifier provides which bits are changed in the state + * variable with the val parameter (second) to the callback. */ -void extcon_set_state(struct extcon_dev *edev, u32 state) +void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) { char name_buf[120]; char state_buf[120]; @@ -90,15 +199,20 @@ void extcon_set_state(struct extcon_dev *edev, u32 state) char *envp[3]; int env_offset = 0; int length; - u32 old_state = edev->state; + unsigned long flags; - if (edev->state != state) { - edev->state = state; + spin_lock_irqsave(&edev->lock, flags); - raw_notifier_call_chain(&edev->nh, old_state ^ edev->state, - edev); + if (edev->state != ((edev->state & ~mask) | (state & mask))) { + u32 old_state = edev->state; - prop_buf = (char *)get_zeroed_page(GFP_KERNEL); + edev->state &= ~mask; + edev->state |= state & mask; + + raw_notifier_call_chain(&edev->nh, old_state, edev); + + /* This could be in interrupt handler */ + prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); if (prop_buf) { length = name_show(edev->dev, NULL, prop_buf); if (length > 0) { @@ -117,16 +231,131 @@ void extcon_set_state(struct extcon_dev *edev, u32 state) envp[env_offset++] = state_buf; } envp[env_offset] = NULL; + /* Unlock early before uevent */ + spin_unlock_irqrestore(&edev->lock, flags); + kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp); free_page((unsigned long)prop_buf); } else { + /* Unlock early before uevent */ + spin_unlock_irqrestore(&edev->lock, flags); + dev_err(edev->dev, "out of memory in extcon_set_state\n"); kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE); } + } else { + /* No changes */ + spin_unlock_irqrestore(&edev->lock, flags); } } +EXPORT_SYMBOL_GPL(extcon_update_state); + +/** + * extcon_set_state() - Set the cable attach states of the extcon device. + * @edev: the extcon device + * @state: new cable attach status for @edev + * + * Note that notifier provides which bits are changed in the state + * variable with the val parameter (second) to the callback. + */ +void extcon_set_state(struct extcon_dev *edev, u32 state) +{ + extcon_update_state(edev, 0xffffffff, state); +} EXPORT_SYMBOL_GPL(extcon_set_state); +/** + * extcon_find_cable_index() - Get the cable index based on the cable name. + * @edev: the extcon device that has the cable. + * @cable_name: cable name to be searched. + * + * Note that accessing a cable state based on cable_index is faster than + * cable_name because using cable_name induces a loop with strncmp(). + * Thus, when get/set_cable_state is repeatedly used, using cable_index + * is recommended. + */ +int extcon_find_cable_index(struct extcon_dev *edev, const char *cable_name) +{ + int i; + + if (edev->supported_cable) { + for (i = 0; edev->supported_cable[i]; i++) { + if (!strncmp(edev->supported_cable[i], + cable_name, CABLE_NAME_MAX)) + return i; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(extcon_find_cable_index); + +/** + * extcon_get_cable_state_() - Get the status of a specific cable. + * @edev: the extcon device that has the cable. + * @index: cable index that can be retrieved by extcon_find_cable_index(). + */ +int extcon_get_cable_state_(struct extcon_dev *edev, int index) +{ + if (index < 0 || (edev->max_supported && edev->max_supported <= index)) + return -EINVAL; + + return !!(edev->state & (1 << index)); +} +EXPORT_SYMBOL_GPL(extcon_get_cable_state_); + +/** + * extcon_get_cable_state() - Get the status of a specific cable. + * @edev: the extcon device that has the cable. + * @cable_name: cable name. + * + * Note that this is slower than extcon_get_cable_state_. + */ +int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name) +{ + return extcon_get_cable_state_(edev, extcon_find_cable_index + (edev, cable_name)); +} +EXPORT_SYMBOL_GPL(extcon_get_cable_state); + +/** + * extcon_get_cable_state_() - Set the status of a specific cable. + * @edev: the extcon device that has the cable. + * @index: cable index that can be retrieved by extcon_find_cable_index(). + * @cable_state: the new cable status. The default semantics is + * true: attached / false: detached. + */ +int extcon_set_cable_state_(struct extcon_dev *edev, + int index, bool cable_state) +{ + u32 state; + + if (index < 0 || (edev->max_supported && edev->max_supported <= index)) + return -EINVAL; + + state = cable_state ? (1 << index) : 0; + extcon_update_state(edev, 1 << index, state); + return 0; +} +EXPORT_SYMBOL_GPL(extcon_set_cable_state_); + +/** + * extcon_get_cable_state() - Set the status of a specific cable. + * @edev: the extcon device that has the cable. + * @cable_name: cable name. + * @cable_state: the new cable status. The default semantics is + * true: attached / false: detached. + * + * Note that this is slower than extcon_set_cable_state_. + */ +int extcon_set_cable_state(struct extcon_dev *edev, + const char *cable_name, bool cable_state) +{ + return extcon_set_cable_state_(edev, extcon_find_cable_index + (edev, cable_name), cable_state); +} +EXPORT_SYMBOL_GPL(extcon_set_cable_state); + /** * extcon_get_extcon_dev() - Get the extcon device instance from the name * @extcon_name: The extcon name provided with extcon_dev_register() @@ -147,11 +376,88 @@ out: } EXPORT_SYMBOL_GPL(extcon_get_extcon_dev); +static int _call_per_cable(struct notifier_block *nb, unsigned long val, + void *ptr) +{ + struct extcon_specific_cable_nb *obj = container_of(nb, + struct extcon_specific_cable_nb, internal_nb); + struct extcon_dev *edev = ptr; + + if ((val & (1 << obj->cable_index)) != + (edev->state & (1 << obj->cable_index))) { + obj->previous_value = val; + return obj->user_nb->notifier_call(obj->user_nb, val, ptr); + } + + return NOTIFY_OK; +} + +/** + * extcon_register_interest() - Register a notifier for a state change of a + * specific cable, not a entier set of cables of a + * extcon device. + * @obj: an empty extcon_specific_cable_nb object to be returned. + * @extcon_name: the name of extcon device. + * @cable_name: the target cable name. + * @nb: the notifier block to get notified. + * + * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets + * the struct for you. + * + * extcon_register_interest is a helper function for those who want to get + * notification for a single specific cable's status change. If a user wants + * to get notification for any changes of all cables of a extcon device, + * he/she should use the general extcon_register_notifier(). + * + * Note that the second parameter given to the callback of nb (val) is + * "old_state", not the current state. The current state can be retrieved + * by looking at the third pameter (edev pointer)'s state value. + */ +int extcon_register_interest(struct extcon_specific_cable_nb *obj, + const char *extcon_name, const char *cable_name, + struct notifier_block *nb) +{ + if (!obj || !extcon_name || !cable_name || !nb) + return -EINVAL; + + obj->edev = extcon_get_extcon_dev(extcon_name); + if (!obj->edev) + return -ENODEV; + + obj->cable_index = extcon_find_cable_index(obj->edev, cable_name); + if (obj->cable_index < 0) + return -ENODEV; + + obj->user_nb = nb; + + obj->internal_nb.notifier_call = _call_per_cable; + + return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb); +} + +/** + * extcon_unregister_interest() - Unregister the notifier registered by + * extcon_register_interest(). + * @obj: the extcon_specific_cable_nb object returned by + * extcon_register_interest(). + */ +int extcon_unregister_interest(struct extcon_specific_cable_nb *obj) +{ + if (!obj) + return -EINVAL; + + return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb); +} + /** * extcon_register_notifier() - Register a notifee to get notified by * any attach status changes from the extcon. * @edev: the extcon device. * @nb: a notifier block to be registered. + * + * Note that the second parameter given to the callback of nb (val) is + * "old_state", not the current state. The current state can be retrieved + * by looking at the third pameter (edev pointer)'s state value. */ int extcon_register_notifier(struct extcon_dev *edev, struct notifier_block *nb) @@ -173,8 +479,9 @@ int extcon_unregister_notifier(struct extcon_dev *edev, EXPORT_SYMBOL_GPL(extcon_unregister_notifier); static struct device_attribute extcon_attrs[] = { - __ATTR_RO(state), + __ATTR(state, S_IRUGO | S_IWUSR, state_show, state_store), __ATTR_RO(name), + __ATTR_NULL, }; static int create_extcon_class(void) @@ -202,6 +509,16 @@ static void extcon_cleanup(struct extcon_dev *edev, bool skip) mutex_unlock(&extcon_dev_list_lock); if (!skip && get_device(edev->dev)) { + int index; + + for (index = 0; index < edev->max_supported; index++) + kfree(edev->cables[index].attr_g.name); + + if (edev->max_supported) { + kfree(edev->extcon_dev_type.groups); + kfree(edev->cables); + } + device_unregister(edev->dev); put_device(edev->dev); } @@ -216,6 +533,10 @@ static void extcon_dev_release(struct device *dev) extcon_cleanup(edev, true); } +static void dummy_sysfs_dev_release(struct device *dev) +{ +} + /** * extcon_dev_register() - Register a new extcon device * @edev : the new extcon device (should be allocated before calling) @@ -228,7 +549,7 @@ static void extcon_dev_release(struct device *dev) */ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) { - int ret; + int ret, index = 0; if (!extcon_class) { ret = create_extcon_class(); @@ -236,12 +557,93 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) return ret; } + if (edev->supported_cable) { + /* Get size of array */ + for (index = 0; edev->supported_cable[index]; index++) + ; + edev->max_supported = index; + } else { + edev->max_supported = 0; + } + + if (index > SUPPORTED_CABLE_MAX) { + dev_err(edev->dev, "extcon: maximum number of supported cables exceeded.\n"); + return -EINVAL; + } + edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL); edev->dev->parent = dev; edev->dev->class = extcon_class; edev->dev->release = extcon_dev_release; dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev)); + + if (edev->max_supported) { + char buf[10]; + char *str; + struct extcon_cable *cable; + + edev->cables = kzalloc(sizeof(struct extcon_cable) * + edev->max_supported, GFP_KERNEL); + if (!edev->cables) { + ret = -ENOMEM; + goto err_sysfs_alloc; + } + for (index = 0; index < edev->max_supported; index++) { + cable = &edev->cables[index]; + + snprintf(buf, 10, "cable.%d", index); + str = kzalloc(sizeof(char) * (strlen(buf) + 1), + GFP_KERNEL); + if (!str) { + for (index--; index >= 0; index--) { + cable = &edev->cables[index]; + kfree(cable->attr_g.name); + } + ret = -ENOMEM; + + goto err_alloc_cables; + } + strcpy(str, buf); + + cable->edev = edev; + cable->cable_index = index; + cable->attrs[0] = &cable->attr_name.attr; + cable->attrs[1] = &cable->attr_state.attr; + cable->attrs[2] = NULL; + cable->attr_g.name = str; + cable->attr_g.attrs = cable->attrs; + + cable->attr_name.attr.name = "name"; + cable->attr_name.attr.mode = 0444; + cable->attr_name.show = cable_name_show; + + cable->attr_state.attr.name = "state"; + cable->attr_state.attr.mode = 0644; + cable->attr_state.show = cable_state_show; + cable->attr_state.store = cable_state_store; + } + } + + if (edev->max_supported) { + edev->extcon_dev_type.groups = + kzalloc(sizeof(struct attribute_group *) * + (edev->max_supported + 1), GFP_KERNEL); + if (!edev->extcon_dev_type.groups) { + ret = -ENOMEM; + goto err_alloc_groups; + } + + edev->extcon_dev_type.name = dev_name(edev->dev); + edev->extcon_dev_type.release = dummy_sysfs_dev_release; + + for (index = 0; index < edev->max_supported; index++) + edev->extcon_dev_type.groups[index] = + &edev->cables[index].attr_g; + + edev->dev->type = &edev->extcon_dev_type; + } + ret = device_register(edev->dev); if (ret) { put_device(edev->dev); @@ -253,6 +655,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) dev); #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ + spin_lock_init(&edev->lock); + RAW_INIT_NOTIFIER_HEAD(&edev->nh); dev_set_drvdata(edev->dev, edev); @@ -265,6 +669,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) return 0; err_dev: + if (edev->max_supported) + kfree(edev->extcon_dev_type.groups); +err_alloc_groups: + for (index = 0; index < edev->max_supported; index++) + kfree(edev->cables[index].attr_g.name); +err_alloc_cables: + if (edev->max_supported) + kfree(edev->cables); +err_sysfs_alloc: kfree(edev->dev); return ret; } diff --git a/include/linux/extcon.h b/include/linux/extcon.h index c9c9afe12b47..20e24b32a17d 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -24,10 +24,60 @@ #define __LINUX_EXTCON_H__ #include + +#define SUPPORTED_CABLE_MAX 32 +#define CABLE_NAME_MAX 30 + +/* + * The standard cable name is to help support general notifier + * and notifee device drivers to share the common names. + * Please use standard cable names unless your notifier device has + * a very unique and abnormal cable or + * the cable type is supposed to be used with only one unique + * pair of notifier/notifee devices. + * + * Please add any other "standard" cables used with extcon dev. + * + * You may add a dot and number to specify version or specification + * of the specific cable if it is required. (e.g., "Fast-charger.18" + * and "Fast-charger.10" for 1.8A and 1.0A chargers) + * However, the notifee and notifier should be able to handle such + * string and if the notifee can negotiate the protocol or idenify, + * you don't need such convention. This convention is helpful when + * notifier can distinguish but notifiee cannot. + */ +enum extcon_cable_name { + EXTCON_USB = 0, + EXTCON_USB_HOST, + EXTCON_TA, /* Travel Adaptor */ + EXTCON_FAST_CHARGER, + EXTCON_SLOW_CHARGER, + EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */ + EXTCON_HDMI, + EXTCON_MHL, + EXTCON_DVI, + EXTCON_VGA, + EXTCON_DOCK, + EXTCON_LINE_IN, + EXTCON_LINE_OUT, + EXTCON_MIC_IN, + EXTCON_HEADPHONE_OUT, + EXTCON_SPDIF_IN, + EXTCON_SPDIF_OUT, + EXTCON_VIDEO_IN, + EXTCON_VIDEO_OUT, +}; +extern const char *extcon_cable_name[]; + +struct extcon_cable; + /** * struct extcon_dev - An extcon device represents one external connector. * @name The name of this extcon device. Parent device name is used * if NULL. + * @supported_cable Array of supported cable name ending with NULL. + * If supported_cable is NULL, cable name related APIs + * are disabled. * @print_name An optional callback to override the method to print the * name of the extcon device. * @print_state An optional callback to override the method to print the @@ -38,6 +88,11 @@ * @nh Notifier for the state change events from this extcon * @entry To support list of extcon devices so that uses can search * for extcon devices based on the extcon name. + * @lock + * @max_supported Internal value to store the number of cables. + * @extcon_dev_type Device_type struct to provide attribute_groups + * customized for each extcon device. + * @cables Sysfs subdirectories. Each represents one cable. * * In most cases, users only need to provide "User initializing data" of * this struct when registering an extcon. In some exceptional cases, @@ -47,6 +102,7 @@ struct extcon_dev { /* --- Optional user initializing data --- */ const char *name; + const char **supported_cable; /* --- Optional callbacks to override class functions --- */ ssize_t (*print_name)(struct extcon_dev *edev, char *buf); @@ -57,6 +113,49 @@ struct extcon_dev { u32 state; struct raw_notifier_head nh; struct list_head entry; + spinlock_t lock; /* could be called by irq handler */ + int max_supported; + + /* /sys/class/extcon/.../cable.n/... */ + struct device_type extcon_dev_type; + struct extcon_cable *cables; +}; + +/** + * struct extcon_cable - An internal data for each cable of extcon device. + * @edev The extcon device + * @cable_index Index of this cable in the edev + * @attr_g Attribute group for the cable + * @attr_name "name" sysfs entry + * @attr_state "state" sysfs entry + * @attrs Array pointing to attr_name and attr_state for attr_g + */ +struct extcon_cable { + struct extcon_dev *edev; + int cable_index; + + struct attribute_group attr_g; + struct device_attribute attr_name; + struct device_attribute attr_state; + + struct attribute *attrs[3]; /* to be fed to attr_g.attrs */ +}; + +/** + * struct extcon_specific_cable_nb - An internal data for + * extcon_register_interest(). + * @internal_nb a notifier block bridging extcon notifier and cable notifier. + * @user_nb user provided notifier block for events from a specific cable. + * @cable_index the target cable. + * @edev the target extcon device. + * @previous_value the saved previous event value. + */ +struct extcon_specific_cable_nb { + struct notifier_block internal_nb; + struct notifier_block *user_nb; + int cable_index; + struct extcon_dev *edev; + unsigned long previous_value; }; #if IS_ENABLED(CONFIG_EXTCON) @@ -69,16 +168,54 @@ extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev); extern void extcon_dev_unregister(struct extcon_dev *edev); extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); +/* + * get/set/update_state access the 32b encoded state value, which represents + * states of all possible cables of the multistate port. For example, if one + * calls extcon_set_state(edev, 0x7), it may mean that all the three cables + * are attached to the port. + */ static inline u32 extcon_get_state(struct extcon_dev *edev) { return edev->state; } extern void extcon_set_state(struct extcon_dev *edev, u32 state); +extern void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state); + +/* + * get/set_cable_state access each bit of the 32b encoded state value. + * They are used to access the status of each cable based on the cable_name + * or cable_index, which is retrived by extcon_find_cable_index + */ +extern int extcon_find_cable_index(struct extcon_dev *sdev, + const char *cable_name); +extern int extcon_get_cable_state_(struct extcon_dev *edev, int cable_index); +extern int extcon_set_cable_state_(struct extcon_dev *edev, int cable_index, + bool cable_state); + +extern int extcon_get_cable_state(struct extcon_dev *edev, + const char *cable_name); +extern int extcon_set_cable_state(struct extcon_dev *edev, + const char *cable_name, bool cable_state); + +/* + * Following APIs are for notifiees (those who want to be notified) + * to register a callback for events from a specific cable of the extcon. + * Notifiees are the connected device drivers wanting to get notified by + * a specific external port of a connection device. + */ +extern int extcon_register_interest(struct extcon_specific_cable_nb *obj, + const char *extcon_name, + const char *cable_name, + struct notifier_block *nb); +extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb); /* * Following APIs are to monitor every action of a notifier. * Registerer gets notified for every external port of a connection device. + * Probably this could be used to debug an action of notifier; however, + * we do not recommend to use this at normal 'notifiee' device drivers who + * want to be notified by a specific external port of the notifier. */ extern int extcon_register_notifier(struct extcon_dev *edev, struct notifier_block *nb); @@ -99,6 +236,41 @@ static inline u32 extcon_get_state(struct extcon_dev *edev) } static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { } + +static inline void extcon_update_state(struct extcon_dev *edev, u32 mask, + u32 state) +{ } + +static inline int extcon_find_cable_index(struct extcon_dev *edev, + const char *cable_name) +{ + return 0; +} + +static inline int extcon_get_cable_state_(struct extcon_dev *edev, + int cable_index) +{ + return 0; +} + +static inline int extcon_set_cable_state_(struct extcon_dev *edev, + int cable_index, bool cable_state) +{ + return 0; +} + +static inline int extcon_get_cable_state(struct extcon_dev *edev, + const char *cable_name) +{ + return 0; +} + +static inline int extcon_set_cable_state(struct extcon_dev *edev, + const char *cable_name, int state) +{ + return 0; +} + static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) { return NULL; @@ -116,5 +288,18 @@ static inline int extcon_unregister_notifier(struct extcon_dev *edev, return 0; } +static inline int extcon_register_interest(struct extcon_specific_cable_nb *obj, + const char *extcon_name, + const char *cable_name, + struct notifier_block *nb) +{ + return 0; +} + +static inline int extcon_unregister_interest(struct extcon_specific_cable_nb + *obj) +{ + return 0; +} #endif /* CONFIG_EXTCON */ #endif /* __LINUX_EXTCON_H__ */ -- cgit v1.2.3 From bde68e60b18208978c50c6fb9bdf29826d2887f3 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Fri, 20 Apr 2012 14:16:26 +0900 Subject: Extcon: support mutually exclusive relation between cables. There could be cables that t recannot be attaches simulatenously. Extcon device drivers may express such information via mutually_exclusive in struct extcon_dev. For example, for an extcon device with 16 cables (bits 0 to 15 are available), if mutually_exclusive = { 0x7, 0xC0, 0x81, 0 }, then, the following attachments are prohibitted. {0, 1} {0, 2} {1, 2} {6, 7} {0, 7} and every attachment set that are superset of one of the above. For the detail, please refer to linux/include/linux/extcon.h. The concept is suggested by NeilBrown Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park -- Changes from V5: - Updated sysfs format Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-extcon | 22 +++++ drivers/extcon/extcon_class.c | 123 +++++++++++++++++++++++++-- include/linux/extcon.h | 28 ++++-- 3 files changed, 160 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon index 19b18e986a57..20ab361bd8c6 100644 --- a/Documentation/ABI/testing/sysfs-class-extcon +++ b/Documentation/ABI/testing/sysfs-class-extcon @@ -15,6 +15,10 @@ Description: may have both HDMI and Charger attached, or analog audio, video, and USB cables attached simulteneously. + If there are cables mutually exclusive with each other, + such binary relations may be expressed with extcon_dev's + mutually_exclusive array. + What: /sys/class/extcon/.../name Date: February 2012 Contact: MyungJoo Ham @@ -73,3 +77,21 @@ Description: state of cable "x" (integer between 0 and 31) of an extcon device. The state value is either 0 (detached) or 1 (attached). + +What: /sys/class/extcon/.../mutually_exclusive/... +Date: December 2011 +Contact: MyungJoo Ham +Description: + Shows the relations of mutually exclusiveness. For example, + if the mutually_exclusive array of extcon_dev is + {0x3, 0x5, 0xC, 0x0}, the, the output is: + # ls mutually_exclusive/ + 0x3 + 0x5 + 0xc + # + + Note that mutually_exclusive is a sub-directory of the extcon + device and the file names under the mutually_exclusive + directory show the mutually-exclusive sets, not the contents + of the files. diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c index 403933bc3e9c..3bc4b8af46cf 100644 --- a/drivers/extcon/extcon_class.c +++ b/drivers/extcon/extcon_class.c @@ -72,6 +72,39 @@ static struct class_compat *switch_class; static LIST_HEAD(extcon_dev_list); static DEFINE_MUTEX(extcon_dev_list_lock); +/** + * check_mutually_exclusive - Check if new_state violates mutually_exclusive + * condition. + * @edev: the extcon device + * @new_state: new cable attach status for @edev + * + * Returns 0 if nothing violates. Returns the index + 1 for the first + * violated condition. + */ +static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state) +{ + int i = 0; + + if (!edev->mutually_exclusive) + return 0; + + for (i = 0; edev->mutually_exclusive[i]; i++) { + int count = 0, j; + u32 correspondants = new_state & edev->mutually_exclusive[i]; + u32 exp = 1; + + for (j = 0; j < 32; j++) { + if (exp & correspondants) + count++; + if (count > 1) + return i + 1; + exp <<= 1; + } + } + + return 0; +} + static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -100,7 +133,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, return count; } -void extcon_set_state(struct extcon_dev *edev, u32 state); +int extcon_set_state(struct extcon_dev *edev, u32 state); static ssize_t state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -112,7 +145,7 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr, if (ret == 0) ret = -EINVAL; else - extcon_set_state(edev, state); + ret = extcon_set_state(edev, state); if (ret < 0) return ret; @@ -191,7 +224,7 @@ static ssize_t cable_state_store(struct device *dev, * Note that the notifier provides which bits are changed in the state * variable with the val parameter (second) to the callback. */ -void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) +int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) { char name_buf[120]; char state_buf[120]; @@ -206,6 +239,12 @@ void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) if (edev->state != ((edev->state & ~mask) | (state & mask))) { u32 old_state = edev->state; + if (check_mutually_exclusive(edev, (edev->state & ~mask) | + (state & mask))) { + spin_unlock_irqrestore(&edev->lock, flags); + return -EPERM; + } + edev->state &= ~mask; edev->state |= state & mask; @@ -247,6 +286,8 @@ void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) /* No changes */ spin_unlock_irqrestore(&edev->lock, flags); } + + return 0; } EXPORT_SYMBOL_GPL(extcon_update_state); @@ -258,9 +299,9 @@ EXPORT_SYMBOL_GPL(extcon_update_state); * Note that notifier provides which bits are changed in the state * variable with the val parameter (second) to the callback. */ -void extcon_set_state(struct extcon_dev *edev, u32 state) +int extcon_set_state(struct extcon_dev *edev, u32 state) { - extcon_update_state(edev, 0xffffffff, state); + return extcon_update_state(edev, 0xffffffff, state); } EXPORT_SYMBOL_GPL(extcon_set_state); @@ -334,8 +375,7 @@ int extcon_set_cable_state_(struct extcon_dev *edev, return -EINVAL; state = cable_state ? (1 << index) : 0; - extcon_update_state(edev, 1 << index, state); - return 0; + return extcon_update_state(edev, 1 << index, state); } EXPORT_SYMBOL_GPL(extcon_set_cable_state_); @@ -511,6 +551,14 @@ static void extcon_cleanup(struct extcon_dev *edev, bool skip) if (!skip && get_device(edev->dev)) { int index; + if (edev->mutually_exclusive && edev->max_supported) { + for (index = 0; edev->mutually_exclusive[index]; + index++) + kfree(edev->d_attrs_muex[index].attr.name); + kfree(edev->d_attrs_muex); + kfree(edev->attrs_muex); + } + for (index = 0; index < edev->max_supported; index++) kfree(edev->cables[index].attr_g.name); @@ -533,6 +581,7 @@ static void extcon_dev_release(struct device *dev) extcon_cleanup(edev, true); } +static const char *muex_name = "mutually_exclusive"; static void dummy_sysfs_dev_release(struct device *dev) { } @@ -625,10 +674,58 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) } } + if (edev->max_supported && edev->mutually_exclusive) { + char buf[80]; + char *name; + + /* Count the size of mutually_exclusive array */ + for (index = 0; edev->mutually_exclusive[index]; index++) + ; + + edev->attrs_muex = kzalloc(sizeof(struct attribute *) * + (index + 1), GFP_KERNEL); + if (!edev->attrs_muex) { + ret = -ENOMEM; + goto err_muex; + } + + edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) * + index, GFP_KERNEL); + if (!edev->d_attrs_muex) { + ret = -ENOMEM; + kfree(edev->attrs_muex); + goto err_muex; + } + + for (index = 0; edev->mutually_exclusive[index]; index++) { + sprintf(buf, "0x%x", edev->mutually_exclusive[index]); + name = kzalloc(sizeof(char) * (strlen(buf) + 1), + GFP_KERNEL); + if (!name) { + for (index--; index >= 0; index--) { + kfree(edev->d_attrs_muex[index].attr. + name); + } + kfree(edev->d_attrs_muex); + kfree(edev->attrs_muex); + ret = -ENOMEM; + goto err_muex; + } + strcpy(name, buf); + edev->d_attrs_muex[index].attr.name = name; + edev->d_attrs_muex[index].attr.mode = 0000; + edev->attrs_muex[index] = &edev->d_attrs_muex[index] + .attr; + } + edev->attr_g_muex.name = muex_name; + edev->attr_g_muex.attrs = edev->attrs_muex; + + } + if (edev->max_supported) { edev->extcon_dev_type.groups = kzalloc(sizeof(struct attribute_group *) * - (edev->max_supported + 1), GFP_KERNEL); + (edev->max_supported + 2), GFP_KERNEL); if (!edev->extcon_dev_type.groups) { ret = -ENOMEM; goto err_alloc_groups; @@ -640,6 +737,9 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) for (index = 0; index < edev->max_supported; index++) edev->extcon_dev_type.groups[index] = &edev->cables[index].attr_g; + if (edev->mutually_exclusive) + edev->extcon_dev_type.groups[index] = + &edev->attr_g_muex; edev->dev->type = &edev->extcon_dev_type; } @@ -672,6 +772,13 @@ err_dev: if (edev->max_supported) kfree(edev->extcon_dev_type.groups); err_alloc_groups: + if (edev->max_supported && edev->mutually_exclusive) { + for (index = 0; edev->mutually_exclusive[index]; index++) + kfree(edev->d_attrs_muex[index].attr.name); + kfree(edev->d_attrs_muex); + kfree(edev->attrs_muex); + } +err_muex: for (index = 0; index < edev->max_supported; index++) kfree(edev->cables[index].attr_g.name); err_alloc_cables: diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 20e24b32a17d..6495f7731400 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -78,6 +78,14 @@ struct extcon_cable; * @supported_cable Array of supported cable name ending with NULL. * If supported_cable is NULL, cable name related APIs * are disabled. + * @mutually_exclusive Array of mutually exclusive set of cables that cannot + * be attached simultaneously. The array should be + * ending with NULL or be NULL (no mutually exclusive + * cables). For example, if it is { 0x7, 0x30, 0}, then, + * {0, 1}, {0, 1, 2}, {0, 2}, {1, 2}, or {4, 5} cannot + * be attached simulataneously. {0x7, 0} is equivalent to + * {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there + * can be no simultaneous connections. * @print_name An optional callback to override the method to print the * name of the extcon device. * @print_state An optional callback to override the method to print the @@ -103,6 +111,7 @@ struct extcon_dev { /* --- Optional user initializing data --- */ const char *name; const char **supported_cable; + const u32 *mutually_exclusive; /* --- Optional callbacks to override class functions --- */ ssize_t (*print_name)(struct extcon_dev *edev, char *buf); @@ -119,6 +128,10 @@ struct extcon_dev { /* /sys/class/extcon/.../cable.n/... */ struct device_type extcon_dev_type; struct extcon_cable *cables; + /* /sys/class/extcon/.../mutually_exclusive/... */ + struct attribute_group attr_g_muex; + struct attribute **attrs_muex; + struct device_attribute *d_attrs_muex; }; /** @@ -179,8 +192,8 @@ static inline u32 extcon_get_state(struct extcon_dev *edev) return edev->state; } -extern void extcon_set_state(struct extcon_dev *edev, u32 state); -extern void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state); +extern int extcon_set_state(struct extcon_dev *edev, u32 state); +extern int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state); /* * get/set_cable_state access each bit of the 32b encoded state value. @@ -235,11 +248,16 @@ static inline u32 extcon_get_state(struct extcon_dev *edev) return 0; } -static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { } +static inline int extcon_set_state(struct extcon_dev *edev, u32 state) +{ + return 0; +} -static inline void extcon_update_state(struct extcon_dev *edev, u32 mask, +static inline int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) -{ } +{ + return 0; +} static inline int extcon_find_cable_index(struct extcon_dev *edev, const char *cable_name) -- cgit v1.2.3 From 4ccf4beab8c447f8cd33d46afb6e10e1aa3befc6 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 31 Aug 2011 20:35:40 +0200 Subject: lib: add support for stmp-style devices MX23/28 use IP cores which follow a register layout I have first seen on STMP3xxx SoCs. In this layout, every register actually has four u32: 1.) to store a value directly 2.) a SET register where every 1-bit sets the corresponding bit, others are unaffected 3.) same with a CLR register 4.) same with a TOG (toggle) register Also, the 2 MSBs in register 0 are always the same and can be used to reset the IP core. All this is strictly speaking not mach-specific (but IP core specific) and, thus, doesn't need to be in mach-mxs/include. At least mx6 also uses IP cores following this stmp-style. So: Introduce a stmp-style device, put the code and defines for that in a public place (lib/), and let drivers for stmp-style devices select that code. To avoid regressions and ease reviewing, the actual code is simply copied from mach-mxs. It definately wants updates, but those need a seperate patch series. Voila, mach dependency gone, reusable code introduced. Note that I didn't remove the duplicated code from mach-mxs yet, first the drivers have to be converted. Signed-off-by: Wolfram Sang Acked-by: Shawn Guo Acked-by: Dong Aisheng --- include/linux/stmp_device.h | 20 ++++++++++++ lib/Kconfig | 3 ++ lib/Makefile | 2 ++ lib/stmp_device.c | 80 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 include/linux/stmp_device.h create mode 100644 lib/stmp_device.c (limited to 'include/linux') diff --git a/include/linux/stmp_device.h b/include/linux/stmp_device.h new file mode 100644 index 000000000000..6cf7ec9547cf --- /dev/null +++ b/include/linux/stmp_device.h @@ -0,0 +1,20 @@ +/* + * basic functions for devices following the "stmp" style register layout + * + * Copyright (C) 2011 Wolfram Sang, Pengutronix e.K. + * + * 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. + */ + +#ifndef __STMP_DEVICE_H__ +#define __STMP_DEVICE_H__ + +#define STMP_OFFSET_REG_SET 0x4 +#define STMP_OFFSET_REG_CLR 0x8 +#define STMP_OFFSET_REG_TOG 0xc + +extern int stmp_reset_block(void __iomem *); +#endif /* __STMP_DEVICE_H__ */ diff --git a/lib/Kconfig b/lib/Kconfig index 4a8aba2e5cc0..c5da1548b964 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -33,6 +33,9 @@ config GENERIC_IO boolean default n +config STMP_DEVICE + bool + config CRC_CCITT tristate "CRC-CCITT functions" help diff --git a/lib/Makefile b/lib/Makefile index 18515f0267c4..f78dbcdc7e3d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -123,6 +123,8 @@ obj-$(CONFIG_SIGNATURE) += digsig.o obj-$(CONFIG_CLZ_TAB) += clz_tab.o +obj-$(CONFIG_STMP_DEVICE) += stmp_device.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/stmp_device.c b/lib/stmp_device.c new file mode 100644 index 000000000000..8ac9bcc4289a --- /dev/null +++ b/lib/stmp_device.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 1999 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd + * Copyright 2006-2007,2010 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * Copyright 2009 Ilya Yanok, Emcraft Systems Ltd, yanok@emcraft.com + * Copyright (C) 2011 Wolfram Sang, Pengutronix e.K. + * + * 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 + +#define STMP_MODULE_CLKGATE (1 << 30) +#define STMP_MODULE_SFTRST (1 << 31) + +/* + * Clear the bit and poll it cleared. This is usually called with + * a reset address and mask being either SFTRST(bit 31) or CLKGATE + * (bit 30). + */ +static int stmp_clear_poll_bit(void __iomem *addr, u32 mask) +{ + int timeout = 0x400; + + writel(mask, addr + STMP_OFFSET_REG_CLR); + udelay(1); + while ((readl(addr) & mask) && --timeout) + /* nothing */; + + return !timeout; +} + +int stmp_reset_block(void __iomem *reset_addr) +{ + int ret; + int timeout = 0x400; + + /* clear and poll SFTRST */ + ret = stmp_clear_poll_bit(reset_addr, STMP_MODULE_SFTRST); + if (unlikely(ret)) + goto error; + + /* clear CLKGATE */ + writel(STMP_MODULE_CLKGATE, reset_addr + STMP_OFFSET_REG_CLR); + + /* set SFTRST to reset the block */ + writel(STMP_MODULE_SFTRST, reset_addr + STMP_OFFSET_REG_SET); + udelay(1); + + /* poll CLKGATE becoming set */ + while ((!(readl(reset_addr) & STMP_MODULE_CLKGATE)) && --timeout) + /* nothing */; + if (unlikely(!timeout)) + goto error; + + /* clear and poll SFTRST */ + ret = stmp_clear_poll_bit(reset_addr, STMP_MODULE_SFTRST); + if (unlikely(ret)) + goto error; + + /* clear and poll CLKGATE */ + ret = stmp_clear_poll_bit(reset_addr, STMP_MODULE_CLKGATE); + if (unlikely(ret)) + goto error; + + return 0; + +error: + pr_err("%s(%p): module reset timeout\n", __func__, reset_addr); + return -ETIMEDOUT; +} +EXPORT_SYMBOL(stmp_reset_block); -- cgit v1.2.3 From 57c498fa5df05e4bd410c101795368439fec7b4e Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 20 Apr 2012 12:31:45 -0700 Subject: alarmtimer: Provide accessor to alarmtimer rtc device The Android alarm interface provides a settime call that sets both the alarmtimer RTC device and CLOCK_REALTIME to the same value. Since there may be multiple rtc devices, provide a hook to access the one the alarmtimer infrastructure is using. CC: Colin Cross CC: Thomas Gleixner CC: Android Kernel Team Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- include/linux/alarmtimer.h | 3 +++ kernel/time/alarmtimer.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h index 975009e1cbe6..96c5c249b086 100644 --- a/include/linux/alarmtimer.h +++ b/include/linux/alarmtimer.h @@ -76,4 +76,7 @@ static inline int alarmtimer_callback_running(struct alarm *timer) } +/* Provide way to access the rtc device being used by alarmtimers */ +struct rtc_device *alarmtimer_get_rtcdev(void); + #endif diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 8a538c55fc7b..aa27d391bfc8 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -59,7 +59,7 @@ static DEFINE_SPINLOCK(rtcdev_lock); * If one has not already been chosen, it checks to see if a * functional rtc device is available. */ -static struct rtc_device *alarmtimer_get_rtcdev(void) +struct rtc_device *alarmtimer_get_rtcdev(void) { unsigned long flags; struct rtc_device *ret; @@ -115,7 +115,7 @@ static void alarmtimer_rtc_interface_remove(void) class_interface_unregister(&alarmtimer_rtc_interface); } #else -static inline struct rtc_device *alarmtimer_get_rtcdev(void) +struct rtc_device *alarmtimer_get_rtcdev(void) { return NULL; } -- cgit v1.2.3 From a5347fe36b313c07d59b065d00a8fa56362c5f97 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 19 Apr 2012 13:45:29 +0000 Subject: net: Delete all remaining instances of ctl_path We don't use struct ctl_path anymore so delete the exported constants. Signed-off-by: Eric W. Biederman Acked-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/netfilter.h | 6 ------ include/net/ip.h | 3 --- include/net/ip_vs.h | 2 -- include/net/ipv6.h | 1 - net/core/sysctl_net_core.c | 6 ------ net/ipv4/netfilter.c | 10 ---------- net/ipv4/sysctl_net_ipv4.c | 7 ------- net/ipv6/sysctl_net_ipv6.c | 7 ------- net/netfilter/core.c | 9 --------- net/netfilter/ipvs/ip_vs_ctl.c | 7 ------- 10 files changed, 58 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 29734be334c1..ff9c84c29b28 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -154,12 +154,6 @@ void nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n); int nf_register_sockopt(struct nf_sockopt_ops *reg); void nf_unregister_sockopt(struct nf_sockopt_ops *reg); -#ifdef CONFIG_SYSCTL -/* Sysctl registration */ -extern struct ctl_path nf_net_netfilter_sysctl_path[]; -extern struct ctl_path nf_net_ipv4_netfilter_sysctl_path[]; -#endif /* CONFIG_SYSCTL */ - extern struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; #if defined(CONFIG_JUMP_LABEL) diff --git a/include/net/ip.h b/include/net/ip.h index b53d65f24f7b..94ddb69cc0f3 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -222,9 +222,6 @@ static inline int inet_is_reserved_local_port(int port) extern int sysctl_ip_nonlocal_bind; -extern struct ctl_path net_core_path[]; -extern struct ctl_path net_ipv4_ctl_path[]; - /* From inetpeer.c */ extern int inet_peer_threshold; extern int inet_peer_minttl; diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index a903a828bcef..990216ded6b3 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -10,7 +10,6 @@ #include /* for __uXX types */ -#include /* for ctl_path */ #include /* for struct list_head */ #include /* for struct rwlock_t */ #include /* for struct atomic_t */ @@ -1184,7 +1183,6 @@ extern void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg); * IPVS control data and functions (from ip_vs_ctl.c) */ extern struct ip_vs_stats ip_vs_stats; -extern const struct ctl_path net_vs_ctl_path[]; extern int sysctl_ip_vs_sync_ver; extern void ip_vs_sync_switch_mode(struct net *net, int mode); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index e1a85adbeee3..4332e9aad853 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -113,7 +113,6 @@ struct frag_hdr { /* sysctls */ extern int sysctl_mld_max_msf; -extern struct ctl_path net_ipv6_ctl_path[]; #define _DEVINC(net, statname, modifier, idev, field) \ ({ \ diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 64924e345a9b..a7c36845b123 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -203,12 +203,6 @@ static struct ctl_table netns_core_table[] = { { } }; -__net_initdata struct ctl_path net_core_path[] = { - { .procname = "net", }, - { .procname = "core", }, - { }, -}; - static __net_init int sysctl_core_net_init(struct net *net) { struct ctl_table *tbl; diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 3cd8c586741a..ed1b36783192 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -237,13 +237,3 @@ static void ipv4_netfilter_fini(void) module_init(ipv4_netfilter_init); module_exit(ipv4_netfilter_fini); - -#ifdef CONFIG_SYSCTL -struct ctl_path nf_net_ipv4_netfilter_sysctl_path[] = { - { .procname = "net", }, - { .procname = "ipv4", }, - { .procname = "netfilter", }, - { } -}; -EXPORT_SYMBOL_GPL(nf_net_ipv4_netfilter_sysctl_path); -#endif /* CONFIG_SYSCTL */ diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 56e64f7b75d0..33417f84e07f 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -768,13 +768,6 @@ static struct ctl_table ipv4_net_table[] = { { } }; -struct ctl_path net_ipv4_ctl_path[] = { - { .procname = "net", }, - { .procname = "ipv4", }, - { }, -}; -EXPORT_SYMBOL_GPL(net_ipv4_ctl_path); - static __net_init int ipv4_sysctl_init_net(struct net *net) { struct ctl_table *table; diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index a52d8203a5de..e85c48bd404f 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -38,13 +38,6 @@ static ctl_table ipv6_rotable[] = { { } }; -struct ctl_path net_ipv6_ctl_path[] = { - { .procname = "net", }, - { .procname = "ipv6", }, - { }, -}; -EXPORT_SYMBOL_GPL(net_ipv6_ctl_path); - static int __net_init ipv6_sysctl_net_init(struct net *net) { struct ctl_table *ipv6_table; diff --git a/net/netfilter/core.c b/net/netfilter/core.c index e1b7e051332e..e19f3653db23 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -290,12 +290,3 @@ void __init netfilter_init(void) if (netfilter_log_init() < 0) panic("cannot initialize nf_log"); } - -#ifdef CONFIG_SYSCTL -struct ctl_path nf_net_netfilter_sysctl_path[] = { - { .procname = "net", }, - { .procname = "netfilter", }, - { } -}; -EXPORT_SYMBOL_GPL(nf_net_netfilter_sysctl_path); -#endif /* CONFIG_SYSCTL */ diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index a606d6b1b0e5..e3707d2bd9c7 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1846,13 +1846,6 @@ static struct ctl_table vs_vars[] = { { } }; -const struct ctl_path net_vs_ctl_path[] = { - { .procname = "net", }, - { .procname = "ipv4", }, - { .procname = "vs", }, - { } -}; -EXPORT_SYMBOL_GPL(net_vs_ctl_path); #endif #ifdef CONFIG_PROC_FS -- cgit v1.2.3 From ee9952831cfd0bbe834f4a26489d7dce74582e37 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 19 Apr 2012 03:40:39 +0000 Subject: tcp: Initial repair mode This includes (according the the previous description): * TCP_REPAIR sockoption This one just puts the socket in/out of the repair mode. Allowed for CAP_NET_ADMIN and for closed/establised sockets only. When repair mode is turned off and the socket happens to be in the established state the window probe is sent to the peer to 'unlock' the connection. * TCP_REPAIR_QUEUE sockoption This one sets the queue which we're about to repair. The 'no-queue' is set by default. * TCP_QUEUE_SEQ socoption Sets the write_seq/rcv_nxt of a selected repaired queue. Allowed for TCP_CLOSE-d sockets only. When the socket changes its state the other seq-s are changed by the kernel according to the protocol rules (most of the existing code is actually reused). * Ability to forcibly bind a socket to a port The sk->sk_reuse is set to SK_FORCE_REUSE. * Immediate connect modification The connect syscall initializes the connection, then directly jumps to the code which finalizes it. * Silent close modification The close just aborts the connection (similar to SO_LINGER with 0 time) but without sending any FIN/RST-s to peer. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/tcp.h | 14 ++++++++++- include/net/tcp.h | 2 ++ net/ipv4/tcp.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++- net/ipv4/tcp_ipv4.c | 19 +++++++++++--- net/ipv4/tcp_output.c | 16 +++++++++--- 5 files changed, 111 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index b6c62d294380..4e90e6ae79df 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -106,6 +106,16 @@ enum { #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 */ +#define TCP_REPAIR 19 /* TCP sock is under repair right now */ +#define TCP_REPAIR_QUEUE 20 +#define TCP_QUEUE_SEQ 21 + +enum { + TCP_NO_QUEUE, + TCP_RECV_QUEUE, + TCP_SEND_QUEUE, + TCP_QUEUES_NR, +}; /* for TCP_INFO socket option */ #define TCPI_OPT_TIMESTAMPS 1 @@ -353,7 +363,9 @@ struct tcp_sock { u8 nonagle : 4,/* Disable Nagle algorithm? */ thin_lto : 1,/* Use linear timeouts for thin streams */ thin_dupack : 1,/* Fast retransmit on first dupack */ - unused : 2; + repair : 1, + unused : 1; + u8 repair_queue; /* RTT measurement */ u32 srtt; /* smoothed round trip time << 3 */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 633fde27f24b..b4ccb8a6c9c1 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -612,6 +612,8 @@ static inline u32 tcp_receive_window(const struct tcp_sock *tp) */ extern u32 __tcp_select_window(struct sock *sk); +void tcp_send_window_probe(struct sock *sk); + /* TCP timestamps are only 32-bits, this causes a slight * complication on 64-bit systems since we store a snapshot * of jiffies in the buffer control blocks below. We decided diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index bb4200f56158..e38d6f240321 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1935,7 +1935,9 @@ void tcp_close(struct sock *sk, long timeout) * advertise a zero window, then kill -9 the FTP client, wheee... * Note: timeout is always zero in such a case. */ - if (data_was_unread) { + if (unlikely(tcp_sk(sk)->repair)) { + sk->sk_prot->disconnect(sk, 0); + } else if (data_was_unread) { /* Unread data was tossed, zap the connection. */ NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE); tcp_set_state(sk, TCP_CLOSE); @@ -2074,6 +2076,8 @@ int tcp_disconnect(struct sock *sk, int flags) /* ABORT function of RFC793 */ if (old_state == TCP_LISTEN) { inet_csk_listen_stop(sk); + } else if (unlikely(tp->repair)) { + sk->sk_err = ECONNABORTED; } else if (tcp_need_reset(old_state) || (tp->snd_nxt != tp->write_seq && (1 << old_state) & (TCPF_CLOSING | TCPF_LAST_ACK))) { @@ -2125,6 +2129,12 @@ int tcp_disconnect(struct sock *sk, int flags) } EXPORT_SYMBOL(tcp_disconnect); +static inline int tcp_can_repair_sock(struct sock *sk) +{ + return capable(CAP_NET_ADMIN) && + ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_ESTABLISHED)); +} + /* * Socket option code for TCP. */ @@ -2297,6 +2307,42 @@ static int do_tcp_setsockopt(struct sock *sk, int level, tp->thin_dupack = val; break; + case TCP_REPAIR: + if (!tcp_can_repair_sock(sk)) + err = -EPERM; + else if (val == 1) { + tp->repair = 1; + sk->sk_reuse = SK_FORCE_REUSE; + tp->repair_queue = TCP_NO_QUEUE; + } else if (val == 0) { + tp->repair = 0; + sk->sk_reuse = SK_NO_REUSE; + tcp_send_window_probe(sk); + } else + err = -EINVAL; + + break; + + case TCP_REPAIR_QUEUE: + if (!tp->repair) + err = -EPERM; + else if (val < TCP_QUEUES_NR) + tp->repair_queue = val; + else + err = -EINVAL; + break; + + case TCP_QUEUE_SEQ: + if (sk->sk_state != TCP_CLOSE) + err = -EPERM; + else if (tp->repair_queue == TCP_SEND_QUEUE) + tp->write_seq = val; + else if (tp->repair_queue == TCP_RECV_QUEUE) + tp->rcv_nxt = val; + else + err = -EINVAL; + break; + case TCP_CORK: /* When set indicates to always queue non-full frames. * Later the user clears this option and we transmit @@ -2632,6 +2678,26 @@ static int do_tcp_getsockopt(struct sock *sk, int level, val = tp->thin_dupack; break; + case TCP_REPAIR: + val = tp->repair; + break; + + case TCP_REPAIR_QUEUE: + if (tp->repair) + val = tp->repair_queue; + else + return -EINVAL; + break; + + case TCP_QUEUE_SEQ: + if (tp->repair_queue == TCP_SEND_QUEUE) + val = tp->write_seq; + else if (tp->repair_queue == TCP_RECV_QUEUE) + val = tp->rcv_nxt; + else + return -EINVAL; + break; + case TCP_USER_TIMEOUT: val = jiffies_to_msecs(icsk->icsk_user_timeout); break; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 0cb86ceb652f..ba6dad81908e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -138,6 +138,14 @@ int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp) } EXPORT_SYMBOL_GPL(tcp_twsk_unique); +static int tcp_repair_connect(struct sock *sk) +{ + tcp_connect_init(sk); + tcp_finish_connect(sk, NULL); + + return 0; +} + /* This will initiate an outgoing connection. */ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { @@ -196,7 +204,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) /* Reset inherited state */ tp->rx_opt.ts_recent = 0; tp->rx_opt.ts_recent_stamp = 0; - tp->write_seq = 0; + if (likely(!tp->repair)) + tp->write_seq = 0; } if (tcp_death_row.sysctl_tw_recycle && @@ -247,7 +256,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) sk->sk_gso_type = SKB_GSO_TCPV4; sk_setup_caps(sk, &rt->dst); - if (!tp->write_seq) + if (!tp->write_seq && likely(!tp->repair)) tp->write_seq = secure_tcp_sequence_number(inet->inet_saddr, inet->inet_daddr, inet->inet_sport, @@ -255,7 +264,11 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) inet->inet_id = tp->write_seq ^ jiffies; - err = tcp_connect(sk); + if (likely(!tp->repair)) + err = tcp_connect(sk); + else + err = tcp_repair_connect(sk); + rt = NULL; if (err) goto failure; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index db126a6954a2..fa442a61be6a 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2617,9 +2617,11 @@ void tcp_connect_init(struct sock *sk) tp->snd_sml = tp->write_seq; tp->snd_up = tp->write_seq; tp->snd_nxt = tp->write_seq; - tp->rcv_nxt = 0; - tp->rcv_wup = 0; - tp->copied_seq = 0; + + if (likely(!tp->repair)) + tp->rcv_nxt = 0; + tp->rcv_wup = tp->rcv_nxt; + tp->copied_seq = tp->rcv_nxt; inet_csk(sk)->icsk_rto = TCP_TIMEOUT_INIT; inet_csk(sk)->icsk_retransmits = 0; @@ -2790,6 +2792,14 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent) return tcp_transmit_skb(sk, skb, 0, GFP_ATOMIC); } +void tcp_send_window_probe(struct sock *sk) +{ + if (sk->sk_state == TCP_ESTABLISHED) { + tcp_sk(sk)->snd_wl1 = tcp_sk(sk)->rcv_nxt - 1; + tcp_xmit_probe_skb(sk, 0); + } +} + /* Initiate keepalive or window probe from timer. */ int tcp_write_wakeup(struct sock *sk) { -- cgit v1.2.3 From b139ba4e90dccbf4cd4efb112af96a5c9e0b098c Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 19 Apr 2012 03:41:57 +0000 Subject: tcp: Repair connection-time negotiated parameters There are options, which are set up on a socket while performing TCP handshake. Need to resurrect them on a socket while repairing. A new sockoption accepts a buffer and parses it. The buffer should be CODE:VALUE sequence of bytes, where CODE is standard option code and VALUE is the respective value. Only 4 options should be handled on repaired socket. To read 3 out of 4 of these options the TCP_INFO sockoption can be used. An ability to get the last one (the mss_clamp) was added by the previous patch. Now the restore. Three of these options -- timestamp_ok, mss_clamp and snd_wscale -- are just restored on a coket. The sack_ok flags has 2 issues. First, whether or not to do sacks at all. This flag is just read and set back. No other sack info is saved or restored, since according to the standart and the code dropping all sack-ed segments is OK, the sender will resubmit them again, so after the repair we will probably experience a pause in connection. Next, the fack bit. It's just set back on a socket if the respective sysctl is set. No collected stats about packets flow is preserved. As far as I see (plz, correct me if I'm wrong) the fack-based congestion algorithm survives dropping all of the stats and repairs itself eventually, probably losing the performance for that period. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 + net/ipv4/tcp.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4e90e6ae79df..986593685566 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -109,6 +109,7 @@ enum { #define TCP_REPAIR 19 /* TCP sock is under repair right now */ #define TCP_REPAIR_QUEUE 20 #define TCP_QUEUE_SEQ 21 +#define TCP_REPAIR_OPTIONS 22 enum { TCP_NO_QUEUE, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index b4e690ddb08c..3ce3bd031f33 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2218,6 +2218,68 @@ static inline int tcp_can_repair_sock(struct sock *sk) ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_ESTABLISHED)); } +static int tcp_repair_options_est(struct tcp_sock *tp, char __user *optbuf, unsigned int len) +{ + /* + * Options are stored in CODE:VALUE form where CODE is 8bit and VALUE + * fits the respective TCPOLEN_ size + */ + + while (len > 0) { + u8 opcode; + + if (get_user(opcode, optbuf)) + return -EFAULT; + + optbuf++; + len--; + + switch (opcode) { + case TCPOPT_MSS: { + u16 in_mss; + + if (len < sizeof(in_mss)) + return -ENODATA; + if (get_user(in_mss, optbuf)) + return -EFAULT; + + tp->rx_opt.mss_clamp = in_mss; + + optbuf += sizeof(in_mss); + len -= sizeof(in_mss); + break; + } + case TCPOPT_WINDOW: { + u8 wscale; + + if (len < sizeof(wscale)) + return -ENODATA; + if (get_user(wscale, optbuf)) + return -EFAULT; + + if (wscale > 14) + return -EFBIG; + + tp->rx_opt.snd_wscale = wscale; + + optbuf += sizeof(wscale); + len -= sizeof(wscale); + break; + } + case TCPOPT_SACK_PERM: + tp->rx_opt.sack_ok |= TCP_SACK_SEEN; + if (sysctl_tcp_fack) + tcp_enable_fack(tp); + break; + case TCPOPT_TIMESTAMP: + tp->rx_opt.tstamp_ok = 1; + break; + } + } + + return 0; +} + /* * Socket option code for TCP. */ @@ -2426,6 +2488,15 @@ static int do_tcp_setsockopt(struct sock *sk, int level, err = -EINVAL; break; + case TCP_REPAIR_OPTIONS: + if (!tp->repair) + err = -EINVAL; + else if (sk->sk_state == TCP_ESTABLISHED) + err = tcp_repair_options_est(tp, optval, optlen); + else + err = -EPERM; + break; + case TCP_CORK: /* When set indicates to always queue non-full frames. * Later the user clears this option and we transmit -- cgit v1.2.3 From 19a0b58e506b06fd41659d8734bba6a3e87980f4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 20 Apr 2012 04:42:05 +0000 Subject: team: allow to enable/disable ports This patch changes content of hashlist (used to get port struct by computed index (0...en_port_count-1)). Now the hash list contains only enabled ports so userspace will be able to say what ports can be used for tx/rx. This becomes handy when userspace will need to disable ports which does not belong to active aggregator. By default, newly added port is enabled. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 51 ++++++++++++++++++++++---------- drivers/net/team/team_mode_loadbalance.c | 2 +- drivers/net/team/team_mode_roundrobin.c | 2 +- include/linux/if_team.h | 15 +++++----- 4 files changed, 45 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 153a62d03c9f..fe7ca40284fa 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -559,6 +559,8 @@ static int team_change_mode(struct team *team, const char *kind) * Rx path frame handler ************************/ +static bool team_port_enabled(struct team_port *port); + /* note: already called with rcu_read_lock */ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) { @@ -575,8 +577,12 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) port = team_port_get_rcu(skb->dev); team = port->team; - - res = team->ops.receive(team, port, skb); + if (!team_port_enabled(port)) { + /* allow exact match delivery for disabled ports */ + res = RX_HANDLER_EXACT; + } else { + res = team->ops.receive(team, port, skb); + } if (res == RX_HANDLER_ANOTHER) { struct team_pcpu_stats *pcpu_stats; @@ -612,17 +618,25 @@ static bool team_port_find(const struct team *team, return false; } +static bool team_port_enabled(struct team_port *port) +{ + return port->index != -1; +} + /* - * Add/delete port to the team port list. Write guarded by rtnl_lock. - * Takes care of correct port->index setup (might be racy). + * Enable/disable port by adding to enabled port hashlist and setting + * port->index (Might be racy so reader could see incorrect ifindex when + * processing a flying packet, but that is not a problem). Write guarded + * by team->lock. */ -static void team_port_list_add_port(struct team *team, - struct team_port *port) +static void team_port_enable(struct team *team, + struct team_port *port) { - port->index = team->port_count++; + if (team_port_enabled(port)) + return; + port->index = team->en_port_count++; hlist_add_head_rcu(&port->hlist, team_port_index_hash(team, port->index)); - list_add_tail_rcu(&port->list, &team->port_list); } static void __reconstruct_port_hlist(struct team *team, int rm_index) @@ -630,7 +644,7 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index) int i; struct team_port *port; - for (i = rm_index + 1; i < team->port_count; i++) { + for (i = rm_index + 1; i < team->en_port_count; i++) { port = team_get_port_by_index(team, i); hlist_del_rcu(&port->hlist); port->index--; @@ -639,15 +653,17 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index) } } -static void team_port_list_del_port(struct team *team, - struct team_port *port) +static void team_port_disable(struct team *team, + struct team_port *port) { int rm_index = port->index; + if (!team_port_enabled(port)) + return; hlist_del_rcu(&port->hlist); - list_del_rcu(&port->list); __reconstruct_port_hlist(team, rm_index); - team->port_count--; + team->en_port_count--; + port->index = -1; } #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ @@ -800,7 +816,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev) goto err_option_port_add; } - team_port_list_add_port(team, port); + port->index = -1; + team_port_enable(team, port); + list_add_tail_rcu(&port->list, &team->port_list); team_adjust_ops(team); __team_compute_features(team); __team_port_change_check(port, !!netif_carrier_ok(port_dev)); @@ -849,7 +867,8 @@ static int team_port_del(struct team *team, struct net_device *port_dev) port->removed = true; __team_port_change_check(port, false); - team_port_list_del_port(team, port); + team_port_disable(team, port); + list_del_rcu(&port->list); team_adjust_ops(team); team_option_port_del(team, port); netdev_rx_handler_unregister(port_dev); @@ -956,7 +975,7 @@ static int team_init(struct net_device *dev) return -ENOMEM; for (i = 0; i < TEAM_PORT_HASHENTRIES; i++) - INIT_HLIST_HEAD(&team->port_hlist[i]); + INIT_HLIST_HEAD(&team->en_port_hlist[i]); INIT_LIST_HEAD(&team->port_list); team_adjust_ops(team); diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index 438d5b871630..86e8183c8e3d 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -38,7 +38,7 @@ static bool lb_transmit(struct team *team, struct sk_buff *skb) if (unlikely(!fp)) goto drop; hash = SK_RUN_FILTER(fp, skb); - port_index = hash % team->port_count; + port_index = hash % team->en_port_count; port = team_get_port_by_index_rcu(team, port_index); if (unlikely(!port)) goto drop; diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index a0e8f806331a..6abfbdc96be5 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -50,7 +50,7 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb) struct team_port *port; int port_index; - port_index = rr_priv(team)->sent_packets++ % team->port_count; + 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); if (unlikely(!port)) diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 5fd5ab171165..8185f57a9c7f 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -28,10 +28,10 @@ struct team; struct team_port { struct net_device *dev; - struct hlist_node hlist; /* node in hash list */ + struct hlist_node hlist; /* node in enabled ports hash list */ struct list_head list; /* node in ordinary list */ struct team *team; - int index; + int index; /* index of enabled port. If disabled, it's set to -1 */ bool linkup; /* either state.linkup or user.linkup */ @@ -125,11 +125,12 @@ struct team { struct mutex lock; /* used for overall locking, e.g. port lists write */ /* - * port lists with port count + * List of enabled ports and their count */ - int port_count; - struct hlist_head port_hlist[TEAM_PORT_HASHENTRIES]; - struct list_head port_list; + int en_port_count; + struct hlist_head en_port_hlist[TEAM_PORT_HASHENTRIES]; + + struct list_head port_list; /* list of all ports */ struct list_head option_list; struct list_head option_inst_list; /* list of option instances */ @@ -142,7 +143,7 @@ struct team { static inline struct hlist_head *team_port_index_hash(struct team *team, int port_index) { - return &team->port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)]; + return &team->en_port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)]; } static inline struct team_port *team_get_port_by_index(struct team *team, -- cgit v1.2.3 From 1451ba3a5fa52d874e03a3380d053f3e6a5fcae4 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Sun, 22 Apr 2012 11:59:47 +0200 Subject: i2c-pnx.c: Use resources in platforms As a precondition for device tree conversion, the platforms using i2c-pnx.c are converted to using mem and irq resources instead of platform data. Signed-off-by: Roland Stigge Reviewed-by: Arnd Bergmann Signed-off-by: Wolfram Sang --- arch/arm/mach-lpc32xx/common.c | 67 ++++++++++++++++++++++++++---------------- arch/arm/mach-pnx4008/i2c.c | 64 ++++++++++++++++++++++++---------------- drivers/i2c/busses/i2c-pnx.c | 47 +++++++++++++++-------------- include/linux/i2c-pnx.h | 9 ++---- 4 files changed, 108 insertions(+), 79 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-lpc32xx/common.c b/arch/arm/mach-lpc32xx/common.c index bbbf063a74c2..6f255600fc97 100644 --- a/arch/arm/mach-lpc32xx/common.c +++ b/arch/arm/mach-lpc32xx/common.c @@ -27,7 +27,6 @@ #include -#include #include #include #include "common.h" @@ -53,46 +52,64 @@ struct platform_device lpc32xx_watchdog_device = { /* * I2C busses */ -static struct i2c_pnx_data i2c0_data = { - .name = I2C_CHIP_NAME "1", - .base = LPC32XX_I2C1_BASE, - .irq = IRQ_LPC32XX_I2C_1, +static struct resource i2c0_resources[] = { + [0] = { + .start = LPC32XX_I2C1_BASE, + .end = LPC32XX_I2C1_BASE + 0x100 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_LPC32XX_I2C_1, + .end = IRQ_LPC32XX_I2C_1, + .flags = IORESOURCE_IRQ, + }, }; -static struct i2c_pnx_data i2c1_data = { - .name = I2C_CHIP_NAME "2", - .base = LPC32XX_I2C2_BASE, - .irq = IRQ_LPC32XX_I2C_2, +static struct resource i2c1_resources[] = { + [0] = { + .start = LPC32XX_I2C2_BASE, + .end = LPC32XX_I2C2_BASE + 0x100 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_LPC32XX_I2C_2, + .end = IRQ_LPC32XX_I2C_2, + .flags = IORESOURCE_IRQ, + }, }; -static struct i2c_pnx_data i2c2_data = { - .name = "USB-I2C", - .base = LPC32XX_OTG_I2C_BASE, - .irq = IRQ_LPC32XX_USB_I2C, +static struct resource i2c2_resources[] = { + [0] = { + .start = LPC32XX_OTG_I2C_BASE, + .end = LPC32XX_OTG_I2C_BASE + 0x100 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_LPC32XX_USB_I2C, + .end = IRQ_LPC32XX_USB_I2C, + .flags = IORESOURCE_IRQ, + }, }; struct platform_device lpc32xx_i2c0_device = { - .name = "pnx-i2c", + .name = "pnx-i2c.0", .id = 0, - .dev = { - .platform_data = &i2c0_data, - }, + .num_resources = ARRAY_SIZE(i2c0_resources), + .resource = i2c0_resources, }; struct platform_device lpc32xx_i2c1_device = { - .name = "pnx-i2c", + .name = "pnx-i2c.1", .id = 1, - .dev = { - .platform_data = &i2c1_data, - }, + .num_resources = ARRAY_SIZE(i2c1_resources), + .resource = i2c1_resources, }; struct platform_device lpc32xx_i2c2_device = { - .name = "pnx-i2c", + .name = "pnx-i2c.2", .id = 2, - .dev = { - .platform_data = &i2c2_data, - }, + .num_resources = ARRAY_SIZE(i2c2_resources), + .resource = i2c2_resources, }; /* TSC (Touch Screen Controller) */ diff --git a/arch/arm/mach-pnx4008/i2c.c b/arch/arm/mach-pnx4008/i2c.c index 8103f9644e2d..550cfc2a1f2e 100644 --- a/arch/arm/mach-pnx4008/i2c.c +++ b/arch/arm/mach-pnx4008/i2c.c @@ -16,48 +16,62 @@ #include #include #include -#include -static struct i2c_pnx_data i2c0_data = { - .name = I2C_CHIP_NAME "0", - .base = PNX4008_I2C1_BASE, - .irq = I2C_1_INT, +static struct resource i2c0_resources[] = { + { + .start = PNX4008_I2C1_BASE, + .end = PNX4008_I2C1_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, { + .start = I2C_1_INT, + .end = I2C_1_INT, + .flags = IORESOURCE_IRQ, + }, }; -static struct i2c_pnx_data i2c1_data = { - .name = I2C_CHIP_NAME "1", - .base = PNX4008_I2C2_BASE, - .irq = I2C_2_INT, +static struct resource i2c1_resources[] = { + { + .start = PNX4008_I2C2_BASE, + .end = PNX4008_I2C2_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, { + .start = I2C_2_INT, + .end = I2C_2_INT, + .flags = IORESOURCE_IRQ, + }, }; -static struct i2c_pnx_data i2c2_data = { - .name = "USB-I2C", - .base = (PNX4008_USB_CONFIG_BASE + 0x300), - .irq = USB_I2C_INT, +static struct resource i2c2_resources[] = { + { + .start = PNX4008_USB_CONFIG_BASE + 0x300, + .end = PNX4008_USB_CONFIG_BASE + 0x300 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, { + .start = USB_I2C_INT, + .end = USB_I2C_INT, + .flags = IORESOURCE_IRQ, + }, }; static struct platform_device i2c0_device = { - .name = "pnx-i2c", + .name = "pnx-i2c.0", .id = 0, - .dev = { - .platform_data = &i2c0_data, - }, + .resource = i2c0_resources, + .num_resources = ARRAY_SIZE(i2c0_resources), }; static struct platform_device i2c1_device = { - .name = "pnx-i2c", + .name = "pnx-i2c.1", .id = 1, - .dev = { - .platform_data = &i2c1_data, - }, + .resource = i2c1_resources, + .num_resources = ARRAY_SIZE(i2c1_resources), }; static struct platform_device i2c2_device = { - .name = "pnx-i2c", + .name = "pnx-i2c.2", .id = 2, - .dev = { - .platform_data = &i2c2_data, - }, + .resource = i2c2_resources, + .num_resources = ARRAY_SIZE(i2c2_resources), }; static struct platform_device *devices[] __initdata = { diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index eb8ad538c79f..6fb97aef0465 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -568,14 +568,7 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) int ret = 0; struct i2c_pnx_algo_data *alg_data; unsigned long freq; - struct i2c_pnx_data *i2c_pnx = pdev->dev.platform_data; - - if (!i2c_pnx || !i2c_pnx->name) { - dev_err(&pdev->dev, "%s: no platform data supplied\n", - __func__); - ret = -EINVAL; - goto out; - } + struct resource *res; alg_data = kzalloc(sizeof(*alg_data), GFP_KERNEL); if (!alg_data) { @@ -585,13 +578,10 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, alg_data); - strlcpy(alg_data->adapter.name, i2c_pnx->name, - sizeof(alg_data->adapter.name)); alg_data->adapter.dev.parent = &pdev->dev; alg_data->adapter.algo = &pnx_algorithm; alg_data->adapter.algo_data = alg_data; alg_data->adapter.nr = pdev->id; - alg_data->i2c_pnx = i2c_pnx; alg_data->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(alg_data->clk)) { @@ -603,17 +593,27 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) alg_data->mif.timer.function = i2c_pnx_timeout; alg_data->mif.timer.data = (unsigned long)alg_data; + snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name), + "%s", pdev->name); + /* Register I/O resource */ - if (!request_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE, + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get mem resource.\n"); + ret = -EBUSY; + goto out_clkget; + } + if (!request_mem_region(res->start, I2C_PNX_REGION_SIZE, pdev->name)) { dev_err(&pdev->dev, "I/O region 0x%08x for I2C already in use.\n", - i2c_pnx->base); + res->start); ret = -ENODEV; goto out_clkget; } - alg_data->ioaddr = ioremap(i2c_pnx->base, I2C_PNX_REGION_SIZE); + alg_data->base = res->start; + alg_data->ioaddr = ioremap(res->start, I2C_PNX_REGION_SIZE); if (!alg_data->ioaddr) { dev_err(&pdev->dev, "Couldn't ioremap I2C I/O region\n"); ret = -ENOMEM; @@ -650,7 +650,12 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) } init_completion(&alg_data->mif.complete); - ret = request_irq(i2c_pnx->irq, i2c_pnx_interrupt, + alg_data->irq = platform_get_irq(pdev, 0); + if (alg_data->irq < 0) { + dev_err(&pdev->dev, "Failed to get IRQ from platform resource\n"); + goto out_irq; + } + ret = request_irq(alg_data->irq, i2c_pnx_interrupt, 0, pdev->name, alg_data); if (ret) goto out_clock; @@ -663,38 +668,36 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) } dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n", - alg_data->adapter.name, i2c_pnx->base, i2c_pnx->irq); + alg_data->adapter.name, res->start, alg_data->irq); return 0; out_irq: - free_irq(i2c_pnx->irq, alg_data); + free_irq(alg_data->irq, alg_data); out_clock: clk_disable(alg_data->clk); out_unmap: iounmap(alg_data->ioaddr); out_release: - release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE); + release_mem_region(res->start, I2C_PNX_REGION_SIZE); out_clkget: clk_put(alg_data->clk); out_drvdata: kfree(alg_data); err_kzalloc: platform_set_drvdata(pdev, NULL); -out: return ret; } static int __devexit i2c_pnx_remove(struct platform_device *pdev) { struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); - struct i2c_pnx_data *i2c_pnx = alg_data->i2c_pnx; - free_irq(i2c_pnx->irq, alg_data); + free_irq(alg_data->irq, alg_data); i2c_del_adapter(&alg_data->adapter); clk_disable(alg_data->clk); iounmap(alg_data->ioaddr); - release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE); + release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE); clk_put(alg_data->clk); kfree(alg_data); platform_set_drvdata(pdev, NULL); diff --git a/include/linux/i2c-pnx.h b/include/linux/i2c-pnx.h index a87124d4d533..6e8efb7afd7c 100644 --- a/include/linux/i2c-pnx.h +++ b/include/linux/i2c-pnx.h @@ -29,14 +29,9 @@ struct i2c_pnx_algo_data { struct i2c_pnx_mif mif; int last; struct clk *clk; - struct i2c_pnx_data *i2c_pnx; struct i2c_adapter adapter; -}; - -struct i2c_pnx_data { - const char *name; - u32 base; - int irq; + phys_addr_t base; + int irq; }; #endif /* __I2C_PNX_H__ */ -- cgit v1.2.3 From b41a216dafe4dd23c95cb4203de288f773a097a6 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Sun, 22 Apr 2012 11:59:47 +0200 Subject: i2c: Add device tree support to i2c-pnx.c This patch adds device tree support to the pnx-i2c driver by using platform resources for memory region and irq and removing dependency on mach includes. The following platforms are affected: * PNX * LPC31xx (WIP) * LPC32xx The patch is based on a patch by Jon Smirl, working on lpc31xx integration Signed-off-by: Roland Stigge Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/pnx.txt | 36 +++++++++++++++ drivers/i2c/busses/i2c-pnx.c | 63 ++++++++++++++++++++------- include/linux/i2c-pnx.h | 1 + 3 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 Documentation/devicetree/bindings/i2c/pnx.txt (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/i2c/pnx.txt b/Documentation/devicetree/bindings/i2c/pnx.txt new file mode 100644 index 000000000000..fe98ada33ee4 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/pnx.txt @@ -0,0 +1,36 @@ +* NXP PNX I2C Controller + +Required properties: + + - reg: Offset and length of the register set for the device + - compatible: should be "nxp,pnx-i2c" + - interrupts: configure one interrupt line + - #address-cells: always 1 (for i2c addresses) + - #size-cells: always 0 + - interrupt-parent: the phandle for the interrupt controller that + services interrupts for this device. + +Optional properties: + + - clock-frequency: desired I2C bus clock frequency in Hz, Default: 100000 Hz + +Examples: + + i2c1: i2c@400a0000 { + compatible = "nxp,pnx-i2c"; + reg = <0x400a0000 0x100>; + interrupt-parent = <&mic>; + interrupts = <51 0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c2: i2c@400a8000 { + compatible = "nxp,pnx-i2c"; + reg = <0x400a8000 0x100>; + interrupt-parent = <&mic>; + interrupts = <50 0>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000>; + }; diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index f69d80b8d736..99389d2eae51 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -23,10 +23,11 @@ #include #include #include +#include -#define I2C_PNX_TIMEOUT 10 /* msec */ -#define I2C_PNX_SPEED_KHZ 100 -#define I2C_PNX_REGION_SIZE 0x100 +#define I2C_PNX_TIMEOUT_DEFAULT 10 /* msec */ +#define I2C_PNX_SPEED_KHZ_DEFAULT 100 +#define I2C_PNX_REGION_SIZE 0x100 enum { mstatus_tdi = 0x00000001, @@ -74,8 +75,9 @@ enum { #define I2C_REG_TXS(a) ((a)->ioaddr + 0x28) /* Tx slave FIFO (RO) */ #define I2C_REG_STFL(a) ((a)->ioaddr + 0x2c) /* Tx slave FIFO level (RO) */ -static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data) +static inline int wait_timeout(struct i2c_pnx_algo_data *data) { + long timeout = data->timeout; while (timeout > 0 && (ioread32(I2C_REG_STS(data)) & mstatus_active)) { mdelay(1); @@ -84,8 +86,9 @@ static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data) return (timeout <= 0); } -static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data) +static inline int wait_reset(struct i2c_pnx_algo_data *data) { + long timeout = data->timeout; while (timeout > 0 && (ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) { mdelay(1); @@ -97,7 +100,7 @@ static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data) static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data) { struct timer_list *timer = &alg_data->mif.timer; - unsigned long expires = msecs_to_jiffies(I2C_PNX_TIMEOUT); + unsigned long expires = msecs_to_jiffies(alg_data->timeout); if (expires <= 1) expires = 2; @@ -135,7 +138,7 @@ static int i2c_pnx_start(unsigned char slave_addr, } /* First, make sure bus is idle */ - if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) { + if (wait_timeout(alg_data)) { /* Somebody else is monopolizing the bus */ dev_err(&alg_data->adapter.dev, "%s: Bus busy. Slave addr = %02x, cntrl = %x, stat = %x\n", @@ -228,7 +231,7 @@ static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data) if (alg_data->mif.len == 0) { if (alg_data->last) { /* Wait until the STOP is seen. */ - if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) + if (wait_timeout(alg_data)) dev_err(&alg_data->adapter.dev, "The bus is still active after timeout\n"); } @@ -326,7 +329,7 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data) if (alg_data->mif.len == 0) { if (alg_data->last) /* Wait until the STOP is seen. */ - if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) + if (wait_timeout(alg_data)) dev_err(&alg_data->adapter.dev, "The bus is still active after timeout\n"); @@ -442,7 +445,7 @@ static void i2c_pnx_timeout(unsigned long data) ctl |= mcntrl_reset; iowrite32(ctl, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); alg_data->mif.ret = -EIO; complete(&alg_data->mif.complete); } @@ -457,18 +460,18 @@ static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data) alg_data->adapter.name); iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); } else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) { /* If there is data in the fifo's after transfer, * flush fifo's by reset. */ iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); } else if (stat & mstatus_nai) { iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset, I2C_REG_CTL(alg_data)); - wait_reset(I2C_PNX_TIMEOUT, alg_data); + wait_reset(alg_data); } } @@ -612,6 +615,7 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) struct i2c_pnx_algo_data *alg_data; unsigned long freq; struct resource *res; + u32 speed = I2C_PNX_SPEED_KHZ_DEFAULT * 1000; alg_data = kzalloc(sizeof(*alg_data), GFP_KERNEL); if (!alg_data) { @@ -626,6 +630,22 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) alg_data->adapter.algo_data = alg_data; alg_data->adapter.nr = pdev->id; + alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT; +#ifdef CONFIG_OF + alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node); + if (pdev->dev.of_node) { + of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &speed); + /* + * At this point, it is planned to add an OF timeout property. + * As soon as there is a consensus about how to call and handle + * this, sth. like the following can be put here: + * + * of_property_read_u32(pdev->dev.of_node, "timeout", + * &alg_data->timeout); + */ + } +#endif alg_data->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(alg_data->clk)) { ret = PTR_ERR(alg_data->clk); @@ -651,7 +671,7 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) dev_err(&pdev->dev, "I/O region 0x%08x for I2C already in use.\n", res->start); - ret = -ENODEV; + ret = -ENOMEM; goto out_clkget; } @@ -680,14 +700,14 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) * the deglitching filter length. */ - tmp = ((freq / 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2; + tmp = (freq / speed) / 2 - 2; if (tmp > 0x3FF) tmp = 0x3FF; iowrite32(tmp, I2C_REG_CKH(alg_data)); iowrite32(tmp, I2C_REG_CKL(alg_data)); iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data)); - if (wait_reset(I2C_PNX_TIMEOUT, alg_data)) { + if (wait_reset(alg_data)) { ret = -ENODEV; goto out_clock; } @@ -710,6 +730,8 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) goto out_irq; } + of_i2c_register_devices(&alg_data->adapter); + dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n", alg_data->adapter.name, res->start, alg_data->irq); @@ -748,10 +770,19 @@ static int __devexit i2c_pnx_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id i2c_pnx_of_match[] = { + { .compatible = "nxp,pnx-i2c" }, + { }, +}; +MODULE_DEVICE_TABLE(of, i2c_pnx_of_match); +#endif + static struct platform_driver i2c_pnx_driver = { .driver = { .name = "pnx-i2c", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(i2c_pnx_of_match), }, .probe = i2c_pnx_probe, .remove = __devexit_p(i2c_pnx_remove), diff --git a/include/linux/i2c-pnx.h b/include/linux/i2c-pnx.h index 6e8efb7afd7c..1bc74afe7a35 100644 --- a/include/linux/i2c-pnx.h +++ b/include/linux/i2c-pnx.h @@ -32,6 +32,7 @@ struct i2c_pnx_algo_data { struct i2c_adapter adapter; phys_addr_t base; int irq; + u32 timeout; }; #endif /* __I2C_PNX_H__ */ -- cgit v1.2.3 From bbbc4c4d8c5face097d695f9bf3a39647ba6b7e7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 16 Apr 2012 19:16:54 -0400 Subject: mmc: sdio: avoid spurious calls to interrupt handlers Commit 06e8935feb ("optimized SDIO IRQ handling for single irq") introduced some spurious calls to SDIO function interrupt handlers, such as when the SDIO IRQ thread is started, or the safety check performed upon a system resume. Let's add a flag to perform the optimization only when a real interrupt is signaled by the host driver and we know there is no point confirming it. Reported-by: Sujit Reddy Thumma Signed-off-by: Nicolas Pitre Cc: stable Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 2 +- drivers/mmc/core/sdio_irq.c | 11 +++++++---- include/linux/mmc/host.h | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 2c7c83f832d2..13d0e95380ab 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -947,7 +947,7 @@ static int mmc_sdio_resume(struct mmc_host *host) } if (!err && host->sdio_irqs) - mmc_signal_sdio_irq(host); + wake_up_process(host->sdio_irq_thread); mmc_release_host(host); /* diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index f573e7f9f740..3d8ceb4084de 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -28,18 +28,20 @@ #include "sdio_ops.h" -static int process_sdio_pending_irqs(struct mmc_card *card) +static int process_sdio_pending_irqs(struct mmc_host *host) { + struct mmc_card *card = host->card; int i, ret, count; unsigned char pending; struct sdio_func *func; /* * Optimization, if there is only 1 function interrupt registered - * call irq handler directly + * and we know an IRQ was signaled then call irq handler directly. + * Otherwise do the full probe. */ func = card->sdio_single_irq; - if (func) { + if (func && host->sdio_irq_pending) { func->irq_handler(func); return 1; } @@ -116,7 +118,8 @@ static int sdio_irq_thread(void *_host) ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort); if (ret) break; - ret = process_sdio_pending_irqs(host->card); + ret = process_sdio_pending_irqs(host); + host->sdio_irq_pending = false; mmc_release_host(host); /* diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index cbde4b7e675e..0707d228d7f1 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -297,6 +297,7 @@ struct mmc_host { unsigned int sdio_irqs; struct task_struct *sdio_irq_thread; + bool sdio_irq_pending; atomic_t sdio_irq_thread_abort; mmc_pm_flag_t pm_flags; /* requested pm features */ @@ -352,6 +353,7 @@ extern int mmc_cache_ctrl(struct mmc_host *, u8); static inline void mmc_signal_sdio_irq(struct mmc_host *host) { host->ops->enable_sdio_irq(host, 0); + host->sdio_irq_pending = true; wake_up_process(host->sdio_irq_thread); } -- cgit v1.2.3 From 9923777dff4543050fdf938cf6b19f6d4376b7c5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 14 Apr 2012 18:03:10 +0200 Subject: mm: fixup compilation error due to an asm write through a const pointer This regression has been introduced in commit f56f821feb7b36223f309e0ec05986bb137ce418 Author: Daniel Vetter Date: Sun Mar 25 19:47:41 2012 +0200 mm: extend prefault helpers to fault in more than PAGE_SIZE I have failed to notice this because x86 asm seems to happily compile things as-is. Reported-by: Geert Uytterhoeven Signed-off-by: Dave Airlie --- include/linux/pagemap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index c93a9a9bcd35..efa26b4da8d2 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -461,7 +461,7 @@ static inline int fault_in_pages_readable(const char __user *uaddr, int size) static inline int fault_in_multipages_writeable(char __user *uaddr, int size) { int ret; - const char __user *end = uaddr + size - 1; + char __user *end = uaddr + size - 1; if (unlikely(size == 0)) return 0; -- cgit v1.2.3 From 07d777fe8c3985bc83428c2866713c2d1b3d4129 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 22 Sep 2011 14:01:55 -0400 Subject: tracing: Add percpu buffers for trace_printk() Currently, trace_printk() uses a single buffer to write into to calculate the size and format needed to save the trace. To do this safely in an SMP environment, a spin_lock() is taken to only allow one writer at a time to the buffer. But this could also affect what is being traced, and add synchronization that would not be there otherwise. Ideally, using percpu buffers would be useful, but since trace_printk() is only used in development, having per cpu buffers for something never used is a waste of space. Thus, the use of the trace_bprintk() format section is changed to be used for static fmts as well as dynamic ones. Then at boot up, we can check if the section that holds the trace_printk formats is non-empty, and if it does contain something, then we know a trace_printk() has been added to the kernel. At this time the trace_printk per cpu buffers are allocated. A check is also done at module load time in case a module is added that contains a trace_printk(). Once the buffers are allocated, they are never freed. If you use a trace_printk() then you should know what you are doing. A buffer is made for each type of context: normal softirq irq nmi The context is checked and the appropriate buffer is used. This allows for totally lockless usage of trace_printk(), and they no longer even disable interrupts. Requested-by: Peter Zijlstra Signed-off-by: Steven Rostedt --- include/linux/kernel.h | 13 ++-- kernel/trace/trace.c | 184 ++++++++++++++++++++++++++++++++------------ kernel/trace/trace.h | 2 + kernel/trace/trace_printk.c | 4 + 4 files changed, 148 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 645231c373c8..c0d34420a913 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -480,15 +480,16 @@ do { \ #define trace_printk(fmt, args...) \ do { \ + static const char *trace_printk_fmt \ + __attribute__((section("__trace_printk_fmt"))) = \ + __builtin_constant_p(fmt) ? fmt : NULL; \ + \ __trace_printk_check_format(fmt, ##args); \ - if (__builtin_constant_p(fmt)) { \ - static const char *trace_printk_fmt \ - __attribute__((section("__trace_printk_fmt"))) = \ - __builtin_constant_p(fmt) ? fmt : NULL; \ \ + if (__builtin_constant_p(fmt)) \ __trace_bprintk(_THIS_IP_, trace_printk_fmt, ##args); \ - } else \ - __trace_printk(_THIS_IP_, fmt, ##args); \ + else \ + __trace_printk(_THIS_IP_, fmt, ##args); \ } while (0) extern __printf(2, 3) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index ed7b5d1e12f4..1ab8e35d069b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1498,25 +1498,119 @@ static void __trace_userstack(struct trace_array *tr, unsigned long flags) #endif /* CONFIG_STACKTRACE */ +/* created for use with alloc_percpu */ +struct trace_buffer_struct { + char buffer[TRACE_BUF_SIZE]; +}; + +static struct trace_buffer_struct *trace_percpu_buffer; +static struct trace_buffer_struct *trace_percpu_sirq_buffer; +static struct trace_buffer_struct *trace_percpu_irq_buffer; +static struct trace_buffer_struct *trace_percpu_nmi_buffer; + +/* + * The buffer used is dependent on the context. There is a per cpu + * buffer for normal context, softirq contex, hard irq context and + * for NMI context. Thise allows for lockless recording. + * + * Note, if the buffers failed to be allocated, then this returns NULL + */ +static char *get_trace_buf(void) +{ + struct trace_buffer_struct *percpu_buffer; + struct trace_buffer_struct *buffer; + + /* + * If we have allocated per cpu buffers, then we do not + * need to do any locking. + */ + if (in_nmi()) + percpu_buffer = trace_percpu_nmi_buffer; + else if (in_irq()) + percpu_buffer = trace_percpu_irq_buffer; + else if (in_softirq()) + percpu_buffer = trace_percpu_sirq_buffer; + else + percpu_buffer = trace_percpu_buffer; + + if (!percpu_buffer) + return NULL; + + buffer = per_cpu_ptr(percpu_buffer, smp_processor_id()); + + return buffer->buffer; +} + +static int alloc_percpu_trace_buffer(void) +{ + struct trace_buffer_struct *buffers; + struct trace_buffer_struct *sirq_buffers; + struct trace_buffer_struct *irq_buffers; + struct trace_buffer_struct *nmi_buffers; + + buffers = alloc_percpu(struct trace_buffer_struct); + if (!buffers) + goto err_warn; + + sirq_buffers = alloc_percpu(struct trace_buffer_struct); + if (!sirq_buffers) + goto err_sirq; + + irq_buffers = alloc_percpu(struct trace_buffer_struct); + if (!irq_buffers) + goto err_irq; + + nmi_buffers = alloc_percpu(struct trace_buffer_struct); + if (!nmi_buffers) + goto err_nmi; + + trace_percpu_buffer = buffers; + trace_percpu_sirq_buffer = sirq_buffers; + trace_percpu_irq_buffer = irq_buffers; + trace_percpu_nmi_buffer = nmi_buffers; + + return 0; + + err_nmi: + free_percpu(irq_buffers); + err_irq: + free_percpu(sirq_buffers); + err_sirq: + free_percpu(buffers); + err_warn: + WARN(1, "Could not allocate percpu trace_printk buffer"); + return -ENOMEM; +} + +void trace_printk_init_buffers(void) +{ + static int buffers_allocated; + + if (buffers_allocated) + return; + + if (alloc_percpu_trace_buffer()) + return; + + pr_info("ftrace: Allocated trace_printk buffers\n"); + + buffers_allocated = 1; +} + /** * trace_vbprintk - write binary msg to tracing buffer * */ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args) { - static arch_spinlock_t trace_buf_lock = - (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; - static u32 trace_buf[TRACE_BUF_SIZE]; - struct ftrace_event_call *call = &event_bprint; struct ring_buffer_event *event; struct ring_buffer *buffer; struct trace_array *tr = &global_trace; - struct trace_array_cpu *data; struct bprint_entry *entry; unsigned long flags; - int disable; - int cpu, len = 0, size, pc; + char *tbuffer; + int len = 0, size, pc; if (unlikely(tracing_selftest_running || tracing_disabled)) return 0; @@ -1526,43 +1620,36 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args) pc = preempt_count(); preempt_disable_notrace(); - cpu = raw_smp_processor_id(); - data = tr->data[cpu]; - disable = atomic_inc_return(&data->disabled); - if (unlikely(disable != 1)) + tbuffer = get_trace_buf(); + if (!tbuffer) { + len = 0; goto out; + } - /* Lockdep uses trace_printk for lock tracing */ - local_irq_save(flags); - arch_spin_lock(&trace_buf_lock); - len = vbin_printf(trace_buf, TRACE_BUF_SIZE, fmt, args); + len = vbin_printf((u32 *)tbuffer, TRACE_BUF_SIZE/sizeof(int), fmt, args); - if (len > TRACE_BUF_SIZE || len < 0) - goto out_unlock; + if (len > TRACE_BUF_SIZE/sizeof(int) || len < 0) + goto out; + local_save_flags(flags); size = sizeof(*entry) + sizeof(u32) * len; buffer = tr->buffer; event = trace_buffer_lock_reserve(buffer, TRACE_BPRINT, size, flags, pc); if (!event) - goto out_unlock; + goto out; entry = ring_buffer_event_data(event); entry->ip = ip; entry->fmt = fmt; - memcpy(entry->buf, trace_buf, sizeof(u32) * len); + memcpy(entry->buf, tbuffer, sizeof(u32) * len); if (!filter_check_discard(call, entry, buffer, event)) { ring_buffer_unlock_commit(buffer, event); ftrace_trace_stack(buffer, flags, 6, pc); } -out_unlock: - arch_spin_unlock(&trace_buf_lock); - local_irq_restore(flags); - out: - atomic_dec_return(&data->disabled); preempt_enable_notrace(); unpause_graph_tracing(); @@ -1588,58 +1675,53 @@ int trace_array_printk(struct trace_array *tr, int trace_array_vprintk(struct trace_array *tr, unsigned long ip, const char *fmt, va_list args) { - static arch_spinlock_t trace_buf_lock = __ARCH_SPIN_LOCK_UNLOCKED; - static char trace_buf[TRACE_BUF_SIZE]; - struct ftrace_event_call *call = &event_print; struct ring_buffer_event *event; struct ring_buffer *buffer; - struct trace_array_cpu *data; - int cpu, len = 0, size, pc; + int len = 0, size, pc; struct print_entry *entry; - unsigned long irq_flags; - int disable; + unsigned long flags; + char *tbuffer; if (tracing_disabled || tracing_selftest_running) return 0; + /* Don't pollute graph traces with trace_vprintk internals */ + pause_graph_tracing(); + pc = preempt_count(); preempt_disable_notrace(); - cpu = raw_smp_processor_id(); - data = tr->data[cpu]; - disable = atomic_inc_return(&data->disabled); - if (unlikely(disable != 1)) + + tbuffer = get_trace_buf(); + if (!tbuffer) { + len = 0; goto out; + } - pause_graph_tracing(); - raw_local_irq_save(irq_flags); - arch_spin_lock(&trace_buf_lock); - len = vsnprintf(trace_buf, TRACE_BUF_SIZE, fmt, args); + len = vsnprintf(tbuffer, TRACE_BUF_SIZE, fmt, args); + if (len > TRACE_BUF_SIZE) + goto out; + local_save_flags(flags); size = sizeof(*entry) + len + 1; buffer = tr->buffer; event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, size, - irq_flags, pc); + flags, pc); if (!event) - goto out_unlock; + goto out; entry = ring_buffer_event_data(event); entry->ip = ip; - memcpy(&entry->buf, trace_buf, len); + memcpy(&entry->buf, tbuffer, len); entry->buf[len] = '\0'; if (!filter_check_discard(call, entry, buffer, event)) { ring_buffer_unlock_commit(buffer, event); - ftrace_trace_stack(buffer, irq_flags, 6, pc); + ftrace_trace_stack(buffer, flags, 6, pc); } - - out_unlock: - arch_spin_unlock(&trace_buf_lock); - raw_local_irq_restore(irq_flags); - unpause_graph_tracing(); out: - atomic_dec_return(&data->disabled); preempt_enable_notrace(); + unpause_graph_tracing(); return len; } @@ -4955,6 +5037,10 @@ __init static int tracer_alloc_buffers(void) if (!alloc_cpumask_var(&tracing_cpumask, GFP_KERNEL)) goto out_free_buffer_mask; + /* Only allocate trace_printk buffers if a trace_printk exists */ + if (__stop___trace_bprintk_fmt != __start___trace_bprintk_fmt) + trace_printk_init_buffers(); + /* To save memory, keep the ring buffer size to its minimum */ if (ring_buffer_expanded) ring_buf_size = trace_buf_size; diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 95059f091a24..f9d85504f04b 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -826,6 +826,8 @@ extern struct list_head ftrace_events; extern const char *__start___trace_bprintk_fmt[]; extern const char *__stop___trace_bprintk_fmt[]; +void trace_printk_init_buffers(void); + #undef FTRACE_ENTRY #define FTRACE_ENTRY(call, struct_name, id, tstruct, print, filter) \ extern struct ftrace_event_call \ diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index 6fd4ffd042f9..a9077c1b4ad3 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c @@ -51,6 +51,10 @@ void hold_module_trace_bprintk_format(const char **start, const char **end) const char **iter; char *fmt; + /* allocate the trace_printk per cpu buffers */ + if (start != end) + trace_printk_init_buffers(); + mutex_lock(&btrace_mutex); for (iter = start; iter < end; iter++) { struct trace_bprintk_fmt *tb_fmt = lookup_format(*iter); -- cgit v1.2.3 From 438ced1720b584000a9e8a4349d1f6bb7ee3ad6d Mon Sep 17 00:00:00 2001 From: Vaibhav Nagarnaik Date: Thu, 2 Feb 2012 12:00:41 -0800 Subject: ring-buffer: Add per_cpu ring buffer control files Add a debugfs entry under per_cpu/ folder for each cpu called buffer_size_kb to control the ring buffer size for each CPU independently. If the global file buffer_size_kb is used to set size, the individual ring buffers will be adjusted to the given size. The buffer_size_kb will report the common size to maintain backward compatibility. If the buffer_size_kb file under the per_cpu/ directory is used to change buffer size for a specific CPU, only the size of the respective ring buffer is updated. When tracing/buffer_size_kb is read, it reports 'X' to indicate that sizes of per_cpu ring buffers are not equivalent. Link: http://lkml.kernel.org/r/1328212844-11889-1-git-send-email-vnagarnaik@google.com Cc: Frederic Weisbecker Cc: Michael Rubin Cc: David Sharp Cc: Justin Teravest Signed-off-by: Vaibhav Nagarnaik Signed-off-by: Steven Rostedt --- include/linux/ring_buffer.h | 6 +- kernel/trace/ring_buffer.c | 248 +++++++++++++++++++++++++------------------- kernel/trace/trace.c | 190 ++++++++++++++++++++++++++------- kernel/trace/trace.h | 2 +- 4 files changed, 297 insertions(+), 149 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index 7be2e88f23fd..6c8835f74f79 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -96,9 +96,11 @@ __ring_buffer_alloc(unsigned long size, unsigned flags, struct lock_class_key *k __ring_buffer_alloc((size), (flags), &__key); \ }) +#define RING_BUFFER_ALL_CPUS -1 + void ring_buffer_free(struct ring_buffer *buffer); -int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size); +int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size, int cpu); void ring_buffer_change_overwrite(struct ring_buffer *buffer, int val); @@ -129,7 +131,7 @@ ring_buffer_read(struct ring_buffer_iter *iter, u64 *ts); void ring_buffer_iter_reset(struct ring_buffer_iter *iter); int ring_buffer_iter_empty(struct ring_buffer_iter *iter); -unsigned long ring_buffer_size(struct ring_buffer *buffer); +unsigned long ring_buffer_size(struct ring_buffer *buffer, int cpu); void ring_buffer_reset_cpu(struct ring_buffer *buffer, int cpu); void ring_buffer_reset(struct ring_buffer *buffer); diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index cf8d11e91efd..2d5eb3320827 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -449,6 +449,7 @@ struct ring_buffer_per_cpu { raw_spinlock_t reader_lock; /* serialize readers */ arch_spinlock_t lock; struct lock_class_key lock_key; + unsigned int nr_pages; struct list_head *pages; struct buffer_page *head_page; /* read from head */ struct buffer_page *tail_page; /* write to tail */ @@ -466,10 +467,12 @@ struct ring_buffer_per_cpu { unsigned long read_bytes; u64 write_stamp; u64 read_stamp; + /* ring buffer pages to update, > 0 to add, < 0 to remove */ + int nr_pages_to_update; + struct list_head new_pages; /* new pages to add */ }; struct ring_buffer { - unsigned pages; unsigned flags; int cpus; atomic_t record_disabled; @@ -963,14 +966,10 @@ static int rb_check_pages(struct ring_buffer_per_cpu *cpu_buffer) return 0; } -static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, - unsigned nr_pages) +static int __rb_allocate_pages(int nr_pages, struct list_head *pages, int cpu) { + int i; struct buffer_page *bpage, *tmp; - LIST_HEAD(pages); - unsigned i; - - WARN_ON(!nr_pages); for (i = 0; i < nr_pages; i++) { struct page *page; @@ -981,15 +980,13 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, */ bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()), GFP_KERNEL | __GFP_NORETRY, - cpu_to_node(cpu_buffer->cpu)); + cpu_to_node(cpu)); if (!bpage) goto free_pages; - rb_check_bpage(cpu_buffer, bpage); + list_add(&bpage->list, pages); - list_add(&bpage->list, &pages); - - page = alloc_pages_node(cpu_to_node(cpu_buffer->cpu), + page = alloc_pages_node(cpu_to_node(cpu), GFP_KERNEL | __GFP_NORETRY, 0); if (!page) goto free_pages; @@ -997,6 +994,27 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, rb_init_page(bpage->page); } + return 0; + +free_pages: + list_for_each_entry_safe(bpage, tmp, pages, list) { + list_del_init(&bpage->list); + free_buffer_page(bpage); + } + + return -ENOMEM; +} + +static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, + unsigned nr_pages) +{ + LIST_HEAD(pages); + + WARN_ON(!nr_pages); + + if (__rb_allocate_pages(nr_pages, &pages, cpu_buffer->cpu)) + return -ENOMEM; + /* * The ring buffer page list is a circular list that does not * start and end with a list head. All page list items point to @@ -1005,20 +1023,15 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, cpu_buffer->pages = pages.next; list_del(&pages); + cpu_buffer->nr_pages = nr_pages; + rb_check_pages(cpu_buffer); return 0; - - free_pages: - list_for_each_entry_safe(bpage, tmp, &pages, list) { - list_del_init(&bpage->list); - free_buffer_page(bpage); - } - return -ENOMEM; } static struct ring_buffer_per_cpu * -rb_allocate_cpu_buffer(struct ring_buffer *buffer, int cpu) +rb_allocate_cpu_buffer(struct ring_buffer *buffer, int nr_pages, int cpu) { struct ring_buffer_per_cpu *cpu_buffer; struct buffer_page *bpage; @@ -1052,7 +1065,7 @@ rb_allocate_cpu_buffer(struct ring_buffer *buffer, int cpu) INIT_LIST_HEAD(&cpu_buffer->reader_page->list); - ret = rb_allocate_pages(cpu_buffer, buffer->pages); + ret = rb_allocate_pages(cpu_buffer, nr_pages); if (ret < 0) goto fail_free_reader; @@ -1113,7 +1126,7 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, { struct ring_buffer *buffer; int bsize; - int cpu; + int cpu, nr_pages; /* keep it in its own cache line */ buffer = kzalloc(ALIGN(sizeof(*buffer), cache_line_size()), @@ -1124,14 +1137,14 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, if (!alloc_cpumask_var(&buffer->cpumask, GFP_KERNEL)) goto fail_free_buffer; - buffer->pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE); + nr_pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE); buffer->flags = flags; buffer->clock = trace_clock_local; buffer->reader_lock_key = key; /* need at least two pages */ - if (buffer->pages < 2) - buffer->pages = 2; + if (nr_pages < 2) + nr_pages = 2; /* * In case of non-hotplug cpu, if the ring-buffer is allocated @@ -1154,7 +1167,7 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, for_each_buffer_cpu(buffer, cpu) { buffer->buffers[cpu] = - rb_allocate_cpu_buffer(buffer, cpu); + rb_allocate_cpu_buffer(buffer, nr_pages, cpu); if (!buffer->buffers[cpu]) goto fail_free_buffers; } @@ -1276,6 +1289,18 @@ out: raw_spin_unlock_irq(&cpu_buffer->reader_lock); } +static void update_pages_handler(struct ring_buffer_per_cpu *cpu_buffer) +{ + if (cpu_buffer->nr_pages_to_update > 0) + rb_insert_pages(cpu_buffer, &cpu_buffer->new_pages, + cpu_buffer->nr_pages_to_update); + else + rb_remove_pages(cpu_buffer, -cpu_buffer->nr_pages_to_update); + cpu_buffer->nr_pages += cpu_buffer->nr_pages_to_update; + /* reset this value */ + cpu_buffer->nr_pages_to_update = 0; +} + /** * ring_buffer_resize - resize the ring buffer * @buffer: the buffer to resize. @@ -1285,14 +1310,12 @@ out: * * Returns -1 on failure. */ -int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size) +int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size, + int cpu_id) { struct ring_buffer_per_cpu *cpu_buffer; - unsigned nr_pages, rm_pages, new_pages; - struct buffer_page *bpage, *tmp; - unsigned long buffer_size; - LIST_HEAD(pages); - int i, cpu; + unsigned nr_pages; + int cpu; /* * Always succeed at resizing a non-existent buffer: @@ -1302,15 +1325,11 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size) size = DIV_ROUND_UP(size, BUF_PAGE_SIZE); size *= BUF_PAGE_SIZE; - buffer_size = buffer->pages * BUF_PAGE_SIZE; /* we need a minimum of two pages */ if (size < BUF_PAGE_SIZE * 2) size = BUF_PAGE_SIZE * 2; - if (size == buffer_size) - return size; - atomic_inc(&buffer->record_disabled); /* Make sure all writers are done with this buffer. */ @@ -1321,68 +1340,56 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size) nr_pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE); - if (size < buffer_size) { - - /* easy case, just free pages */ - if (RB_WARN_ON(buffer, nr_pages >= buffer->pages)) - goto out_fail; - - rm_pages = buffer->pages - nr_pages; - + if (cpu_id == RING_BUFFER_ALL_CPUS) { + /* calculate the pages to update */ for_each_buffer_cpu(buffer, cpu) { cpu_buffer = buffer->buffers[cpu]; - rb_remove_pages(cpu_buffer, rm_pages); - } - goto out; - } - /* - * This is a bit more difficult. We only want to add pages - * when we can allocate enough for all CPUs. We do this - * by allocating all the pages and storing them on a local - * link list. If we succeed in our allocation, then we - * add these pages to the cpu_buffers. Otherwise we just free - * them all and return -ENOMEM; - */ - if (RB_WARN_ON(buffer, nr_pages <= buffer->pages)) - goto out_fail; + cpu_buffer->nr_pages_to_update = nr_pages - + cpu_buffer->nr_pages; - new_pages = nr_pages - buffer->pages; + /* + * nothing more to do for removing pages or no update + */ + if (cpu_buffer->nr_pages_to_update <= 0) + continue; - for_each_buffer_cpu(buffer, cpu) { - for (i = 0; i < new_pages; i++) { - struct page *page; /* - * __GFP_NORETRY flag makes sure that the allocation - * fails gracefully without invoking oom-killer and - * the system is not destabilized. + * to add pages, make sure all new pages can be + * allocated without receiving ENOMEM */ - bpage = kzalloc_node(ALIGN(sizeof(*bpage), - cache_line_size()), - GFP_KERNEL | __GFP_NORETRY, - cpu_to_node(cpu)); - if (!bpage) - goto free_pages; - list_add(&bpage->list, &pages); - page = alloc_pages_node(cpu_to_node(cpu), - GFP_KERNEL | __GFP_NORETRY, 0); - if (!page) - goto free_pages; - bpage->page = page_address(page); - rb_init_page(bpage->page); + INIT_LIST_HEAD(&cpu_buffer->new_pages); + if (__rb_allocate_pages(cpu_buffer->nr_pages_to_update, + &cpu_buffer->new_pages, cpu)) + /* not enough memory for new pages */ + goto no_mem; } - } - for_each_buffer_cpu(buffer, cpu) { - cpu_buffer = buffer->buffers[cpu]; - rb_insert_pages(cpu_buffer, &pages, new_pages); - } + /* wait for all the updates to complete */ + for_each_buffer_cpu(buffer, cpu) { + cpu_buffer = buffer->buffers[cpu]; + if (cpu_buffer->nr_pages_to_update) { + update_pages_handler(cpu_buffer); + } + } + } else { + cpu_buffer = buffer->buffers[cpu_id]; + if (nr_pages == cpu_buffer->nr_pages) + goto out; - if (RB_WARN_ON(buffer, !list_empty(&pages))) - goto out_fail; + cpu_buffer->nr_pages_to_update = nr_pages - + cpu_buffer->nr_pages; + + INIT_LIST_HEAD(&cpu_buffer->new_pages); + if (cpu_buffer->nr_pages_to_update > 0 && + __rb_allocate_pages(cpu_buffer->nr_pages_to_update, + &cpu_buffer->new_pages, cpu_id)) + goto no_mem; + + update_pages_handler(cpu_buffer); + } out: - buffer->pages = nr_pages; put_online_cpus(); mutex_unlock(&buffer->mutex); @@ -1390,25 +1397,24 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size) return size; - free_pages: - list_for_each_entry_safe(bpage, tmp, &pages, list) { - list_del_init(&bpage->list); - free_buffer_page(bpage); + no_mem: + for_each_buffer_cpu(buffer, cpu) { + struct buffer_page *bpage, *tmp; + cpu_buffer = buffer->buffers[cpu]; + /* reset this number regardless */ + cpu_buffer->nr_pages_to_update = 0; + if (list_empty(&cpu_buffer->new_pages)) + continue; + list_for_each_entry_safe(bpage, tmp, &cpu_buffer->new_pages, + list) { + list_del_init(&bpage->list); + free_buffer_page(bpage); + } } put_online_cpus(); mutex_unlock(&buffer->mutex); atomic_dec(&buffer->record_disabled); return -ENOMEM; - - /* - * Something went totally wrong, and we are too paranoid - * to even clean up the mess. - */ - out_fail: - put_online_cpus(); - mutex_unlock(&buffer->mutex); - atomic_dec(&buffer->record_disabled); - return -1; } EXPORT_SYMBOL_GPL(ring_buffer_resize); @@ -1510,7 +1516,7 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer) * assign the commit to the tail. */ again: - max_count = cpu_buffer->buffer->pages * 100; + max_count = cpu_buffer->nr_pages * 100; while (cpu_buffer->commit_page != cpu_buffer->tail_page) { if (RB_WARN_ON(cpu_buffer, !(--max_count))) @@ -3588,9 +3594,18 @@ EXPORT_SYMBOL_GPL(ring_buffer_read); * ring_buffer_size - return the size of the ring buffer (in bytes) * @buffer: The ring buffer. */ -unsigned long ring_buffer_size(struct ring_buffer *buffer) +unsigned long ring_buffer_size(struct ring_buffer *buffer, int cpu) { - return BUF_PAGE_SIZE * buffer->pages; + /* + * Earlier, this method returned + * BUF_PAGE_SIZE * buffer->nr_pages + * Since the nr_pages field is now removed, we have converted this to + * return the per cpu buffer value. + */ + if (!cpumask_test_cpu(cpu, buffer->cpumask)) + return 0; + + return BUF_PAGE_SIZE * buffer->buffers[cpu]->nr_pages; } EXPORT_SYMBOL_GPL(ring_buffer_size); @@ -3765,8 +3780,11 @@ int ring_buffer_swap_cpu(struct ring_buffer *buffer_a, !cpumask_test_cpu(cpu, buffer_b->cpumask)) goto out; + cpu_buffer_a = buffer_a->buffers[cpu]; + cpu_buffer_b = buffer_b->buffers[cpu]; + /* At least make sure the two buffers are somewhat the same */ - if (buffer_a->pages != buffer_b->pages) + if (cpu_buffer_a->nr_pages != cpu_buffer_b->nr_pages) goto out; ret = -EAGAIN; @@ -3780,9 +3798,6 @@ int ring_buffer_swap_cpu(struct ring_buffer *buffer_a, if (atomic_read(&buffer_b->record_disabled)) goto out; - cpu_buffer_a = buffer_a->buffers[cpu]; - cpu_buffer_b = buffer_b->buffers[cpu]; - if (atomic_read(&cpu_buffer_a->record_disabled)) goto out; @@ -4071,6 +4086,8 @@ static int rb_cpu_notify(struct notifier_block *self, struct ring_buffer *buffer = container_of(self, struct ring_buffer, cpu_notify); long cpu = (long)hcpu; + int cpu_i, nr_pages_same; + unsigned int nr_pages; switch (action) { case CPU_UP_PREPARE: @@ -4078,8 +4095,23 @@ static int rb_cpu_notify(struct notifier_block *self, if (cpumask_test_cpu(cpu, buffer->cpumask)) return NOTIFY_OK; + nr_pages = 0; + nr_pages_same = 1; + /* check if all cpu sizes are same */ + for_each_buffer_cpu(buffer, cpu_i) { + /* fill in the size from first enabled cpu */ + if (nr_pages == 0) + nr_pages = buffer->buffers[cpu_i]->nr_pages; + if (nr_pages != buffer->buffers[cpu_i]->nr_pages) { + nr_pages_same = 0; + break; + } + } + /* allocate minimum pages, user can later expand it */ + if (!nr_pages_same) + nr_pages = 2; buffer->buffers[cpu] = - rb_allocate_cpu_buffer(buffer, cpu); + rb_allocate_cpu_buffer(buffer, nr_pages, cpu); if (!buffer->buffers[cpu]) { WARN(1, "failed to allocate ring buffer on CPU %ld\n", cpu); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index bbcde546f9f7..f11a285ee5bb 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -838,7 +838,8 @@ __acquires(kernel_lock) /* If we expanded the buffers, make sure the max is expanded too */ if (ring_buffer_expanded && type->use_max_tr) - ring_buffer_resize(max_tr.buffer, trace_buf_size); + ring_buffer_resize(max_tr.buffer, trace_buf_size, + RING_BUFFER_ALL_CPUS); /* the test is responsible for initializing and enabling */ pr_info("Testing tracer %s: ", type->name); @@ -854,7 +855,8 @@ __acquires(kernel_lock) /* Shrink the max buffer again */ if (ring_buffer_expanded && type->use_max_tr) - ring_buffer_resize(max_tr.buffer, 1); + ring_buffer_resize(max_tr.buffer, 1, + RING_BUFFER_ALL_CPUS); printk(KERN_CONT "PASSED\n"); } @@ -3053,7 +3055,14 @@ int tracer_init(struct tracer *t, struct trace_array *tr) return t->init(tr); } -static int __tracing_resize_ring_buffer(unsigned long size) +static void set_buffer_entries(struct trace_array *tr, unsigned long val) +{ + int cpu; + for_each_tracing_cpu(cpu) + tr->data[cpu]->entries = val; +} + +static int __tracing_resize_ring_buffer(unsigned long size, int cpu) { int ret; @@ -3064,19 +3073,32 @@ static int __tracing_resize_ring_buffer(unsigned long size) */ ring_buffer_expanded = 1; - ret = ring_buffer_resize(global_trace.buffer, size); + ret = ring_buffer_resize(global_trace.buffer, size, cpu); if (ret < 0) return ret; if (!current_trace->use_max_tr) goto out; - ret = ring_buffer_resize(max_tr.buffer, size); + ret = ring_buffer_resize(max_tr.buffer, size, cpu); if (ret < 0) { - int r; + int r = 0; + + if (cpu == RING_BUFFER_ALL_CPUS) { + int i; + for_each_tracing_cpu(i) { + r = ring_buffer_resize(global_trace.buffer, + global_trace.data[i]->entries, + i); + if (r < 0) + break; + } + } else { + r = ring_buffer_resize(global_trace.buffer, + global_trace.data[cpu]->entries, + cpu); + } - r = ring_buffer_resize(global_trace.buffer, - global_trace.entries); if (r < 0) { /* * AARGH! We are left with different @@ -3098,14 +3120,21 @@ static int __tracing_resize_ring_buffer(unsigned long size) return ret; } - max_tr.entries = size; + if (cpu == RING_BUFFER_ALL_CPUS) + set_buffer_entries(&max_tr, size); + else + max_tr.data[cpu]->entries = size; + out: - global_trace.entries = size; + if (cpu == RING_BUFFER_ALL_CPUS) + set_buffer_entries(&global_trace, size); + else + global_trace.data[cpu]->entries = size; return ret; } -static ssize_t tracing_resize_ring_buffer(unsigned long size) +static ssize_t tracing_resize_ring_buffer(unsigned long size, int cpu_id) { int cpu, ret = size; @@ -3121,12 +3150,19 @@ static ssize_t tracing_resize_ring_buffer(unsigned long size) atomic_inc(&max_tr.data[cpu]->disabled); } - if (size != global_trace.entries) - ret = __tracing_resize_ring_buffer(size); + if (cpu_id != RING_BUFFER_ALL_CPUS) { + /* make sure, this cpu is enabled in the mask */ + if (!cpumask_test_cpu(cpu_id, tracing_buffer_mask)) { + ret = -EINVAL; + goto out; + } + } + ret = __tracing_resize_ring_buffer(size, cpu_id); if (ret < 0) ret = -ENOMEM; +out: for_each_tracing_cpu(cpu) { if (global_trace.data[cpu]) atomic_dec(&global_trace.data[cpu]->disabled); @@ -3157,7 +3193,8 @@ int tracing_update_buffers(void) mutex_lock(&trace_types_lock); if (!ring_buffer_expanded) - ret = __tracing_resize_ring_buffer(trace_buf_size); + ret = __tracing_resize_ring_buffer(trace_buf_size, + RING_BUFFER_ALL_CPUS); mutex_unlock(&trace_types_lock); return ret; @@ -3181,7 +3218,8 @@ static int tracing_set_tracer(const char *buf) mutex_lock(&trace_types_lock); if (!ring_buffer_expanded) { - ret = __tracing_resize_ring_buffer(trace_buf_size); + ret = __tracing_resize_ring_buffer(trace_buf_size, + RING_BUFFER_ALL_CPUS); if (ret < 0) goto out; ret = 0; @@ -3207,8 +3245,8 @@ 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); - max_tr.entries = 1; + ring_buffer_resize(max_tr.buffer, 1, RING_BUFFER_ALL_CPUS); + set_buffer_entries(&max_tr, 1); } destroy_trace_option_files(topts); @@ -3216,10 +3254,17 @@ static int tracing_set_tracer(const char *buf) topts = create_trace_option_files(current_trace); if (current_trace->use_max_tr) { - ret = ring_buffer_resize(max_tr.buffer, global_trace.entries); - if (ret < 0) - goto out; - max_tr.entries = global_trace.entries; + int cpu; + /* we need to make per cpu buffer sizes equivalent */ + for_each_tracing_cpu(cpu) { + ret = ring_buffer_resize(max_tr.buffer, + global_trace.data[cpu]->entries, + cpu); + if (ret < 0) + goto out; + max_tr.data[cpu]->entries = + global_trace.data[cpu]->entries; + } } if (t->init) { @@ -3721,30 +3766,82 @@ out_err: goto out; } +struct ftrace_entries_info { + struct trace_array *tr; + int cpu; +}; + +static int tracing_entries_open(struct inode *inode, struct file *filp) +{ + struct ftrace_entries_info *info; + + if (tracing_disabled) + return -ENODEV; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->tr = &global_trace; + info->cpu = (unsigned long)inode->i_private; + + filp->private_data = info; + + return 0; +} + static ssize_t tracing_entries_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - struct trace_array *tr = filp->private_data; - char buf[96]; - int r; + struct ftrace_entries_info *info = filp->private_data; + struct trace_array *tr = info->tr; + char buf[64]; + int r = 0; + ssize_t ret; mutex_lock(&trace_types_lock); - if (!ring_buffer_expanded) - r = sprintf(buf, "%lu (expanded: %lu)\n", - tr->entries >> 10, - trace_buf_size >> 10); - else - r = sprintf(buf, "%lu\n", tr->entries >> 10); + + if (info->cpu == RING_BUFFER_ALL_CPUS) { + int cpu, buf_size_same; + unsigned long size; + + size = 0; + buf_size_same = 1; + /* check if all cpu sizes are same */ + for_each_tracing_cpu(cpu) { + /* fill in the size from first enabled cpu */ + if (size == 0) + size = tr->data[cpu]->entries; + if (size != tr->data[cpu]->entries) { + buf_size_same = 0; + break; + } + } + + if (buf_size_same) { + if (!ring_buffer_expanded) + r = sprintf(buf, "%lu (expanded: %lu)\n", + size >> 10, + trace_buf_size >> 10); + else + r = sprintf(buf, "%lu\n", size >> 10); + } else + r = sprintf(buf, "X\n"); + } else + r = sprintf(buf, "%lu\n", tr->data[info->cpu]->entries >> 10); + mutex_unlock(&trace_types_lock); - return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, r); + return ret; } static ssize_t tracing_entries_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { + struct ftrace_entries_info *info = filp->private_data; unsigned long val; int ret; @@ -3759,7 +3856,7 @@ tracing_entries_write(struct file *filp, const char __user *ubuf, /* value is in KB */ val <<= 10; - ret = tracing_resize_ring_buffer(val); + ret = tracing_resize_ring_buffer(val, info->cpu); if (ret < 0) return ret; @@ -3768,6 +3865,16 @@ tracing_entries_write(struct file *filp, const char __user *ubuf, return cnt; } +static int +tracing_entries_release(struct inode *inode, struct file *filp) +{ + struct ftrace_entries_info *info = filp->private_data; + + kfree(info); + + return 0; +} + static ssize_t tracing_total_entries_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) @@ -3779,7 +3886,7 @@ tracing_total_entries_read(struct file *filp, char __user *ubuf, mutex_lock(&trace_types_lock); for_each_tracing_cpu(cpu) { - size += tr->entries >> 10; + size += tr->data[cpu]->entries >> 10; if (!ring_buffer_expanded) expanded_size += trace_buf_size >> 10; } @@ -3813,7 +3920,7 @@ tracing_free_buffer_release(struct inode *inode, struct file *filp) if (trace_flags & TRACE_ITER_STOP_ON_FREE) tracing_off(); /* resize the ring buffer to 0 */ - tracing_resize_ring_buffer(0); + tracing_resize_ring_buffer(0, RING_BUFFER_ALL_CPUS); return 0; } @@ -4012,9 +4119,10 @@ static const struct file_operations tracing_pipe_fops = { }; static const struct file_operations tracing_entries_fops = { - .open = tracing_open_generic, + .open = tracing_entries_open, .read = tracing_entries_read, .write = tracing_entries_write, + .release = tracing_entries_release, .llseek = generic_file_llseek, }; @@ -4466,6 +4574,9 @@ static void tracing_init_debugfs_percpu(long cpu) trace_create_file("stats", 0444, d_cpu, (void *) cpu, &tracing_stats_fops); + + trace_create_file("buffer_size_kb", 0444, d_cpu, + (void *) cpu, &tracing_entries_fops); } #ifdef CONFIG_FTRACE_SELFTEST @@ -4795,7 +4906,7 @@ static __init int tracer_init_debugfs(void) (void *) TRACE_PIPE_ALL_CPU, &tracing_pipe_fops); trace_create_file("buffer_size_kb", 0644, d_tracer, - &global_trace, &tracing_entries_fops); + (void *) RING_BUFFER_ALL_CPUS, &tracing_entries_fops); trace_create_file("buffer_total_size_kb", 0444, d_tracer, &global_trace, &tracing_total_entries_fops); @@ -5056,7 +5167,6 @@ __init static int tracer_alloc_buffers(void) WARN_ON(1); goto out_free_cpumask; } - global_trace.entries = ring_buffer_size(global_trace.buffer); if (global_trace.buffer_disabled) tracing_off(); @@ -5069,7 +5179,6 @@ __init static int tracer_alloc_buffers(void) ring_buffer_free(global_trace.buffer); goto out_free_cpumask; } - max_tr.entries = 1; #endif /* Allocate the first page for all buffers */ @@ -5078,6 +5187,11 @@ __init static int tracer_alloc_buffers(void) max_tr.data[i] = &per_cpu(max_tr_data, i); } + set_buffer_entries(&global_trace, ring_buf_size); +#ifdef CONFIG_TRACER_MAX_TRACE + set_buffer_entries(&max_tr, 1); +#endif + trace_init_cmdlines(); register_tracer(&nop_trace); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index f9d85504f04b..1c8b7c6f7b3b 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -131,6 +131,7 @@ struct trace_array_cpu { atomic_t disabled; void *buffer_page; /* ring buffer spare */ + unsigned long entries; unsigned long saved_latency; unsigned long critical_start; unsigned long critical_end; @@ -152,7 +153,6 @@ struct trace_array_cpu { */ struct trace_array { struct ring_buffer *buffer; - unsigned long entries; int cpu; int buffer_disabled; cycle_t time_start; -- cgit v1.2.3 From 38ba0a65faf451dd46c7860b4fade84c0b8e444f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 Apr 2012 17:48:27 +0000 Subject: net: skb_can_coalesce returns a boolean Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 6 +++--- net/ipv4/tcp.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index f25795ca6753..4a656b51825e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1963,8 +1963,8 @@ static inline int skb_add_data(struct sk_buff *skb, return -EFAULT; } -static inline int skb_can_coalesce(struct sk_buff *skb, int i, - const struct page *page, int off) +static inline bool skb_can_coalesce(struct sk_buff *skb, int i, + const struct page *page, int off) { if (i) { const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i - 1]; @@ -1972,7 +1972,7 @@ static inline int skb_can_coalesce(struct sk_buff *skb, int i, return page == skb_frag_page(frag) && off == frag->page_offset + skb_frag_size(frag); } - return 0; + return false; } static inline int __skb_linearize(struct sk_buff *skb) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index bcc4eab5f251..de6a238f0e1d 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -848,9 +848,10 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse while (psize > 0) { struct sk_buff *skb = tcp_write_queue_tail(sk); struct page *page = pages[poffset / PAGE_SIZE]; - int copy, i, can_coalesce; + int copy, i; int offset = poffset % PAGE_SIZE; int size = min_t(size_t, psize, PAGE_SIZE - offset); + bool can_coalesce; if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0) { new_segment: -- cgit v1.2.3 From 1a39b310e920bb7098067d96411b31e459ae8f32 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 16 Apr 2012 16:26:02 -0400 Subject: vgaarb: Add support for setting the default video device (v2) The default VGA device is a somewhat fluid concept on platforms with multiple GPUs. Add support for setting it so switching code can update things appropriately, and make sure that the sysfs code returns the right device if it's changed. v2: Updated to fix builds when __ARCH_HAS_VGA_DEFAULT_DEVICE is false. Signed-off-by: Matthew Garrett Acked-by: H. Peter Anvin Acked-by: benh@kernel.crashing.org Cc: airlied@redhat.com Signed-off-by: Dave Airlie --- drivers/gpu/vga/vgaarb.c | 7 +++++++ drivers/pci/pci-sysfs.c | 5 +++++ include/linux/vgaarb.h | 2 ++ 3 files changed, 14 insertions(+) (limited to 'include/linux') diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index 111d956d8e7d..e223b96fa6a0 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -136,6 +136,11 @@ struct pci_dev *vga_default_device(void) { return vga_default; } + +void vga_set_default_device(struct pci_dev *pdev) +{ + vga_default = pdev; +} #endif static inline void vga_irq_set_state(struct vga_device *vgadev, bool state) @@ -605,10 +610,12 @@ static bool vga_arbiter_del_pci_device(struct pci_dev *pdev) goto bail; } +#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE if (vga_default == pdev) { pci_dev_put(vga_default); vga_default = NULL; } +#endif if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM)) vga_decode_count--; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index a55e248618cd..86c63fe45d11 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "pci.h" static int sysfs_initialized; /* = 0 */ @@ -417,6 +418,10 @@ static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pci_dev *pdev = to_pci_dev(dev); + struct pci_dev *vga_dev = vga_default_device(); + + if (vga_dev) + return sprintf(buf, "%u\n", (pdev == vga_dev)); return sprintf(buf, "%u\n", !!(pdev->resource[PCI_ROM_RESOURCE].flags & diff --git a/include/linux/vgaarb.h b/include/linux/vgaarb.h index 9c3120dca294..759a25ba0539 100644 --- a/include/linux/vgaarb.h +++ b/include/linux/vgaarb.h @@ -31,6 +31,7 @@ #ifndef LINUX_VGA_H #define LINUX_VGA_H +#include